CLLocationManager 区域监控委托类问题

Posted

技术标签:

【中文标题】CLLocationManager 区域监控委托类问题【英文标题】:CLLocationManager region monitoring delegate class issue 【发布时间】:2015-09-29 19:42:33 【问题描述】:

我已经围绕 CoreLocation 实现了一个自定义类来进行 iBeacon 区域监控。

这个类有一些自定义属性,我用来存储一些与信标相关的信息以供以后使用(在进入和退出事件期间)。

我面临的问题是当应用程序终止或保留在后台时,这些存储的属性不再可用。我的意思是,假设应用程序在后台/终止时发现了一个信标区域,像往常一样,应用程序将在后台启动以供我们处理。我想在那段时间使用存储的属性进行自定义操作。

以前有人遇到过这个问题吗?我这样做是错误的吗?另外,我正在使用我目前正在工作的 cocoapod 库中的此类。

下面是我写的类。

 @available(ios 9.0, *)
class BeaconManager: NSObject, CLLocationManagerDelegate 
   //these properties are becoming nil
   private var manager: CLLocationManager
   private var lastDetection: NSDate?
   private var isMonitoring = false
   private var repository: [String: DBeacon]
   private var monitoredRegions: [String: DBeacon] becoming nil
   private var notifyBackground = true

   static let sharedManager = BeaconManager()
   weak var delegate:BeaconProtocol?

   private override init() 
    manager = CLLocationManager()
    repository =  [:]
    monitoredRegions = [:]

    if CLLocationManager.authorizationStatus() != .AuthorizedAlways 
        manager.requestAlwaysAuthorization()
    

    super.init()
    manager.delegate = self


func startMonitoringForBeacon(beacon: Beacon) throws 
    guard CLLocationManager.locationServicesEnabled() else 
        CFLogger.ERROR("Location services not enabled")
        throw BeaconErrorDomain.AuthorizationError(msg: "Location services not enabled")
    

    guard CLLocationManager.authorizationStatus() == .AuthorizedAlways else 
        switch CLLocationManager.authorizationStatus() 
        case .Denied:
            throw BeaconErrorDomain.AuthorizationError(msg: "User denied location services")
        case .Restricted:
            throw BeaconErrorDomain.AuthorizationError(msg: "App is prevented from accessing Location Services")
        default:
            throw BeaconErrorDomain.AuthorizationError(msg: "App doesn't have authorization to monitor regions")
        
    

    guard CLLocationManager.isMonitoringAvailableForClass(CLBeaconRegion) else 
        CFLogger.ERROR("Region monitoring not available on this device")
        throw DBeaconKitErrorDomain.RegionMonitoringError(msg: "Region monitoring not available on this device")
    


    guard let auuid = NSUUID(UUIDString: beacon.uuid) else 
        throw BeaconErrorDomain.InvalidUUIDString
    

    let region:CLBeaconRegion!

    switch (beacon.major, beacon.minor) 
        case (.None, .None):
            region = CLBeaconRegion(proximityUUID: auuid, identifier: dbeacon.identifier)
        case (.Some(let major), .None):
            region = CLBeaconRegion(proximityUUID: auuid, major: UInt16(major), identifier: beacon.identifier)
        case (.Some(let major), .Some(let minor)):
            region = CLBeaconRegion(proximityUUID: auuid, major: UInt16(major), minor: UInt16(minor), identifier: beacon.identifier)
        default:
            throw BeaconErrorDomain.InvalidDBeaconInfo
    

    region.notifyEntryStateOnDisplay = false
    region.notifyOnEntry = true
    region.notifyOnExit = true

    repository[beacon.identifier] = beacon
    manager.startMonitoringForRegion(region)


func stopMonitoringForBeacons(beacons: [Beacon]) 
    guard isMonitoring else 
        return
    

    beacons.forEach  (dbeacon) -> () in
        stopMonitoringForBeacon(beacon)
    


