WatchConnectivity: Say Hello to WCSession

Check out previous blog posts on WatchOS 2 if you haven’t already:

WCSession is the magic ingredient of WatchConnectivity. So let’s dive in!

WCSession.defaultSession() will return the WCSession singleton for transfering data between your iOS and Watch app. But of course there are several caveats to keep in mind when using WCSession!

The first one is that you have to set a delegate for the session and activate it!

“The default session is used to communicate between two counterpart apps (i.e. iOS app and its native WatchKit extension). The session provides methods for sending, receiving, and tracking state.

On start up an app should set a delegate on the default session and call activate. This will allow the system to populate the state properties and deliver any outstanding background transfers.” – Apple Documentation Notes

So your code will look something like this…

At this point, I would recommend wrapping your WCSession Singleton into your own Singleton, which you can use throughout your app:

So now you can activate your session from application:didFinishLaunchingWithOptions in the AppDelegate and use it everywhere else in your app:

But activating the session is not enough of course. WCSession has multiple checks you need to go through so that your application is not doing the extra work of formatting your data for transfer.


Check if session is supported on this iOS device. Session is always available on WatchOS

If you have a Universal app, for example, WCSession will not be supported on the iPad (since the Apple Watch does not pair with the iPad). So always make sure to do the isSupported() check in your iOS project:

This means the WatchSessionManager Singleton in your iOS app will need to adjust to deal with the possibility of the WCSession not being supported (hello optionals!):

iOS App State For Watch

If you’re sending data from your iOS app to the Watch, you need to do a few extra checks so you’re not wasting CPU power for processing your data for transfer when the Watch is not in a state where it can receive the data.


This is pretty self-explanatory. In order to transfer data from the iOS device to the Watch, the user must own an Apple Watch and have it paired to your iOS device.


A user might have a paired device, but of course they can choose to delete your Watch App from their device. So you have to check that your Watch App is actually installed on their paired Apple Watch in order to do the data transfer.

If the user gets to a point in your app where you believe they will benefit from the Apple Watch version of your app, that would be a good place to do this check and prompt the user with a value proposition to install your watch app if they have a paired device without your app on it.

To make these checks much easier to do as you keep working with the session in your singleton and possibly throughout your application, I like to create a validSession variable in my iOS app:


Finally, if you have a complication for your app, you have to check if the complication is enabled. I won’t go into much detail on Complications in my WatchConnectivity tutorials, but if you’d like to learn more, watch the super useful and comprehensive WWDC 2015 Creating Complications with ClockKit session.


Note that there’s a delegate method to notify you of when any of the above WCSession states change in case you need that information in your iOS app:

So, for example, if your app needs the Watch App to be installed, you can implement this delegate method to monitor that your Watch App was in fact installed and let the user follow through on the additional flow in your iOS app to finalize setup.


In order to use Interactive Messaging to send data between the iOS and Watch actively running apps right away, you need to do an additional check to make sure that the two apps are in reachable state:

Reachability in the Watch app requires the paired iOS device to have been unlocked at least once after reboot. This property can be used to determine if the iOS device needs to be unlocked. If the reachable property is set to NO it may be because the iOS device has rebooted and needs to be unlocked. If this is the case, the Watch can show a prompt to the user suggesting they unlock their paired iOS device.

I like to add an extra valideReachableSession variable to my singleton for using with Interactive Messaging:

If the session is not reachable, you can prompt the user to unlock their iOS device as suggested by Apple. To know that the user has unlocked their device, implement the sessionReachabilityDidChange delegate method:

That’s it! You now should understand all the little gotchas with WCSession, so we can now move on to the fun part – actually using it to send and receive data between the Watch and iOS app!

You can view the full WatchSessionManager Singleton on Github here.

Enjoy the article? Join over 14,500+ Swift developers and enthusiasts who get my weekly updates.

  • Marcus ⌚️

    Hi Natasha, thank you for this clarifying article. However i have a question.
    I understood how to start a session or how to update the context but i wonder how to use the delegate methods for receiving something from the singleton. (I never used a singleton before :/ )
    Could you please point me in the right direction?

    Thank you

  • Cool, thanks for the article!

  • sparetimedev

    Thanks, this is really helpful.

    While I’m able to send data to the watch using updateApplicationContext I’m stuck with sending data to the iPhone using didReceiveMessage:

    if session.reachable == true {
    print(“Session reachable”)
    session.sendMessage([“test” : “test”], replyHandler: nil, errorHandler: nil)}

    The Session is reachable as the statement is executed but nothing seems to be called in the WatchSessionManager.

    Did I miss something?

    • WatchSessionManager is my own class to wrap around the singleton. Take a look at my latest blog post for details (can easily be applied to interactive messaging):

      • sparetimedev

        Thanks for your response.

        I never succeed in using sendMessage to send a message from Watch to iPhone when WCSession is wrapped into a Singleton. When I use WCSession NOT wrapped into a Singleton everything works as expected.

        So let me rephrase my question 🙂

        Have you ever successfully sent a message from Watch to iPhone with sendMessage when WCSession is wrapped into a Singleton?

        Thanks again 🙂

  • Seyoung

    Thank you so much. This is a great article. I always wonder thought, why do you make extensions unnecessarily? I think the way you make extensions are not what extensions are made for. You could have simply make them as a whole class?

  • charlie

    Your posts are amazing – one question though, I am trying to get the singleton based design you lay out to work, but even when copy pasting the github I am recieving “‘paired’ is unavailable” and “‘watchAppInstalled’ is unavailable” where validSession is set up. Any thoughts? Not sure if it is wanting me to startSession before validating like you have it… Thanks!

    • You don’t need to do the watchAppInstalled and paired on the Watch App part! Since to use the Watch App, you have to have these two things in place. But you do need the check on the iOS side.

  • charlie

    Do you happen to have an example of you using this singleton to sendMessage? I am really struggling with these reply blocks and could use some sample code.

  • Vũ Trịnh Hoàng

    I have stuck with “Reachable” property of WCSession. I move Watch on foreground after sleeping, sometime, “Reachable” property is NO although both Phone and Watch on foreground.
    Is it bug of WatchConnectivity FW?

  • Алексей Жутенков

    Hi Natasha. Can you help me? I tried all. I must send some message to phone and recieve some back to watch. I tried all, send via context,via interactive messaging, in empty project all work fine, but when i made it in work(big) project sharing data via watch-phone-watch work when ios app in foreground. When app in background i recieved error via errorHandler – “WCErrorDomain Code=7014 “Payload could not be delivered.” I don’t know, why it work weird for me in general project(with a lot of modules). I’m sharing small dictionary for test [“test”: “test123”]

    • Алексей Жутенков

      I solved it. I updated pod for flurry and all work fine.

  • Jorge Alberto Lopez

    hi, excellent article, one question, we can obtain a uniq identifier of the watch programmatically??, i need to register in my own app the watch, for example the user press a option like “Pair with app” , and the watch become reliable for my app. So, when the user try to use the app in the watch, i validate in the app in the iPhone if the watch is reliable and display the confident information