私はかなり長い間この質問の答えを探してきたので、私はそれを投稿するためにいくつかの反対票を投じることをいとわないと思っています。
基本的に、Appleが提供するApple Watchバックグラウンド更新のサンプルコードが実際に機能するようにします(以下のリンクとコード)。
私はシミュレータとApple Watch Series 2を搭載したiPhone 6sの両方で試しましたが、バックグラウンドタスクが時間を更新するポイントまで正常に完了していません。私は時計アプリをドックに固定しようとし、アプリをフォアグラウンドに保持して、シミュレータと実際の時計の両方でバックグラウンドに送信しようとしました。 XcodeまたはApple Watchが機能するアップデートを受信するかどうかを確認するために、ほぼ1年も待ちました。
Appleが提供するコードを正しく機能させるために誰かが正常に変更したことはありますか?
ここで実行可能なサンプルプロジェクト全体をダウンロードできます。 WatchBackgroundRefresh:WKRefreshBackgroundTaskを使用して、バックグラウンドでWatchKitアプリを更新します
/*
Copyright (C) 2016-2017 Apple Inc. All Rights Reserved.
See LICENSE.txt for this sample’s licensing information
Abstract:
The main interface controller.
*/
import WatchKit
import Foundation
class MainInterfaceController: WKInterfaceController, WKExtensionDelegate, URLSessionDownloadDelegate {
// MARK: Properties
let sampleDownloadURL = URL(string: "http://devstreaming.Apple.com/videos/wwdc/2015/802mpzd3nzovlygpbg/802/802_designing_for_Apple_watch.pdf?dl=1")!
@IBOutlet var timeDisplayLabel: WKInterfaceLabel!
private let dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateStyle = .none
formatter.timeStyle = .long
return formatter
}()
// MARK: WKInterfaceController
override func awake(withContext context: Any?) {
super.awake(withContext: context)
// Configure interface objects here.
WKExtension.shared().delegate = self
updateDateLabel()
}
// MARK: WKExtensionDelegate
func handle(_ backgroundTasks: Set<WKRefreshBackgroundTask>) {
for task : WKRefreshBackgroundTask in backgroundTasks {
print("received background task: ", task)
// only handle these while running in the background
if (WKExtension.shared().applicationState == .background) {
if task is WKApplicationRefreshBackgroundTask {
// this task is completed below, our app will then suspend while the download session runs
print("application task received, start URL session")
scheduleURLSession()
}
}
else if let urlTask = task as? WKURLSessionRefreshBackgroundTask {
let backgroundConfigObject = URLSessionConfiguration.background(withIdentifier: urlTask.sessionIdentifier)
let backgroundSession = URLSession(configuration: backgroundConfigObject, delegate: self, delegateQueue: nil)
print("Rejoining session ", backgroundSession)
}
// make sure to complete all tasks, even ones you don't handle
task.setTaskCompleted()
}
}
// MARK: Snapshot and UI updating
func scheduleSnapshot() {
// fire now, we're ready
let fireDate = Date()
WKExtension.shared().scheduleSnapshotRefresh(withPreferredDate: fireDate, userInfo: nil) { error in
if (error == nil) {
print("successfully scheduled snapshot. All background work completed.")
}
}
}
func updateDateLabel() {
let currentDate = Date()
timeDisplayLabel.setText(dateFormatter.string(from: currentDate))
}
// MARK: URLSession handling
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
print("NSURLSession finished to url: ", location)
updateDateLabel()
scheduleSnapshot()
}
func scheduleURLSession() {
let backgroundConfigObject = URLSessionConfiguration.background(withIdentifier: NSUUID().uuidString)
backgroundConfigObject.sessionSendsLaunchEvents = true
let backgroundSession = URLSession(configuration: backgroundConfigObject)
let downloadTask = backgroundSession.downloadTask(with: sampleDownloadURL)
downloadTask.resume()
}
// MARK: IB actions
@IBAction func ScheduleRefreshButtonTapped() {
// fire in 20 seconds
let fireDate = Date(timeIntervalSinceNow: 20.0)
// optional, any SecureCoding compliant data can be passed here
let userInfo = ["reason" : "background update"] as NSDictionary
WKExtension.shared().scheduleBackgroundRefresh(withPreferredDate: fireDate, userInfo: userInfo) { (error) in
if (error == nil) {
print("successfully scheduled background task, use the crown to send the app to the background and wait for handle:BackgroundTasks to fire.")
}
}
}
}
シミュレータで実行すると、次のように出力されます。他の構成で実行したときの同様の出力(ただし、必ずしも完全に同じである必要はありません):
successfully scheduled background task, use the crown to send the app to the background and wait for handle:BackgroundTasks to fire.
received background task: <WKSnapshotRefreshBackgroundTask: 0x7b019030>
received background task: <WKApplicationRefreshBackgroundTask: 0x7a711290>
application task received, start URL session
これを見つける可能性のある人にとっては、私が見た2つの問題がありました。どちらもURLSessionスケジューリングに関するものです。これらの変更により、Appleサンプルコードは実際には少なくともシミュレータ上では機能します。
-sampleDownloadURL
は安全である必要があるため、HTTPSを使用したURLが必要です。これは機能します: https://api.weather.gov/points/42.3584,-71.0598/forecast
-URLSession
のデリゲートがself
に設定されなかったように見えるので、次の変更により修正されました。
_let backgroundSession = URLSession(configuration: backgroundConfigObject, delegate: self, delegateQueue: nil)
_
次のworking(ただし、完成度は低くなります)コードが非常に役に立ちました: watchOS 3の新機能:バックグラウンドタスク
編集:発生する可能性のあるもう1つの問題は、タスクが受信された直後に完了することです。実際には、それらを(たとえば、ローカル変数に)保存して、そのタスクのすべての処理が完了した後に完了する必要があります。このコードの場合、これはWKApplicationRefreshBackgroundTask
にぶら下がっており、scheduleSnapshot
への呼び出しの直後までその上でsetTaskCompleted()
を呼び出さないことを意味すると思います。