【问题标题】:App Crashes When Background Location Received收到后台位置时应用程序崩溃
【发布时间】:2018-08-10 12:30:11
【问题描述】:

我正在尝试在后台将位置更新发送到服务器

应用有时在后台崩溃

这是我的locationmanager delegate

func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {

            if let lat = manager.location?.coordinate.latitude,
                let long = manager.location?.coordinate.longitude {
                glat = String(lat)
                glong = String(long)

                if UIApplication.shared.applicationState == .background {
                self.updatebackloc(lat, long: long)
                }

                if UIApplication.shared.applicationState == .active {
                self.updateloc(lat, long: long)
                    locationManager.stopUpdatingLocation()
                    let status = CLLocationManager.authorizationStatus()
                    if (status == CLAuthorizationStatus.authorizedAlways) {
                    locationManager.startMonitoringSignificantLocationChanges()
                    }
                }
            }
            }

这是updatebacklog函数

func updatebackloc(_ lat: CLLocationDegrees, long: CLLocationDegrees) {
        let userID = TegKeychain.get("userID")!
        let parameters: Parameters = ["userID": userID, "lat": lat, "long":long]
        Alamofire.request("https://xxxxx.com/ios/updatebackloc.php", method: .post, parameters: parameters).validate().responseJSON { response in
            switch response.result {
            case .success:
                if let json = response.result.value {
                    var success = 0
                    if let dictJSON = json as? [String: AnyObject] {
                        if let successInteger = dictJSON["success"] as? Int {
                            success = successInteger
                            if success == 1
                            {

                            }
                        }
                    }
                }
            case .failure(_):
                return
            }
        }
    }

didFinishLaunchingWithOptions 部分

if let _ = launchOptions?[UIApplicationLaunchOptionsKey.location] {
                    startSignificationLocation()
                }

startSignificationLocation 函数在didFinishLaunchingWithOptions 上触发

func startSignificationLocation() {
        let locationManager = CLLocationManager()
        locationManager.delegate = self
        locationManager.desiredAccuracy = kCLLocationAccuracyBest
        locationManager.requestAlwaysAuthorization()
        locationManager.allowsBackgroundLocationUpdates = true
        locationManager.startMonitoringSignificantLocationChanges()
    }

这是崩溃日志

Crashed: com.apple.main-thread
0                          0x10285b798 specialized AppDelegate.updatebackloc(Double, long : Double) -> () + 4339644312
1                          0x10285b8e4 specialized AppDelegate.locationManager(CLLocationManager, didUpdateLocations : [CLLocation]) -> () (AppDelegate.swift:396)
2                          0x1028540c0 @objc AppDelegate.locationManager(CLLocationManager, didUpdateLocations : [CLLocation]) -> () (AppDelegate.swift)
3  CoreLocation                   0x1874f97bc (null) + 77412
4  CoreLocation                   0x1874f901c (null) + 75460
5  CoreLocation                   0x1874e16b4 (null) + 1004
6  CoreFoundation                 0x180ea3590 __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ + 20
7  CoreFoundation                 0x180ea2e60 __CFRunLoopDoBlocks + 288
8  CoreFoundation                 0x180ea10c8 __CFRunLoopRun + 2436
9  CoreFoundation                 0x180dc0c58 CFRunLoopRunSpecific + 436
10 GraphicsServices               0x182c6cf84 GSEventRunModal + 100
11 UIKit                          0x18a5195c4 UIApplicationMain + 236
12                         0x1027fe524 main (InboxInterests.swift:30)
13 libdyld.dylib                  0x1808e056c start + 4

这里是代码

编码为文本

