如何在iOS应用程序处于前台时在后台运行操作

12

我有一个JSON文件,里面填充了一些字符串数据,存放在文档目录中。应用程序的用户界面中有一个UIButton。当按钮按下时,一个新的字符串会附加到JSON文件中。

现在我正在寻找任何iOS服务,可以帮助我使用Swift将这些字符串(来自JSON文件)发送到服务器。而且这个服务应该完全独立于我的代码。 关键是,当我按下UIButton时,第一步是把一个字符串保存到JSON文件中,然后如果有网络连接,该服务应该将此字符串发送到服务器。

当一个字符串成功发送后,它应该从JSON文件中删除。 该服务应该每隔30秒跟踪一次是否有任何字符串被保存到JSON文件中,然后将其发送到服务器。

我通过谷歌搜索发现后台获取,但它会自动触发performFetchWithCompletionHandler函数,我无法知道iOS何时触发它。我想在每30秒后手动触发这种服务。


你找到了解决方案吗?谢谢! - Joan Casadellà
1
它为什么/如何独立于您的应用程序? - Wain
2
如果你正在寻找一个守护程序/服务,那么很遗憾,iOS不支持无限运行的自定义应用程序。 - Cristik
谢谢大家,我已经通过使用NSTimer和时间间隔函数解决了我的问题,它会在每30秒后调用我自己创建的syncService类方法。 - user3314286
@xikitidistant,我的回答有帮到你吗?看起来它对原帖作者有所帮助。 - JAL
是的,@xikitidistant的答案提供了与我使用的相同方法。还有一件事是,我早在你之前就用了你提供的相同函数“application.beginBackgroundTask”“application.endBackgroundTask”来解决了我的问题。但你比我先回答了,这就是为什么我打了勾标记你的答案。你也可以看到我发布的答案。再次感谢jal。 - user3314286
5个回答

13

请查阅苹果 iOS 应用程序编程指南中的后台执行部分。

UIApplication 提供了使用 UIBackgroundTaskIdentifier 启动和结束后台任务的接口。

在你的 AppDelegate 的顶层创建一个类级别的任务标识符:

var backgroundTask = UIBackgroundTaskInvalid

现在,使用您希望完成的操作创建任务,并实现任务在过期之前未能完成的错误情况:

backgroundTask = application.beginBackgroundTaskWithName("MyBackgroundTask") {
    // This expirationHandler is called when your task expired
    // Cleanup the task here, remove objects from memory, etc

    application.endBackgroundTask(self.backgroundTask)
    self.backgroundTask = UIBackgroundTaskInvalid
}

// Implement the operation of your task as background task
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
    // Begin your upload and clean up JSON
    // NSURLSession, AlamoFire, etc

    // On completion, end your task
    application.endBackgroundTask(self.backgroundTask)
    self.backgroundTask = UIBackgroundTaskInvalid
}

1
请注意,如果应用程序未被置于后台,此操作将在前台执行,直到工作完成。 - Cristik
非常好的观点@Cristik。我认为这是最接近OP所要求的解决方案,因为受到苹果限制的影响。 - JAL
谢谢朋友,我已经通过你提供的方法解决了我的问题。 - user3314286

4

我所做的就是采用了JAL上述讨论的方法。

这些是我使用的三种方法。

    func reinstateBackgroundTask() {
        if updateTimer != nil && (backgroundTask == UIBackgroundTaskInvalid) {
            registerBackgroundTask()
           }
    }
    func registerBackgroundTask() {
        backgroundTask = UIApplication.sharedApplication().beginBackgroundTaskWithExpirationHandler {
            [unowned self] in
            self.endBackgroundTask()
        }
        assert(backgroundTask != UIBackgroundTaskInvalid)
    }

    func endBackgroundTask() {
        NSLog("Background task ended.")
        UIApplication.sharedApplication().endBackgroundTask(backgroundTask)
        backgroundTask = UIBackgroundTaskInvalid
    }

updateTimer 是属于 NSTIMER 类型的。 以上函数位于我自己创建的名为 "syncService" 的类中。 该类有一个初始化程序,即

init(){
  NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.reinstateBackgroundTask), name: UIApplicationDidBecomeActiveNotification, object: nil)

        updateTimer = NSTimer.scheduledTimerWithTimeInterval(30.0, target: self, selector: #selector(self.syncAudit), userInfo: nil, repeats: true)
        registerBackgroundTask()

 }

然后我只需调用这个类,整个问题就得到了解决。

3
 DispatchQueue.global(qos: .background).async { // sends registration to background queue

 }

1
请参考NSURLSessionUploadTask,它可能会对您有所帮助。

0

这是JAL的答案的Swift 4版本

  extension UIApplication {
  /// Run a block in background after app resigns activity
   public func runInBackground(_ closure: @escaping () -> Void, expirationHandler: (() -> Void)? = nil) {
       DispatchQueue.main.async {
        let taskID: UIBackgroundTaskIdentifier
        if let expirationHandler = expirationHandler {
            taskID = self.beginBackgroundTask(expirationHandler: expirationHandler)
        } else {
            taskID = self.beginBackgroundTask(expirationHandler: { })
        }
        closure()
        self.endBackgroundTask(taskID)
    }
  }

  }

使用示例

UIApplication.shared.runInBackground({
        //do task here
    }) {
       // task after expiration.
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接