8

当用户终止应用程序(强制关闭)时,我需要进行 API 调用。我所做的直接实现如下。

在应用程序委托中,我添加了以下代码。

func applicationWillTerminate(_ application: UIApplication) {
    print("________TERMINATED___________")
    testAPICall()
}

func testAPICall(){
    let url = getURL()
    let contentHeader = ["Content-Type": "application/json"]
    Alamofire.request(url,
                  method: .put,
                  parameters: ["username": "abc@xyz.com"],
                  encoding: JSONEncoding.default,
                  headers: contentHeader).responseJSON { (response) -> Void in
                    print("-----")
                  }
}

但是,没有拨打电话。在浏览文档时,我发现在此方法中完成任务只有 5 秒,最重要的是,进行 api 调用不是要在这里完成的任务。所以我想知道,这样做的方法是什么。

4

3 回答 3

8

这是一个双重问题

第 1 阶段:确保每次用户终止应用程序/在其变为活动状态之前启动 API 调用

你总是可以在你的 appdelegate中使用expiration handler后台模式iOS application

宣布 var bgTask: UIBackgroundTaskIdentifier = UIBackgroundTaskIdentifier(rawValue: 0);

在你的 appdelegate

 func applicationDidEnterBackground(_ application: UIApplication) {

    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.

    bgTask = application.beginBackgroundTask(withName:"MyBackgroundTask", expirationHandler: {() -> Void in
        // Do something to stop our background task or the app will be killed
        application.endBackgroundTask(self.bgTask)
        self.bgTask = UIBackgroundTaskIdentifier.invalid
    })

    DispatchQueue.global(qos: .background).async {
        //make your API call here
    }
    // Perform your background task here
    print("The task has started")
}

后台过期处理程序将确保您每次将应用程序变为非活动状态或终止时都有足够的时间来启动 API 调用

第 2 阶段:确保 API 调用启动成功完成

尽管过期处理程序可能会确保您有足够的时间来启动 API 调用,但它不能确保 API 调用的成功完成。如果 API 调用需要更长的时间并且请求正在进行并且时间用完怎么办?

确保 API 调用一旦启动成功的唯一方法是确保使用正确的配置URLSession

根据文档

后台会话可让您在应用未运行时在后台执行内容的上传和下载。

链接:https ://developer.apple.com/documentation/foundation/nsurlsession?language=objc

所以利用后台会话并使用上传任务。与其拥有一个简单的 get/post API,您将使用一些参数,而是要求您的后端开发人员接受一个文件并将所有参数数据放入该文件(如果有的话)并使用后台会话启动上传任务。

一旦上传任务从后台会话开始,即使在您的应用程序被终止后,iOS 也会负责其完成(除非您显然面临身份验证挑战)。

我相信这是确保启动 API 调用并确保在应用程序处于非活动/终止状态后完成的最接近的方法。我与一位苹果开发人员讨论过同样的问题,他们同意这可能是一个可能的解决方案:)

希望能帮助到你

于 2019-03-19T07:25:43.197 回答
1

这里的主要思想是在应用程序终止之前进行同步调用

func applicationWillTerminate(_ application: UIApplication) {
        
     let semaphore: dispatch_semaphore_t = dispatch_semaphore_create(0)
     let request = NSMutableURLRequest(URL:url)
     let task = NSURLSession.sharedSession().dataTaskWithRequest(request, 
                     completionHandler: {
            taskData, _, error -> () in
            
            dispatch_semaphore_signal(semaphore);
      })
      task.resume()
      dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)
}

提示:

调度信号量是传统计数信号量的有效实现。仅当调用线程需要被阻塞时,调度信号量才会向下调用内核。如果调用信号量不需要阻塞,则不进行内核调用。

您可以通过调用 signal() 方法来增加信号量计数,并通过调用 wait() 或其指定超时的变体之一来减少信号量计数。

于 2019-12-19T15:49:14.190 回答
0

这是完成此任务的简单方法-

func applicationWillTerminate(_ application: UIApplication) {
    
let sem = DispatchSemaphore(value: 0)
startSomethingAsync(completionHandler: {

sem.signal()//When task complete then signal will call
 })
sem.wait()//waiting until task complete
}
于 2022-02-25T16:40:44.510 回答