首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >iOS10背景提取

iOS10背景提取
EN

Stack Overflow用户
提问于 2017-06-01 16:59:56
回答 2查看 14.3K关注 0票数 14

我已经尝试过实现后台抓取,希望能不时唤醒应用程序。

我做了以下工作:

代码语言:javascript
运行
复制
      func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
application.setMinimumBackgroundFetchInterval(UIApplicationBackgroundFetchIntervalMinimum)
        return true
      }

func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
    debugPrint("performFetchWithCompletionHandler")
    getData()
    completionHandler(UIBackgroundFetchResult.newData)
  }

  func getData(){
    debugPrint("getData")
  }

我也已经启用了后台获取功能。这就是我所做的一切。然后我运行这个应用程序。即使过了一个小时(电话睡着了),这个功能也没有打过电话。

为了使函数被调用,我还需要做什么?

EN

回答 2

Stack Overflow用户

发布于 2017-06-02 00:11:36

您已经完成了许多必要的步骤:

话虽如此,但有几点意见:

  1. 我会在“设置”、“一般”、“背景应用程序刷新”中检查应用程序的权限。这不仅确保您成功地请求背景获取在您的plist,而且它是启用的一般,以及您的应用程序。
  2. 确保你没有杀死该应用程序(即双击主按钮,然后在应用程序上滑动以迫使应用程序终止)。如果该应用程序被杀死,它将阻止后台抓取无法正常工作。
  3. 您使用的是debugPrint,但这只在从Xcode运行它时才起作用。但是您应该在物理设备上这样做,而不是从Xcode运行它。您需要使用一个日志系统来显示您的活动,即使没有通过Xcode运行应用程序。 我使用os_log并在控制台上观看它(参见WWDC 2016 统一日志记录和活动跟踪)或通过UserNotifications框架发布通知(参见WWDC 2016 通知简介),因此当应用程序在后台做一些值得注意的事情时,会通知我。或者我已经创建了自己的外部日志系统(例如,写入某个文本文件或plist)。但是您需要某种方式来观察print/debugPrint之外的活动,因为您希望在不独立于Xcode的情况下测试它。在运行连接到调试器的应用程序时,任何与背景相关的行为都会发生变化。
  4. 作为PGDev说,您无法控制后台获取发生的时间。它考虑了许多缺乏文档的因素(wifi连接,连接电源,用户的应用程序使用频率,当其他应用程序可能正在旋转等等)。 话虽如此,但当我启用后台抓取功能时,我从设备(而不是Xcode)运行该应用程序,并将其连接到wifi和power上,第一个后台获取功能出现在我的iPhone 7+上,它在挂起应用程序后的10分钟内就出现了。
  5. 您的代码目前没有执行任何提取请求。这引起了两方面的关切:
代码语言:javascript
运行
复制
- Make sure that the test app actually issues `URLSession` request at some point its normal course of action when you run it (i.e. when you run the app normally, not via background fetch). If you have a test app that doesn't issue any requests, it doesn't appear to enable the background fetch feature. (Or at the very least, it severely affects the frequency of the background fetch requests.)
- Reportedly, the OS will stop issuing subsequent background fetch calls to your app if prior background fetch calls didn't actually result in a network request being issued. (This may be a permutation of the prior point; it's not entirely clear.) I suspect Apple is trying to prevent developers using background fetch mechanism for tasks that aren't really fetching anything.

  1. 注意,您的应用程序没有太多时间来执行请求,所以如果您发出请求,您可能只想询问是否有可用的数据,而不是尝试下载所有数据本身。然后,您可以启动一个后台会话来启动耗时的下载。显然,如果检索到的数据量可以忽略不计,那么这不太可能是一个问题,但是请确保您能够合理地快速地调用后台完成(30秒,IIRC)。如果没有在该时间段内调用它,则会影响后续的背景获取请求是否会被尝试。
  2. 如果应用程序没有处理后台请求,我可能建议将应用程序从设备中删除并重新安装。我曾经遇到过这样的情况:当测试后台获取时,请求停止工作(可能是因为在测试应用程序的前一次迭代时,后台获取请求失败了)。我发现移除和重新安装它是重置后台获取进程的好方法。

为了举例说明,下面是一个成功执行背景获取的示例。我还添加了UserNotifications框架和os_log调用,以提供一种在没有连接到Xcode (即printdebugPrint不再有用的地方)时监视进度的方法:

代码语言:javascript
运行
复制
// AppDelegate.swift

import UIKit
import UserNotifications
import os.log

@UIApplicationMain
class AppDelegate: UIResponder {

    var window: UIWindow?

    /// The URLRequest for seeing if there is data to fetch.

    fileprivate var fetchRequest: URLRequest {
        // create this however appropriate for your app
        var request: URLRequest = ...
        return request
    }

    /// A `OSLog` with my subsystem, so I can focus on my log statements and not those triggered 
    /// by iOS internal subsystems. This isn't necessary (you can omit the `log` parameter to `os_log`,
    /// but it just becomes harder to filter Console for only those log statements this app issued).

    fileprivate let log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "log")

}

