Troubleshooting Handoff.
My iOS app was advertising its Handoff activity to its Mac counterpart just fine. The reverse just would not happen. 11 back and forth TSI emails later, we figured it out. Now I feel like sharing!
Step #0: Debug Build
Just do a normal Debug build. There’s no need to use Release, pick distribution identities, exporting, etc. to enable Handoff.
Step #1: Activity Types
This is trivial, and you probably have this down already: both versions must use the same activityType
, and they both must have a NSUserActivityTypes
array key in Info.plist
, containing that same activityType
string.
Step #2: Sandbox (OS X)
Your Mac app must be sandboxed. Turn on App Sandbox
in the Capabilities
tab of your OS X target.
Step #3: No Developer ID (OS X)
Here’s a warning, straight from the mothership:
It is a known bug that Developer-Id signed apps do not work with Handoff.
You heard the man: do not sign with Developer ID.
Step #4: Code Signing Identity
Instead, for your Debug builds, sign with an App Store development certificate, a.k.a. “identity”.
On OS X, those certificates’ names start with Mac Developer: ...
.
On iOS you’re probably OK, but just in case, use one that starts with iOS Development: ...
.
Step #5: Provisioning Profile
Both apps [must be] signed by the same team ID
This is where I had it wrong.
Apparently, Team IDs for development certificates are, er… random? But that’s okay. What seems to count (for Handoff, at least) is the Team ID from the provisioning profile used to sign the app. Or more precisely, the corresponding entitlement that is generated.
You can use this command to verify that the entitlement is present:
codesign -dvv --entitlements - yourapp.app
You’re looking for the com.apple.developer.team-identifier
entitlement:
<key>com.apple.developer.team-identifier</key>
<string>YOUR-TEAM-ID</string>
Make sure it’s there, and that the Team ID it shows is correct.
If it’s missing or incorrect, I suggest you manually select a Provisioning Profile
in your build settings. In my case, automatic provisioning profile selection was failing - possibly because of the random Team IDs (?).
Step #6: If Everything Fails (OS X)
Regenerate your development certificate and provisioning profile, and then select them in your build settings. Apparently, some old ones are busted in some mysterious way.
Bonus #1 (OS X)
If you’re having trouble closing windows in your app in Release builds, or seeing this in Console:
Cannot update for observer <NSUIActivityResponderMonitor 0x60800000a230> for the key path “mainWindow.firstResponder” from <NSApplication 0x6000001181e0>, most likely because the value for the key “mainWindow” has changed without an appropriate KVO notification being sent. Check the KVO-compliance of the NSApplication class.
You can work around this by avoiding the userActivity
property of NSResponder
. Instead, create and keep around your own NSUserActivity
:
let handoffActivity = NSUserActivity(activityType: "com.example.app.activity")
And manually call becomeCurrent()
on it when appropriate. You can still use the delegate
/needsSave
mechanism.
handoffActivity.needsSave = true
handoffActivity.delegate = self
handoffActivity.becomeCurrent()
Not as elegant as the responder chain way, but it works.
I don’t know what’s causing this, but I’ve filed Radar #19138455. I’ll update this article when the issue is resolved.
Bonus #2 (OS X)
You can get more Console logging from sharingd
(the deamon responsible for Handoff, among other things), by restarting it after setting this sneaky hidden preference flag:
defaults write com.apple.Sharing EnableDebugLogging -bool TRUE
Hope this helps! Let me know if you’re still having problems.