当应用程序在后台时将用户的位置转发到服务器 (Swift/iOS)
Forwarding user's location to server when application is in the background (Swift/iOS)
即使应用程序在后台运行或已终止,我也需要使用用户的位置更新我的服务器。
位置更新工作正常,似乎可以按需唤醒应用程序。
我的问题是关于通过 PUT
请求向服务器转发用户位置。
我能够通过断点检查代码并且一切顺利,除了当我检查 Charles 是否有请求时,没有出现任何内容。
这是我目前的情况:
API 客户端
final class BackgroundNetwork: NSObject, BackgroundNetworkInterface, URLSessionDelegate {
private let keychainStorage: Storage
private var backgroundURLSession: URLSession?
init(keychainStorage: Storage) {
self.keychainStorage = keychainStorage
super.init()
defer {
let sessionConfiguration = URLSessionConfiguration.background(withIdentifier: "backgroundURLSession")
sessionConfiguration.sessionSendsLaunchEvents = true
sessionConfiguration.allowsCellularAccess = true
backgroundURLSession = URLSession(configuration: sessionConfiguration,
delegate: self,
delegateQueue: nil)
}
}
func put<T: Encodable>(url: URL, headers: Headers, body: T) {
var urlRequest = URLRequest(url: url)
urlRequest.httpMethod = "PUT"
let authenticationToken: String? = try? keychainStorage.get(forKey: StorageKeys.authenticationToken)
if let authenticationToken = authenticationToken {
urlRequest.setValue(String(format: "Bearer %@", authenticationToken), forHTTPHeaderField: "Authorization")
}
headers.forEach { (key, value) in
if let value = value as? String {
urlRequest.setValue(value, forHTTPHeaderField: key)
}
}
do {
let jsonData = try JSONEncoder().encode(body)
urlRequest.httpBody = jsonData
} catch {
#if DEBUG
print("\(error.localizedDescription)")
#endif
}
backgroundURLSession?.dataTask(with: urlRequest)
}
}
AppDelegate
// ...
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
if launchOptions?[UIApplication.LaunchOptionsKey.location] != nil {
environment.locationInteractor.backgroundDelegate = self
_ = environment.locationInteractor.start()
}
return true
}
// ...
extension AppDelegate: LocationInteractorBackgroundDelegate {
func locationDidUpdate(location: CLLocation) {
taskId = UIApplication.shared.beginBackgroundTask {
UIApplication.shared.endBackgroundTask(self.taskId)
self.taskId = .invalid
}
environment.tourInteractor.updateLocationFromBackground(latitude: Float(location.coordinate.latitude),
longitude: Float(location.coordinate.longitude))
UIApplication.shared.endBackgroundTask(taskId)
taskId = .invalid
}
}
SceneDelegate(是的,应用程序正在使用 SwiftUI
和 Combine
,我的目标是 iOS 13
或更高版本)
func sceneWillEnterForeground(_ scene: UIScene) {
if let environment = (UIApplication.shared.delegate as? AppDelegate)?.environment {
environment.locationInteractor.backgroundDelegate = nil
}
}
func sceneDidEnterBackground(_ scene: UIScene) {
if let appDelegate = (UIApplication.shared.delegate as? AppDelegate) {
appDelegate.environment.locationInteractor.backgroundDelegate = appDelegate
_ = appDelegate.environment.locationInteractor.start()
}
}
所以基本上,每当我的应用进入后台时,我都会设置我的委托,重新启动位置更新,每当有更新时,我的交互器就会被调用并触发请求。
根据断点,在 backgroundURLSession?.dataTask(with: urlRequest)
之前一切正常。但由于某种原因,请求从未被触发。
我显然检查了 Background Modes
功能 Location updates
和 Background fetch
。
知道为什么吗?
没错,这条线
backgroundURLSession?.dataTask(with: urlRequest)
什么都不做。用会话任务做网络的方法是说resume
,而你永远不会那样说。您的任务已创建并被丢弃。 (我很惊讶编译器没有对此发出警告。)
即使应用程序在后台运行或已终止,我也需要使用用户的位置更新我的服务器。
位置更新工作正常,似乎可以按需唤醒应用程序。
我的问题是关于通过 PUT
请求向服务器转发用户位置。
我能够通过断点检查代码并且一切顺利,除了当我检查 Charles 是否有请求时,没有出现任何内容。
这是我目前的情况:
API 客户端
final class BackgroundNetwork: NSObject, BackgroundNetworkInterface, URLSessionDelegate {
private let keychainStorage: Storage
private var backgroundURLSession: URLSession?
init(keychainStorage: Storage) {
self.keychainStorage = keychainStorage
super.init()
defer {
let sessionConfiguration = URLSessionConfiguration.background(withIdentifier: "backgroundURLSession")
sessionConfiguration.sessionSendsLaunchEvents = true
sessionConfiguration.allowsCellularAccess = true
backgroundURLSession = URLSession(configuration: sessionConfiguration,
delegate: self,
delegateQueue: nil)
}
}
func put<T: Encodable>(url: URL, headers: Headers, body: T) {
var urlRequest = URLRequest(url: url)
urlRequest.httpMethod = "PUT"
let authenticationToken: String? = try? keychainStorage.get(forKey: StorageKeys.authenticationToken)
if let authenticationToken = authenticationToken {
urlRequest.setValue(String(format: "Bearer %@", authenticationToken), forHTTPHeaderField: "Authorization")
}
headers.forEach { (key, value) in
if let value = value as? String {
urlRequest.setValue(value, forHTTPHeaderField: key)
}
}
do {
let jsonData = try JSONEncoder().encode(body)
urlRequest.httpBody = jsonData
} catch {
#if DEBUG
print("\(error.localizedDescription)")
#endif
}
backgroundURLSession?.dataTask(with: urlRequest)
}
}
AppDelegate
// ...
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
if launchOptions?[UIApplication.LaunchOptionsKey.location] != nil {
environment.locationInteractor.backgroundDelegate = self
_ = environment.locationInteractor.start()
}
return true
}
// ...
extension AppDelegate: LocationInteractorBackgroundDelegate {
func locationDidUpdate(location: CLLocation) {
taskId = UIApplication.shared.beginBackgroundTask {
UIApplication.shared.endBackgroundTask(self.taskId)
self.taskId = .invalid
}
environment.tourInteractor.updateLocationFromBackground(latitude: Float(location.coordinate.latitude),
longitude: Float(location.coordinate.longitude))
UIApplication.shared.endBackgroundTask(taskId)
taskId = .invalid
}
}
SceneDelegate(是的,应用程序正在使用 SwiftUI
和 Combine
,我的目标是 iOS 13
或更高版本)
func sceneWillEnterForeground(_ scene: UIScene) {
if let environment = (UIApplication.shared.delegate as? AppDelegate)?.environment {
environment.locationInteractor.backgroundDelegate = nil
}
}
func sceneDidEnterBackground(_ scene: UIScene) {
if let appDelegate = (UIApplication.shared.delegate as? AppDelegate) {
appDelegate.environment.locationInteractor.backgroundDelegate = appDelegate
_ = appDelegate.environment.locationInteractor.start()
}
}
所以基本上,每当我的应用进入后台时,我都会设置我的委托,重新启动位置更新,每当有更新时,我的交互器就会被调用并触发请求。
根据断点,在 backgroundURLSession?.dataTask(with: urlRequest)
之前一切正常。但由于某种原因,请求从未被触发。
我显然检查了 Background Modes
功能 Location updates
和 Background fetch
。
知道为什么吗?
没错,这条线
backgroundURLSession?.dataTask(with: urlRequest)
什么都不做。用会话任务做网络的方法是说resume
,而你永远不会那样说。您的任务已创建并被丢弃。 (我很惊讶编译器没有对此发出警告。)