// MARK: - UIApplicationDelegate

extension AppDelegate: UIApplicationDelegate {

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

        // turn on background fetch

        application.setMinimumBackgroundFetchInterval(UIApplicationBackgroundFetchIntervalMinimum)

        // issue log statement that app launched

        os_log("didFinishLaunching", log: log)

        // turn on user notifications if you want them

        UNUserNotificationCenter.current().delegate = self

        return true
    }

    func applicationWillEnterForeground(_ application: UIApplication) {
        os_log("applicationWillEnterForeground", log: log)
    }

    func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
        os_log("performFetchWithCompletionHandler", log: log)
        processRequest(completionHandler: completionHandler)
    }

}

// MARK: - UNUserNotificationCenterDelegate

extension AppDelegate: UNUserNotificationCenterDelegate {

    func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        os_log("willPresent %{public}@", log: log, notification)
        completionHandler(.alert)
    }

    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
        os_log("didReceive %{public}@", log: log, response)
        completionHandler()
    }
}

// MARK: - Various utility methods

extension AppDelegate {

    /// Issue and process request to see if data is available
    ///
    /// - Parameters:
    ///   - prefix: Some string prefix so I know where request came from (i.e. from ViewController or from background fetch; we'll use this solely for logging purposes.
    ///   - completionHandler: If background fetch, this is the handler passed to us by`performFetchWithCompletionHandler`.

    func processRequest(completionHandler: ((UIBackgroundFetchResult) -> Void)? = nil) {
        let task = URLSession.shared.dataTask(with: fetchRequest) { data, response, error in

            // since I have so many paths execution, I'll `defer` this so it captures all of them

            var result = UIBackgroundFetchResult.failed
            var message = "Unknown"

            defer {
                self.postNotification(message)
                completionHandler?(result)
            }

            // handle network errors

            guard let data = data, error == nil else {
                message = "Network error: \(error?.localizedDescription ?? "Unknown error")"
                return
            }

            // my web service returns JSON with key of `success` if there's data to fetch, so check for that

            guard
                let json = try? JSONSerialization.jsonObject(with: data),
                let dictionary = json as? [String: Any],
                let success = dictionary["success"] as? Bool else {
                    message = "JSON parsing failed"
                    return
            }

            // report back whether there is data to fetch or not

            if success {
                result = .newData
                message = "New Data"
            } else {
                result = .noData
                message = "No Data"
            }
        }
        task.resume()
    }

    /// Post notification if app is running in the background.
    ///
    /// - Parameters:
    ///
    ///   - message:           `String` message to be posted.

    func postNotification(_ message: String) {

        // if background fetch, let the user know that there's data for them

        let content = UNMutableNotificationContent()
        content.title = "MyApp"
        content.body = message
        let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
        let notification = UNNotificationRequest(identifier: "timer", content: content, trigger: trigger)
        UNUserNotificationCenter.current().add(notification)

        // for debugging purposes, log message to console

        os_log("%{public}@", log: self.log, message)  // need `public` for strings in order to see them in console ... don't log anything private here like user authentication details or the like
    }

}

视图控制器只是请求用户通知的权限,并发出一些随机请求:

代码语言:javascript
运行
复制
import UIKit
import UserNotifications

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // request authorization to perform user notifications

        UNUserNotificationCenter.current().requestAuthorization(options: [.sound, .alert]) { granted, error in
            if !granted {
                DispatchQueue.main.async {
                    let alert = UIAlertController(title: nil, message: "Need notification", preferredStyle: .alert)
                    alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
                    self.present(alert, animated: true, completion: nil)
                }
            }
        }

        // you actually have to do some request at some point for background fetch to be turned on;
        // you'd do something meaningful here, but I'm just going to do some random request...

        let url = URL(string: "http://example.com")!
        let request = URLRequest(url: url)
        let task = URLSession.shared.dataTask(with: request) { data, response, error in
            DispatchQueue.main.async {
                let alert = UIAlertController(title: nil, message: error?.localizedDescription ?? "Sample request finished", preferredStyle: .alert)
                alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
                self.present(alert, animated: true)
            }
        }
        task.resume()
    }

}
票数 17
EN

Stack Overflow用户

发布于 2017-06-01 17:19:55

后台提取由系统在适当的时间间隔内自动启动。

一个非常重要和酷的特点,背景抓取是它的能力,学习的时间,应该允许一个应用程序启动到背景,并得到更新。例如,假设一个用户每天早上8:30左右使用一个新闻应用程序(阅读一些新闻和一些热咖啡)。经过几次使用后,系统了解到,很有可能下一次该应用程序将在同一时间运行,因此它会小心地让它在正常的启动时间(可能是上午8:00左右)前进行更新。这样,当用户打开应用程序,新的和刷新的内容是在那里等待他,而不是相反!这个特性称为使用预测

要测试您编写的代码是否正常工作,可以参考Raywenderlich的教程“ on 后台获取”。

教程https://www.raywenderlich.com/143128/background-modes-tutorial-getting-started (搜索:测试背景获取)

票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/44313278

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档