Swift 后台执行

Swift Background Execution

我在 AWS 上有一个由 HTTP Post 请求触发的步骤函数。该功能可能需要几秒钟才能完成。如果用户将应用程序置于后台,我希望继续执行,并在用户将应用程序放回前台(如果执行已完成)后正确导航到下一个屏幕。

我的 API 客户端端点如下所示:

 func connect<OutputType: Decodable>(to request: URLRequestConvertible, decoder: JSONDecoder) -> AnyPublisher<Result<OutputType, Error>, Never> {
    var request = request.asURLRequest()
    
    if let token: String = KeychainWrapper.standard.string(forKey: "apiToken") {
        request.addValue(token, forHTTPHeaderField: "Authorization")
    }
    
    let configuration = URLSessionConfiguration.default
    configuration.waitsForConnectivity = true
    let session = URLSession(configuration: configuration)
    
    return session.dataTaskPublisher(for: request)
        .tryMap({ (data, response) -> Data in
            guard let response = response as? HTTPURLResponse else { throw NetworkError.invalidResponse }
            guard 200..<300 ~= response.statusCode else {
                throw NetworkError.invalidStatusCode(statusCode: response.statusCode)
                
            }
            return data
        })
        .decode(type: OutputType.self, decoder: decoder)
        .map(Result.success)
        .catch { error -> Just<Result<OutputType, Error>> in Just(.failure(error)) }
        .receive(on: DispatchQueue.main)
        .eraseToAnyPublisher()
}

我想知道实现此调用的最佳做法。我目前正在使用下面的 beginBackgroundTask。

func makeRequest() {
    DispatchQueue.global(qos: .userInitiated).async {
        self.backgroundTaskID = UIApplication.shared.beginBackgroundTask (withName: "Request Name") {
            UIApplication.shared.endBackgroundTask(self.backgroundTaskID!)
            self.backgroundTaskID = .invalid
        }
        <implementation>
    }
}

但是,implementation 仅当我嵌套了 DispatchQueue.main.async 块时才有效,在这些块中我在发出 HTTP 请求后执行更多逻辑(例如在我们收到响应后确定导航到下一个屏幕。

这是最好的方法吗?在 DispatchQueue.global 块中嵌套几个不同的 DispatchQueue.main.async 块可以吗?我应该 post .receive(on: ) 到 DispatchQueue.global 吗?

您根本不必将此后台任务分派到后台队列。 (不要将指代应用程序状态的“后台任务”与控制使用哪些线程的“后台队列”混为一谈。)

此外,正如 documentation 所说,过期处理程序闭包在主线程上运行:

The system calls the handler synchronously on the main thread, blocking the app’s suspension momentarily.

所以你真的想在主线程上保留与 backgroundTaskID 的所有交互,否则你将不得不实现一些其他的同步机制。


作为一个好的做法,请确保在完成异步请求后结束后台任务(而不是依赖 expiration/timeout 闭包)。