— iOS, WatchOS, WCSession — 2 min read
The WCSession framework serves as a conduit for data transfer and message passing between a paired Apple Watch and iPhone. It provides a robust set of tools for establishing, managing, and leveraging this communication channel. By utilizing WCSession, developers can create feature-rich applications that seamlessly exchange data and commands between the two devices.
To begin using WCSession, the first step is to establish a session between the Apple Watch and iPhone. This involves setting up the session delegate and activating the session on both devices. Once the session is activated, various methods and properties provided by WCSession can be employed to send messages, transfer files, and monitor the state of the session. You can use a custom class for this, I personally used an ObservableObject in one of my SwiftUI apps. Here is the general gist of getting things started:
1// Example of activating WCSession2if WCSession.isSupported() {3 let session = WCSession.default4 session.delegate = self5 session.activate()6}
One of the fundamental features of WCSession is the ability to send messages containing data from one device to the other. This can be particularly useful for triggering actions or updating information on the recipient device based on user interactions or real-time data changes.
1// Example of sending a message from iPhone to Apple Watch using WCSession2if WCSession.default.isReachable {3 let message = ["key": "value"]4 WCSession.default.sendMessage(message, replyHandler: { reply in5 // Handle reply from Apple Watch6 }, errorHandler: { error in7 // Handle any errors8 })9}
You might potentially want to create a custom struct if your dictionary data gets too complicated. Here's portion of what I used in one of my projects:
1struct SettingsSyncData: Codable, Equatable {2 let dataA: String3 let dataB: Double4 5 static func decode(data: Data) -> SettingsSyncData? {6 let decoder = JSONDecoder()7 return try? decoder.decode(SettingsSyncData.self, from: data)8 }9 10 func encode() -> Data? {11 let encoder = JSONEncoder()12 return try? encoder.encode(self)13 }14}
Because this has an encode function an is codable you can create the dictionary to send like this:
1func syncSettings(syncData: SettingsSyncData) {2 guard let data = syncData.encode() else {3 return4 }5 6 session.sendMessage(["settings_data": data], replyHandler: nil) { error in7 log.error(error)8 }9}
Depending on how critical your data is, you may want to use transferUserInfo
instead(this seems to get higher priority than regular messages and sends it in a FIFO fashion). The frustrating thing is that transferUserInfo
isn't supported by the simulator so your only real option is to test on a physical device...
If you just want to share state between the two devices, and don't care about previous data then check out applicationContext
and updateApplicationContext
. It all really comes down to your specific implementation.
In addition to sending messages, WCSession also supports transferring files between the paired devices. This capability enables the seamless exchange of larger datasets, such as media files or documents, contributing to a more integrated and synchronized user experience.
1// Example of transferring a file from iPhone to Apple Watch using WCSession2if WCSession.default.isReachable {3 if let fileURL = Bundle.main.url(forResource: "example", withExtension: "txt") {4 WCSession.default.transferFile(fileURL, metadata: nil)5 }6}
WCSession provides mechanisms for monitoring the state of the session, including changes in reachability and activation status. These notifications can be leveraged to adapt the behavior of the application based on the current connectivity between the devices.
1// Example of monitoring session state changes2NotificationCenter.default.addObserver(forName: WCSession.didBecomeInactiveNotification, object: nil, queue: nil) { _ in3 // Handle session becoming inactive4}5
6NotificationCenter.default.addObserver(forName: WCSession.didDeactivateNotification, object: nil, queue: nil) { _ in7 // Handle session deactivation8}
You can also get a callback for activation by overridding func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?)
. To override this you'll need to implement WCSessionDelegate in your class:
1class ExampleSyncStore: NSObject, WCSessionDelegate, ObservableObject {2 #if os(iOS)3 public func sessionDidBecomeInactive(_ session: WCSession) {}4 public func sessionDidDeactivate(_ session: WCSession) {}5 #endif6 7 private var session: WCSession = .default8 var isWatch: Bool9
10 init(isWatch: Bool) {11 self.isWatch = isWatch12 super.init()13 14 if WCSession.isSupported() {15 self.session.delegate = self16 self.session.activate()17 }18 }19
20 func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {21 // Activation stuff goes here22 }23}