CLLocationManager 区域监控委托 class 问题
CLLocationManager region monitoring delegate class issue
我已经围绕 CoreLocation 实现了自定义 class 来进行 iBeacon 区域监控。
这个 class 有一些自定义属性,我用它们来存储一些与信标相关的信息供以后使用(在进入和退出事件期间)。
我面临的问题是当应用程序终止或保留在后台时,这些存储的属性不再可用。我的意思是,假设该应用程序在 background/terminated 中发现了一个信标区域,该应用程序将像往常一样在后台启动以供我们处理。我想在那段时间使用存储的属性进行自定义操作。
以前有人遇到过这个问题吗?我做错了吗?此外,我正在使用我目前正在使用的 cocoapod 库中的 class。
下面是我写的class
@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)
}
}
我最终发现存储的属性没有我在启动区域监控时设置的值。
非常感谢任何帮助。
此致。
正如@Paulw11 所说,这些属性在应用程序重新启动时丢失了它们的初始化值。处理此问题的典型方法是将这些属性存储到 NSUserDefaults
中。下面的代码片段显示了如何恢复 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")
}
以上示例仅显示保存和恢复您的单个属性。您需要对所有这些都执行此操作,有些处理起来会更复杂(例如 monitoredRegions
和 repository
),因为它们是复杂的数据类型,无法直接序列化为 NSUserDefaults
。要进行此序列化,您可以尝试使用 JSON 将它们转换为可以存储的字符串,然后从同一个字符串中解析出它们。
我已经围绕 CoreLocation 实现了自定义 class 来进行 iBeacon 区域监控。
这个 class 有一些自定义属性,我用它们来存储一些与信标相关的信息供以后使用(在进入和退出事件期间)。
我面临的问题是当应用程序终止或保留在后台时,这些存储的属性不再可用。我的意思是,假设该应用程序在 background/terminated 中发现了一个信标区域,该应用程序将像往常一样在后台启动以供我们处理。我想在那段时间使用存储的属性进行自定义操作。
以前有人遇到过这个问题吗?我做错了吗?此外,我正在使用我目前正在使用的 cocoapod 库中的 class。
下面是我写的class
@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)
}
}
我最终发现存储的属性没有我在启动区域监控时设置的值。
非常感谢任何帮助。
此致。
正如@Paulw11 所说,这些属性在应用程序重新启动时丢失了它们的初始化值。处理此问题的典型方法是将这些属性存储到 NSUserDefaults
中。下面的代码片段显示了如何恢复 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")
}
以上示例仅显示保存和恢复您的单个属性。您需要对所有这些都执行此操作,有些处理起来会更复杂(例如 monitoredRegions
和 repository
),因为它们是复杂的数据类型,无法直接序列化为 NSUserDefaults
。要进行此序列化,您可以尝试使用 JSON 将它们转换为可以存储的字符串,然后从同一个字符串中解析出它们。