func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {

        if let lat = manager.location?.coordinate.latitude,
            let long = manager.location?.coordinate.longitude {
            glat = String(lat)
            glong = String(long)

            if UIApplication.shared.applicationState == .background {
            self.updatebackloc(lat, long: long)
            }

            if UIApplication.shared.applicationState == .active {
            self.updateloc(lat, long: long)
                locationManager.stopUpdatingLocation()
                let status = CLLocationManager.authorizationStatus()
                if (status == CLAuthorizationStatus.authorizedAlways) {
                locationManager.startMonitoringSignificantLocationChanges()
                }
            }
        }
        }

        func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
            print(error)
        }


        func updateloc(_ lat: CLLocationDegrees, long: CLLocationDegrees) {
            let userID = TegKeychain.get("userID")!
            let parameters: Parameters = ["userID": userID, "lat": lat, "long":long]
            Alamofire.request("https://xxxxx.com/ios/updateloc.php", method: .post, parameters: parameters).validate().responseJSON { response in
                switch response.result {
                case .success:
                    if let json = response.result.value {
                        var success = 0
                        if let dictJSON = json as? [String: AnyObject] {
                            if let successInteger = dictJSON["success"] as? Int {
                                success = successInteger
                                if success == 1
                                {

                                }
                            }
                        }
                    }
                case .failure(_):
                    return
                }
            }
        }
    func updatebackloc(_ lat: CLLocationDegrees, long: CLLocationDegrees) {
        guard let userID = TegKeychain.get("userID") else {
            return
        }
        let parameters: Parameters = ["userID": userID, "lat": lat, "long":long]
        Alamofire.request("https://xxxxx.com/ios/updatebackloc.php", method: .post, parameters: parameters).validate().responseJSON { response in
            switch response.result {
            case .success:
                if let json = response.result.value {
                    var success = 0
                    if let dictJSON = json as? [String: AnyObject] {
                        if let successInteger = dictJSON["success"] as? Int {
                            success = successInteger
                            if success == 1
                            {

                            }
                        }
                    }
                }
            case .failure(_):
                return
            }
        }
    }

【问题讨论】:

  • 你能定位崩溃发生的位置吗?哪个语句抛出异常?
  • 抛出异常的代码行会有帮助。
  • 只有锁定设备才会崩溃?
  • @shallowThought 它在后台崩溃
  • "AppDelegate.swift:436" 您发布的代码中的第 436 行是哪一行?

标签: ios swift crash cllocationmanager


【解决方案1】:

我高度怀疑您正试图强制解开这一行中的 nil 值:

let userID = TegKeychain.get("userID")!

要弄清楚我是否正确,请尝试这是否解决了崩溃问题(但显然仍不能满足您的要求):

guard let userID = TegKeychain.get("userID") else {
    return
}

假设TegKeychain.get("userID") 正在尝试从系统 KeyChain 获取密钥的值,该值很可能已被写入 KeyChain 且具有不合适的可访问性。因此,您不能在后台访问它并返回nil

要解决此问题,请在将凭据保存到 KeyChain 时为密钥 kSecAttrAccessible 设置一个适合您需要的值。

kSecAttrAccessibleAfterFirstUnlock 是 Apple 推荐的,可能适合您的需求。

在代码中:

let userIdToSave = "secretString"
guard let secretAsData = userIdToSave.data(using: String.Encoding.utf8) else {
    return
}
let keyChainKey = "userID"
let query = [
    kSecClass as String: kSecClassGenericPassword as String,
    kSecAttrAccessible as String: kSecAttrAccessibleAfterFirstUnlock as String,
    kSecAttrService as String: yourService,
    kSecAttrAccount as String: keyChainKey,
    kSecValueData as String: secretAsData] as [String : Any]

SecItemDelete(query as CFDictionary)

let status = SecItemAdd(query as CFDictionary, nil)

详情请查看Apples Documentation


编辑:

根据您在 cmets 中的陈述,我的猜测是错误的。

我的下一个猜测是操作系统正在网络进程中杀死你。您可以通过向操作系统询问更多时间来避免这种情况。

为此,请启动一个后台任务。

在看起来像这样的代码中:

