Подтвердить что ты не робот

Пример кода Apple для расширенного фона WatchKit

Я долгое время искал ответ на этот вопрос, поэтому, думаю, я готов рисковать некоторыми downvotes, чтобы опубликовать его.

В принципе, я хочу, чтобы Apple предоставил образец кода для обновления фонаря Apple Watch на самом деле (ссылка и код ниже).

Я пробовал как в симуляторе, так и на iPhone 6 с Apple Watch Series 2, а фоновые задачи никогда не были успешно завершены до такой степени, когда время обновляется. Я попытался прикрепить приложение часов к доке, и я попытался сохранить приложение на переднем плане и отправить его на задний план, как в симуляторе, так и на фактических часах. Я даже пробовал ждать почти год, чтобы узнать, получит ли Xcode или Apple Watch обновление, которое заставит его работать.

Кто-нибудь успешно изменил код, предоставленный 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
4b9b3361

Ответ 1

Для тех, кто может найти это, были две проблемы, которые я видел, и с графиком URLSession. С этими изменениями я думаю, что пример кода Apple действительно работает, по крайней мере, на симуляторе.

- sampleDownloadURL должен быть безопасным, поэтому необходим URL с HTTPS. Это работает: https://api.weather.gov/points/42.3584,-71.0598/forecast

-It выглядит так, как делегат для URLSession никогда не был установлен в self, поэтому следующее изменение зафиксировало, что:

let backgroundSession = URLSession(configuration: backgroundConfigObject, delegate: self, delegateQueue: nil)

Следующий полезный (хотя и неполный) код был очень полезным: Что нового в watchOS 3: Фоновые задачи

Изменить: Еще одна проблема, которая может возникнуть, заключается в том, что задачи выполняются сразу же после их получения. На практике они должны быть сохранены (например, в локальной переменной), а затем завершены после завершения всей обработки для этой задачи. Для этого кода я думаю, что это означает, что он навешивается на WKApplicationRefreshBackgroundTask и не называет setTaskCompleted() на нем, пока сразу после вызова scheduleSnapshot.