Dunfey · Hotel WWDC as data, est. 1983
Front desk everything
Years
Topics

2020 SwiftUI & UI FrameworksSystem Services

WWDC20 · 22 min · SwiftUI & UI Frameworks / System Services

Keep your complications up to date

Time is of the essence: Discover how your Apple Watch complications can provide relevant information throughout the day and help people get the information they need, when they need it. Learn best practices for capitalizing on your app’s runtime opportunities, incorporating APIs like background app refresh and URLSession, and implementing well-timed push notifications.

Watch at developer.apple.com ↗

Transcript all transcripts

Code shown on screen · 12 snippets

updateActiveComplications swift · at 3:32 ↗
class ExtensionDelegate: NSObject, WKExtensionDelegate {

    func updateActiveComplications() {

       let complicationServer = CLKComplicationServer.sharedInstance()

        if let activeComplications = complicationServer.activeComplications {

            for complication in activeComplications {

               complicationServer.reloadTimeline(for: complication)

            }
        } 
    }
}
getCurrentTimelineEntry swift · at 4:26 ↗
class ComplicationController: NSObject, CLKComplicationDataSource {

    func getCurrentTimelineEntry(for complication: CLKComplication, 
        withHandler handler: @escaping (CLKComplicationTimelineEntry?) -> Void) {

        switch (complication.family) {
  
        case .modularSmall:
           let template = CLKComplicationTemplateModularLargeTallBody.init(
                               headerTextProvider: headerTextProvider, 
                               bodyTextProvider: bodyTextProvider)

            entry = CLKComplicationTimelineEntry(date: Date(), 
                        complicationTemplate: template)
        }

        handler(entry)
    }
}
scheduleBar swift · at 6:06 ↗
private func scheduleBAR(_ first: Bool) {
        let now = Date()
        let scheduledDate = now.addingTimeInterval(first ? 60 : 15*60)

        let info:NSDictionary = [“submissionDate”:now]

        let wkExt = WKExtension.shared()
        wkExt.scheduleBackgroundRefresh(withPreferredDate: scheduledDate, userInfo:info)
        { (error: Error?) in
            if (error != nil) {
                print("background refresh could not be scheduled \(error.debugDescription)")
            } 
        }
   }
handleBAR swift · at 7:08 ↗
class ExtensionDelegate: NSObject, WKExtensionDelegate {
    func handle(_ backgroundTasks: Set<WKRefreshBackgroundTask>) {

        for task in backgroundTasks {

          switch task {
          case let backgroundTask as WKApplicationRefreshBackgroundTask:

                if let userInfo:NSDictionary = backgroundTask.userInfo as? NSDictionary {
                   if let then:Date = userInfo["submissionDate"] as! Date {
                      let interval = Date.init().timeIntervalSince(then)
                      print("interval since request was made \(interval)")
                   }
                }

                self.updateActiveComplications()

                self.scheduleBAR(first: false)

                backgroundTask.setTaskCompletedWithSnapshot(false)
handleBAR (DataProvider) swift · at 8:47 ↗
class ExtensionDelegate: NSObject, WKExtensionDelegate {

    var healthDataProvider: HealthDataProvider

    func handle(_ backgroundTasks: Set<WKRefreshBackgroundTask>) {
        for task in backgroundTasks {
            switch task {
            case let backgroundTask as WKApplicationRefreshBackgroundTask:

                healthDataProvider.refresh() { (update: Bool) -> Void in
                    if update {
                        self.updateActiveComplications()
                    }
                    self.scheduleBAR(first: false)
                    backgroundTask.setTaskCompletedWithSnapshot(false)
                }
Instantiate backgroundURLSession swift · at 11:35 ↗
class WeatherDataProvider : NSObject, URLSessionDownloadDelegate {

    private lazy var backgroundURLSession: URLSession = {
        let config = URLSessionConfiguration.background(withIdentifier: “BackgroundWeather")
        config.isDiscretionary = false
        config.sessionSendsLaunchEvents = true

        return URLSession(configuration: config, delegate: self, delegateQueue: nil)
    }()
Schedule backgroundURLSessionTask swift · at 12:02 ↗
func schedule(_ first: Bool) {

        if backgroundTask == nil {

            if let url = self.currentWeatherURLForLocation(delegate.currentLocationCoordinate)
            {
                let bgTask = backgroundURLSession.downloadTask(with: url)

                bgTask.earliestBeginDate = Date().addingTimeInterval(first ? 60 : 15*60)

                bgTask.countOfBytesClientExpectsToSend = 200
                bgTask.countOfBytesClientExpectsToReceive = 1024

                bgTask.resume()

                backgroundTask = bgTask
            }
        }
    }
}
handle backgroundURLSession swift · at 13:29 ↗
class ExtensionDelegate: NSObject, WKExtensionDelegate {

   var weatherDataProvider:WeatherDataProvider

    func handle(_ backgroundTasks: Set<WKRefreshBackgroundTask>) {
       for task in backgroundTasks {
           switch task {
            
                case let urlSessionTask as WKURLSessionRefreshBackgroundTask:

                    weatherDataProvider.refresh() { (update: Bool) -> Void in
                        weatherDataProvider.schedule(first: false)
                        if update {
                            self.updateActiveComplications()
                        }
                        urlSessionTask.setTaskCompletedWithSnapshot(false)
                    }
handle backgroundURLSession swift · at 13:59 ↗
class WeatherDataProvider : NSObject, URLSessionDownloadDelegate {

    var completionHandler : ((_ update: Bool) -> Void)?

    func refresh(_ completionHandler: @escaping (_ update: Bool) -> Void) {

        self.completionHandler = completionHandler

    }
didFinishDownloadingTo swift · at 14:08 ↗
class WeatherDataProvider : NSObject, URLSessionDownloadDelegate {

    func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask,
                    didFinishDownloadingTo location: URL) {

        if location.isFileURL {
            do {

                let jsonData = try Data(contentsOf: location)
                if let kiteFlyingWeather = KiteFlyingWeather(jsonData) {
                    // Process weather data here.
                }

            } catch let error as NSError {
                print("could not read data from \(location)")
            }
        }
    }
didComplete swift · at 14:23 ↗
func urlSession(_ session: URLSession, task: URLSessionTask, 
                     didCompleteWithError error: Error?) {

        print("session didCompleteWithError \(error.debugDescription)”)

        DispatchQueue.main.async {

           self.completionHandler?(error == nil)

            self.completionHandler = nil

        }
    }
}
Complication Pushes swift · at 17:53 ↗
class PushNotificationProvider : NSObject, PKPushRegistryDelegate {

    func startPushKit() -> Void {
        let pushRegistry = PKPushRegistry(queue: .main)
        pushRegistry.delegate = self
        pushRegistry.desiredPushTypes = [.complication]
    }
  
    func pushRegistry(_ registry: PKPushRegistry, 
                      didUpdate pushCredentials: PKPushCredentials, for type: PKPushType) {
        // Send credentials to server 
    }
  
    func pushRegistry(_ registry: PKPushRegistry, 
                        didReceiveIncomingPushWith payload: PKPushPayload, 
                        for type: PKPushType, completion: @escaping () -> Void) {
        // Process payload
        delegate.updateActiveComplications()
        completion()
    }

Resources