func locationManager(manager: CLLocationManager, didFailWithError error: NSError) 
    guard let handler = delegate else 
        return
    

    handler.initializationFailed(error)


func locationManager(manager: CLLocationManager, didStartMonitoringForRegion region: CLRegion) 
    guard let aregion = region as? CLBeaconRegion, beacon = repository[aregion.identifier] else 
        return
    

    isMonitoring = true

    monitoredRegions[aregion.identifier] = beacon
    manager.requestStateForRegion(region)


func locationManager(manager: CLLocationManager, monitoringDidFailForRegion region: CLRegion?, withError error: NSError) 
    guard let aregion = region as? CLBeaconRegion, beacon = repository[aregion.identifier], handler = delegate else 
        return
    

    handler.monitoringFailedForRegion(beacon, error: error)


func locationManager(manager: CLLocationManager, didEnterRegion region: CLRegion) 
    guard let aregion = region as? CLBeaconRegion else 
        return
    

    guard let beacon = monitoredRegions[aregion.identifier] else 
        return
    

    guard let handler = delegate else 
     print("Handler not available to report beacon entry event \(region.identifier)")
        return
    

    print("Entered beacon region \(beacon)")
    handler.entered(beacon)


func locationManager(manager: CLLocationManager, didExitRegion region: CLRegion) 
    guard let aregion = region as? CLBeaconRegion, beacon = monitoredRegions[aregion.identifier], handler = delegate else 
        print("Handler not available to report beacon exit event \(region.identifier)")
        return
    

    print("Exited beacon region \(beacon)")
    handler.exited(beacon)

 

我最终发现存储的属性没有我在启动区域监控时设置的值。

非常感谢任何帮助。

问候。

【问题讨论】:

您的应用可能会在后台重新启动以响应信标区域事件。在这种情况下,您有责任从某个持久存储中恢复您需要的任何其他状态信息;当您的应用程序被卸载并释放内存时,您只是保存在内存中的任何信息都将消失。 谢谢!!会试试的。 【参考方案1】:

正如@Paulw11 所说,属性在应用重新启动时会丢失其初始化值。处理此问题的典型方法是将这些属性存储到NSUserDefaults。下面的 sn-p 显示了如何恢复 init 方法底部的 lastDetection 字段。必须调用称为 save() 的第二个方法以在更改后保留该字段。

private override init() 
    manager = CLLocationManager()
    repository =  [:]
    monitoredRegions = [:]

    if CLLocationManager.authorizationStatus() != .AuthorizedAlways 
        manager.requestAlwaysAuthorization()
    

    super.init()
    manager.delegate = self

    let userDefaults = NSUserDefaults.standardUserDefaults()    
    lastDetection = userDefaults.valueForKey("last_detection") as! NSDate?



func save() 
    let userDefaults = NSUserDefaults.standardUserDefaults()    
    userDefaults.setValue(lastDetection, forKey: "last_detection")

上面的示例仅显示了保存和恢复您的单个属性。您需要对所有这些都执行此操作,有些处理起来会更复杂(如monitoredRegionsrepository),因为它们是无法直接序列化为NSUserDefaults 的复杂数据类型。要执行此序列化,您可以尝试使用 JSON 将它们转换为可以存储的字符串,然后从同一字符串中解析出来。

【讨论】:

谢谢大卫。这就是我什至正在做的事情。但我仍然很好奇 SwiftLocation 等一些 cocoapods 框架是如何做到这一点的。我没有看到他们在执行保存/恢复状态任务。

以上是关于CLLocationManager 区域监控委托类问题的主要内容,如果未能解决你的问题,请参考以下文章

iOS CLLocationManager - 手动进行区域监控

处理来自另一个类的 CLLocationManager 委托

CLLocationManager委托方法未被调用

在 iOS 11 和 Xcode 9.1 中未调用 CLLocationManager 的 didUpdateLocations 委托 [重复]

为啥飞行模式开启时 CLLocationManager 不会失败?

核心位置区域监控