简体   繁体   English

Swift 后台执行

[英]Swift Background Execution

I have a step function on AWS triggered by an HTTP Post request.我在 AWS 上有一个步骤 function 由 HTTP 发布请求触发。 The function can take a few seconds to complete. function 可能需要几秒钟才能完成。 I'd like for execution to continue if the user puts the app into the background, and to correctly navigate to the next screen once the user puts the app back into the foreground (if execution has finished).如果用户将应用程序置于后台,我希望继续执行,并在用户将应用程序放回前台后正确导航到下一个屏幕(如果执行已完成)。

My API Client endpoint looks like this:我的 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()
}

I'd like to know the best practice for implementing this call.我想知道实现这个调用的最佳实践。 I'm currently using beginBackgroundTask below.我目前正在使用下面的 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>
    }
}

However, implementation only works if I have nested DispatchQueue.main.async blocks where I perform more logic after making the HTTP request (like determining which screen to navigate to next after we receive the response.但是,只有当我嵌套了 DispatchQueue.main.async 块时, implementation才有效,在发出 HTTP 请求后执行更多逻辑(例如在收到响应后确定要导航到下一个屏幕。

Is this the best way to do it?这是最好的方法吗? Is it ok to have a few different nested DispatchQueue.main.async blocks inside the DispatchQueue.global block?在 DispatchQueue.global 块内可以有几个不同的嵌套 DispatchQueue.main.async 块吗? Should I post the .receive(on: ) to DispatchQueue.global?我应该将.receive(on: )发布到 DispatchQueue.global 吗?

You don't have to dispatch this background task to a background queue at all.您根本不必将此后台任务分派到后台队列。 (Don't conflate the “background task”, which refers to the app state, with the “background queue”, which governs which threads are used.) (不要将指应用程序 state 的“后台任务”与控制使用哪些线程的“后台队列”混为一谈。)

Besides, as the documentation says, the expiration handler closure runs on the main thread:此外,正如文档所述,到期处理程序闭包在主线程上运行:

The system calls the handler synchronously on the main thread, blocking the app's suspension momentarily.系统在主线程上同步调用处理程序,暂时阻止应用程序的挂起。

So you really want to keep all interaction with backgroundTaskID on the main thread, anyway, or else you would have to implement some other synchronization mechanism.因此,无论如何,您确实希望在主线程上保留与backgroundTaskID的所有交互,否则您将不得不实现一些其他同步机制。


And as a matter of good practice, make sure to end your background task when your asynchronous request is done (rather than relying on the expiration/timeout closure).作为一个好的做法,确保在异步请求完成时结束后台任务(而不是依赖过期/超时关闭)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM