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…

Swift

1

2

3

letsession=WCSession.defaultSession()

session.delegate=self

session.activateSession()

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

Swift

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

importWatchConnectivity

// Note that the WCSessionDelegate must be an NSObject

// So no, you cannot use the nice Swift struct here!

classWatchSessionManager:NSObject,WCSessionDelegate{

// Instantiate the Singleton

staticletsharedManager=WatchSessionManager()

private overrideinit(){

super.init()

}

// Keep a reference for the session,

// which will be used later for sending / receiving data

private letsession=WCSession.defaultSession()

// Activate Session

// This needs to be called to activate the session before first use!

funcstartSession(){

session.delegate=self

session.activateSession()

}

}

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.

isSupported

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:

Swift

1

2

3

4

5

ifWCSession.isSupported(){

letsession=WCSession.defaultSession()

session.delegate=self

session.activateSession()

}

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.

paired

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.

watchAppInstalled

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:

// This is a computed property, since the user can pair their device and / or

// install your app while using your iOS app, so this can become valid

private varvalidSession:WCSession?{

// paired - the user has to have their device paired to the watch

// watchAppInstalled - the user must have your watch app installed

// Note: if the device is paired, but your watch app is not installed

// consider prompting the user to install it for a better experience

ifletsession=session wheresession.paired&&session.watchAppInstalled{

returnsession

}

returnnil

}

// truncated... see above

}

complicationEnabled

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.

sessionWatchStateDidChange

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:

Swift

1

2

3

4

/** Called when any of the Watch state properties change */

funcsessionWatchStateDidChange(session:WCSession){

// handle state change here

}

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.

reachable

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:

Swift

1

2

3

4

5

6

7

8

9

10

11

12

// MARK: Interactive Messaging

extensionWatchSessionManager{

// Live messaging! App has to be reachable

private varvalidReachableSession:WCSession?{

// check for validSession on iOS only (see above)

// in your Watch App, you can just do an if session.reachable check

ifletsession=validSession wheresession.reachable{

returnsession

}

returnnil

}

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:

Swift

1

2

3

4

5

6

7

8

funcsessionReachabilityDidChange(session:WCSession){

// handle session reachability change

ifsession.reachable{

// great! continue on with Interactive Messaging

}else{

// 😥 prompt the user to unlock their iOS device

}

}

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!