问题描述
我开发了一款使用 iBeacon + BLE(核心蓝牙)技术执行门解锁操作的应用程序。我的需求是这样的
-
用户登录后,Location Manager 和 Central Manager 被初始化
func initLocationManager() { DDLogInfo("ACBLEManager > initLocationManager") locationManager = CLLocationManager() locationManager?.delegate = self locationManager?.requestAlwaysAuthorization() }
func initBLEManager() {
DDLogInfo("ACBLEManager > initBLEManager")
var dic : [String : Any] = Dictionary()
dic[CBCentralManagerOptionShowPowerAlertKey] = false
bleCentralManager = CBCentralManager(delegate: self,queue: nil,options: dic)
}
- 针对预定义 UUID 的 iBeacon 测距和监控开始如下
func startScanningiBeacon() {
guard let iBeaconUUID = BTUUIDs.iBeaconUUID else {
return
}
if #available(iOS 13.0,*) {
self.beaconRegion = CLBeaconRegion(uuid: iBeaconUUID,identifier: beaconRegionIdentifier)
} else {
self.beaconRegion = CLBeaconRegion(proximityUUID: iBeaconUUID,identifier: beaconRegionIdentifier)
}
if let region = self.beaconRegion {
region.notifyOnExit = true
region.notifyOnEntry = true
locationManager?.startMonitoring(for: region)
locationManager?.startRangingBeacons(in: region)
}
}
这个,开始为信标测距,我可以在 didRangeBeacon 中使用检测到的信标获得回调
- 一旦检测到信标,我就会得到它的主要和次要值。基于此,我准备了一个动态服务 UUID 并开始扫描 BLE Peripheral,如下所示
var computedDoorID: String = "" {
willSet(newValue) {
if !(newValue.isEmpty) {
computedServiceUUID = CBUUID(string: "\(newValue)-xxxx-xxxx-xxxx-xxxxxxxxxxxx")
dispatchQueue.global().async { [weak self] in
self?.bleCentralManager.scanForperipherals(withServices: [newValue],options: [CBCentralManagerScanoptionAllowDuplicatesKey: false])
}
}
}
}
当检测到任何 BLE 外设时,这会在下面的委托方法中给我回调。
func centralManager(_ central: CBCentralManager,diddiscover peripheral: CBPeripheral,advertisementData: [String : Any],RSSi RSSI: NSNumber) {
DDLogInfo("ACBLEManager > CentralManager diddiscover peripheral > Peripheral name: \(peripheral.name ?? "") > Device Service UUID: \(advertisementData)")
if seenDevices[peripheral.identifier] == nil {
if let deviceids = advertisementData[CBAdvertisementDataServiceUUIDsKey] as? [AnyObject],deviceids.count > 0 {
DDLogInfo("ACBLEManager > centralManager diddiscover peripheral > deviceids :\(deviceids)")
let deviceidString = (deviceids.first as? CBUUID)!.uuidString
if (RSSI.doubleValue < 0) {
DDLogInfo("ACBLEManager > discovered peripheral's > deviceidString :\(deviceidString)")
let device = BTDevice(peripheral: peripheral,manager: bleCentralManager,deviceid: deviceidString)
if isBackgroundMode {
device.latestRSSI = RSSI.doubleValue
}
seenDevices[peripheral.identifier] = device
DDLogInfo("ACBLEManager > centralManager diddiscover peripheral > Array of Seen Devices :\(seenDevices)")
delegate?.diddiscover(device: device)
} else {
DDLogError("BLUetoOTH RSSI: \(RSSI.doubleValue) is > 0 so ignoring it.")
}
}
} else {
if (RSSI.doubleValue < 0) {
DDLogInfo("ACBLEManager > Same BLE Peripheral discovered again \(peripheral) with RSSi: \(RSSI)")
let device = seenDevices[peripheral.identifier]
if isBackgroundMode {
device?.latestRSSI = RSSI.doubleValue
}
seenDevices[peripheral.identifier] = device
delegate?.diddiscover(device: device!)
} else {
DDLogError("BLUetoOTH RSSI for Same Peripheral : \(RSSI.doubleValue) is > 0 so ignoring it.")
}
}
}
当位置经理的代表
locationManager(_ manager: CLLocationManager,didRangeBeacons beacons: [CLBeacon],in region: CLBeaconRegion) function updates the RSSI value (every second)
接收信标更新,我在我的视图控制器中使用最新的 RSSI 值传递它。根据这个 RSSI 值,决定用户是靠近门还是远离。一旦RSSI值大于预定义值(例如-50),则认为用户较近并开始BLE连接并执行开门操作。
这对于前景模式非常有效。现在为了让它在后台模式(或用户终止模式)下运行,我启用了区域监控并延长了后台执行时间。这样,当用户进入信标区域时,应用程序可以激活一段时间,用户可以执行开门操作。
func applicationDidEnterBackground(_ application: UIApplication) {
DDLogInfo("∏∏∏ > AppDelegate > applicationDidEnterBackground > ∏∏∏")
if userDefaultManager.bool(forKey: UserDefaultKey.isUserLoggedIn) {
ACBLEManager.sharedInstance.extendBackgroundRunningTime()
ACBLEManager.sharedInstance.startBeaconRanging()
ACBLEManager.sharedInstance.isBackgroundMode = true
} else {
DDLogError("AppDelegate > applicationDidEnterBackground > User is logged out. So don't perform anything")
}
}
func startBeaconRangingAndMonitoring() {
if let region = self.beaconRegion {
locationManager?.startRangingBeacons(in: region)
locationManager?.startMonitoring(for: region)
}
}
func extendBackgroundRunningTime() {
if (self.backgroundTask != .invalid) {
// if we are in here,that means the background task is already running.
// don't restart it.
return
}
self.backgroundTask = UIApplication.shared.beginBackgroundTask(withName: "DummyTask",expirationHandler: {
print("Background time Expire Handler")
self.isBackgroundMode = true
UIApplication.shared.endBackgroundTask(self.backgroundTask)
if let region = self.beaconRegion {
self.locationManager?.startMonitoring(for: region)
}
self.backgroundTask = .invalid
})
if threadStarted {
print("Background task thread already started.")
}
else {
threadStarted = true
dispatchQueue.global(qos: .default).async {
while (true) {
// A dummy tasks must be running otherwise iOS suspends immediately
Thread.sleep(forTimeInterval: 1);
}
}
}
}
当我通过位置管理器的 didEnter 或 didExit 区域方法进入或离开区域时,这会通知我。
func locationManager(_ manager: CLLocationManager,didEnterRegion region: CLRegion) {
showlocalnotification(title: "Welcome to the Beacon Region!",body: "")
DDLogInfo("LocationManager > DidEnterRegion : \(region)")
manager.allowsBackgroundLocationUpdates = true
manager.startMonitoringSignificantLocationChanges()
manager.startRangingBeacons(in: region as! CLBeaconRegion)
if (self.isBackgroundMode) {
self.extendBackgroundRunningTime()
}
}
func locationManager(_ manager: CLLocationManager,didExitRegion region: CLRegion) {
showlocalnotification(title: "Ohh NO!",body: "You're out of Beacon Region!")
DDLogInfo("LocationManager > DidExitRegion : \(region)")
DDLogInfo("Manager Monitored regions: \(manager.monitoredRegions) region.notifyOnExit : \(region.notifyOnExit),region.notifyOnEntry: \(region.notifyOnEntry)")
guard region is CLBeaconRegion else { return }
manager.startMonitoring(for: region)
manager.startRangingBeacons(in: region as! CLBeaconRegion)
}
但是,这第一次工作正常,即当我的应用程序第一次打开时>移到后台>用户终止它(从应用程序切换器中删除它)>超出信标范围>我按预期收到didExitRegion
现在,当我进入信标范围时 > 我收到了 didEnterRegion 并且信标测距开始,然后 BLE 扫描恢复,即使我的应用程序没有运行,我也可以执行开门操作。
现在,我再次离开信标区域(我的应用程序仍未打开),我从未收到 didExitRegion 通知,并且它一直在后台运行。我可以看到打印了很多信标测距日志,并且当我在信标范围之外时不会收到回调。
但是,当我再次进入范围时,它会通知我并且 BLE 扫描恢复!
请帮我找到这个场景的解决方案(关于应用永不停止以及为什么 didExitRegion 没有被调用)
我怀疑应用可能会在 appStore 审核过程中被拒绝,因为它永远不会停止,以及为什么我没有收到关于 didExitRegion 的回电?
注意:
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)