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 ↗Code shown on screen · 12 snippets
updateActiveComplications
class ExtensionDelegate: NSObject, WKExtensionDelegate {
func updateActiveComplications() {
let complicationServer = CLKComplicationServer.sharedInstance()
if let activeComplications = complicationServer.activeComplications {
for complication in activeComplications {
complicationServer.reloadTimeline(for: complication)
}
}
}
} getCurrentTimelineEntry
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
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
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)
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
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
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
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
class WeatherDataProvider : NSObject, URLSessionDownloadDelegate {
var completionHandler : ((_ update: Bool) -> Void)?
func refresh(_ completionHandler: @escaping (_ update: Bool) -> Void) {
self.completionHandler = completionHandler
} didFinishDownloadingTo
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
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
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
Related sessions
-
32 min -
20 min -
23 min -
14 min