简体   繁体   English

当应用程序在后台时将用户的位置转发到服务器(Swift/iOS)

[英]Forwarding user's location to server when application is in the background (Swift/iOS)

I need to keep my server updated with user's location even when the app is in the background or terminated.即使应用程序在后台或终止,我也需要使用用户的位置更新我的服务器。

The location updating is working just fine and seems to wake the application as wanted.位置更新工作得很好,似乎可以根据需要唤醒应用程序。

My problem is regarding the forwarding of the user's location via a PUT request to the server.我的问题是关于通过PUT请求将用户的位置转发到服务器。 I was able to go through the code with breakpoints and it goes well except that when I check with Charles if requests are going though, nothing appears.我能够通过带有断点的代码 go 并且它运行良好,除了当我与 Charles 检查请求是否正在执行时,什么都没有出现。

Here is what I have so far:这是我到目前为止所拥有的:

API Client 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 (yes, the application is using SwiftUI and Combine and I target iOS 13 or later) SceneDelegate (是的,应用程序正在使用SwiftUICombine ,我的目标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()
    }
}

So basically, whenever my app goes in background, I set my delegate, restart the location updates and whenever an update comes, my interactor is called and a request is triggered.所以基本上,每当我的应用程序进入后台时,我都会设置我的委托,重新启动位置更新,每当有更新时,都会调用我的交互器并触发请求。

According to breakpoints, eveything just works fine up to backgroundURLSession?.dataTask(with: urlRequest) .根据断点,一切都可以正常工作到backgroundURLSession?.dataTask(with: urlRequest) But for some reason the request never gets fired.但由于某种原因,该请求永远不会被解雇。

I obviously checked Background Modes capabilities Location updates and Background fetch .我显然检查了Background Modes功能Location updatesBackground fetch

Any idea why?知道为什么吗?

That's correct, the line没错,线

backgroundURLSession?.dataTask(with: urlRequest)

does nothing.什么也没做。 The way to do networking with a session task is to say resume , and you never say that.使用 session 任务进行联网的方法是说resume ,而你永远不会那样说。 Your task is created and just thrown away.您的任务已创建并被丢弃。 (I'm surprised the compiler doesn't warn about this.) (我很惊讶编译器没有对此发出警告。)

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

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