class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?
    var bgTask = UIBackgroundTaskInvalid

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        if let _ = launchOptions?[UIApplicationLaunchOptionsKey.location] {
            // app is launched in backgrond due to location change
            bgTask = UIApplication.shared.beginBackgroundTask(expirationHandler: { // Start background task
                UIApplication.shared.endBackgroundTask(bgTask)
                bgTask = UIBackgroundTaskInvalid
            })
            startSignificationLocation()
        }
        return true
    }

    // ... your other methods

    func updatebackloc(_ lat: CLLocationDegrees, long: CLLocationDegrees) {
        guard let userID = TegKeychain.get("userID") else {
            return
        }
        let parameters: Parameters = ["userID": userID, "lat": lat, "long":long]
        Alamofire.request("https://xxxxx.com/ios/updatebackloc.php", method: .post, parameters: parameters).validate().responseJSON { response in
            defer {
                // Tell the OS that you are done
                UIApplication.shared.endBackgroundTask(bgTask)
                bgTask = UIBackgroundTaskInvalid
            }
            switch response.result {
            case .success:
                if let json = response.result.value {
                    var success = 0
                    if let dictJSON = json as? [String: AnyObject] {
                        if let successInteger = dictJSON["success"] as? Int {
                            success = successInteger
                            if success == 1
                            {

                            }
                        }
                    }
                }
            case .failure(_):
                return
            }
        }
    }
}

也不要忘记设置所需的能力,如其他答案中提到的那样:

您还应该进行更多调试。阅读this 了解如何在 Xcode 中模拟运动。

【讨论】:

  • kSecAttrService 应该是什么?
  • 我还是有问题
  • 我们需要更多信息。我的理解:使用guard 语句不会崩溃。换句话说,解开nil 是问题的根源。请确认。
  • 使用保护语句也崩溃了
  • 如果是这样,请注意:如果给定 userID 的值之前已写入具有不合适的可访问性,则必须使用 SecItemUpdate 而不是 SecItemAdd 来设置 @987654339 @ 为给定的UserId
【解决方案2】:

我想您需要使用后台配置配置 Alamofire 会话管理器才能在后台执行请求。然后为了使请求使用正确配置的会话管理器。

例如

let configuration = URLSessionConfiguration.background(withIdentifier: "com.example.app.background")
let sessionManager = Alamofire.SessionManager(configuration: configuration)

【讨论】:

  • 谢谢。我会试试这个。
【解决方案3】:

您需要将后台位置调用封装在 UIBackgroundTask 中。

var bgTask: UIBackgroundTaskIdentifier?
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: "bgLocation", expirationHandler: {
        if let task = self.bgTask {
            application.endBackgroundTask(task)
            self.bgTask = UIBackgroundTaskInvalid
        }
    })
}

然后在调用 updatebackloc 函数时使用正确的后台队列并结束后台任务。

类似:

func updatebackloc(_ lat: CLLocationDegrees, long: CLLocationDegrees) {
    let userID = TegKeychain.get("userID")!
    let parameters: Parameters = ["userID": userID, "lat": lat, "long":long]
    DispatchQueue.global().async {
        Alamofire.request("https://xxxxx.com/ios/updatebackloc.php", method: .post, parameters: parameters).validate().responseJSON { response in

            //response handling here...
            //....
            if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
                if let task = appDelegate.bgTask {
                    UIApplication.shared.endBackgroundTask(task)
                    appDelegate.bgTask = UIBackgroundTaskInvalid
                }
            }
        }
    }
}

【讨论】:

  • 当我尝试使用 application.endBackgroundTask(task) 代码结束 bgtask 时,出现对​​成员 'application(_:didFinishLaunchingWithOptions:)' 的不明确引用
  • @UtkuDalmaz 您是从 AppDelegate 外部调用的,对吗?查看我更新的代码
  • applicationDidEnterBackground 是启动任务的错误位置。它将在 3 分钟后被杀死。重要的LocationChages 可以在以后的任何时间唤醒您的应用。
【解决方案4】:

您是否为后台模式启用了定位服务?如果是,那么当您进入后台时尝试重新初始化位置管理器,它实际上对我有用,但最佳做法是在用户激活时将坐标保存在数据库中,将未同步的记录同步到服务器。

【讨论】:

  • 是的,我已经启用了
猜你喜欢
  • 1970-01-01
  • 2019-09-04
  • 2014-05-09
  • 1970-01-01
  • 1970-01-01
  • 2012-10-27
  • 1970-01-01
  • 2017-02-13
  • 1970-01-01
相关资源
最近更新 更多