Watch Connectivity

Episode #252 | 17 minutes | published on January 19, 2017 | Uses watchOS-3, Xcode-8.2
Subscribers Only
In this episode, Conrad Stoll joins us once again to talk about how to use WCSession to pass data back & forth between our watchOS app and our iOS app. We'll use this power for the ultimate good, of course, by ordering a beer straight from our watch.

This episode was authored by Conrad Stoll.

Episode Links

Using WCSession to Send Data

import WatchConnectivity

let session = WCSession.default()
session.delegate = self

if session.activationState != .activated {
    session.activate()
    return
}

var context = session.applicationContext
context["beers"] = arrayOfBeerDictionaries()

do {
    try session.updateApplicationContext(context)
} catch let error {
    print(error)
}

Monitoring Session Activation State

public func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
    if activationState == .activated {
        // Send Data If Necessary
    }
}

Using WCSessionDelegate to Receive Data

func session(session: WCSession, didReceiveApplicationContext applicationContext: [String : AnyObject]) {
    if let arrayOfDictionaries = applicationContext["key"] as? [[String : AnyObject]] {

    }
}

Handling Background Delivery on watchOS App

func handle(_ backgroundTasks: Set<WKRefreshBackgroundTask>) {
    for task in backgroundTasks {
        if let watchConnectivityBackgroundTask = task as? WKWatchConnectivityRefreshBackgroundTask {
            handleWatchConnectivityBackgroundTask(watchConnectivityBackgroundTask)
        } else {
            handleRefreshTask(task)
        }
    }

    completeAllTasksIfReady()
}

Handling Background WatchConnectivity Task Completion Correctly

var watchConnectivityBackgroundTasks: [WKWatchConnectivityRefreshBackgroundTask] = []

override init() {
    super.init()

    let session = WCSession.default()

    // https://developer.apple.com/library/content/samplecode/QuickSwitch/Listings/QuickSwitch_WatchKit_Extension_ExtensionDelegate_swift.html
    session.addObserver(self, forKeyPath: "activationState", options: [], context: nil)
    session.addObserver(self, forKeyPath: "hasContentPending", options: [], context: nil)
}

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    DispatchQueue.main.async {
        self.completeAllTasksIfReady()
    }
}

func completeAllTasksIfReady() {
    let session = WCSession.default()
    // the session's properties only have valid values if the session is activated, so check that first
    if session.activationState == .activated && !session.hasContentPending {
        watchConnectivityBackgroundTasks.forEach { $0.setTaskCompleted() }
        watchConnectivityBackgroundTasks.removeAll()
    }
}

Schedule a Future Background Refresh

WKExtension.shared().scheduleBackgroundRefresh(withPreferredDate: Date(timeIntervalSinceNow: 60 * 5), userInfo: nil) { (error) in

}

Building a Watch App Request/Response System

let session = WCSession.default()
session.sendMessage(["message" : "refreshRequest"], replyHandler: { (items : [String : Any]) in
    if let array = items["beers"] as? [[String : AnyObject]] {
        self.handleArrayOfBeers(array: array)
    }
}) { (_) in

}

Handling Request and Sending Response on iPhone App

func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: @escaping ([String : Any]) -> Void) {
    if let message = message["message"] as? String, message == "refreshRequest" {
        replyHandler(["beers" : arrayOfBeerDictionaries()])
    }
}
blog comments powered by Disqus