我正在设计一个iBeacon设备,以便在iPhone上使用应用程序。该问题与识别信标的应用程序存在非常奇怪的不一致。我主要从这份文件中了解到信标:https://developer.apple.com/ibeacon/Getting-Started-with-iBeacon.pdf 这是我的系统的简短介绍:
设备: - 按下设备上的按钮会以10Hz触发iBeacon BLE广告约两秒钟。 - 设备按顺序循环显示20个UUID,用于按下每个不同的按钮。这是为了在应用程序没有时间“超出范围”时快速按下按钮时区分按钮按下。 20 UUID是Apple允许应用程序监控的限制。
该应用程序: - 该应用程序搜索20个UUID中的任何一个。 - 检测到后,进入和退出事件会触发一些应用程序事件,并在其短暂分配的背景时间内简要使用范围来提取Maj.Min。对价值。
测试: 为了进行测试,应用程序设置为在发生输入,范围和退出事件时显示弹出窗口。应用程序在前台进行测试。我按下按钮,观察输入和范围事件,并等到它退出该区域后再按下。使用BLE扫描仪进行测试表明该设备运行良好,按下按钮100%正常播放,正确循环播放20 UUID。
问题: 在应用内测试中,问题出现并遵循一种奇怪的模式。该应用程序可以识别前9个左右的按钮/ UUID。之后,通常在按下第10个按钮时,应用程序停止响应信标。没有弹出窗口。在我的BLE扫描仪上,我可以清楚地看到该设备正在正常播放。当设备循环回到第一个UUID时,设备通常会再次开始识别信标。我说“通常”因为它有时会在第11个UUID停止,有时会在第20个UUID上再次开始,有时我甚至会随机看到第12个UUID,但是通常会有这个差距或“死区”用于下半部分。 UUID的。如果我快速按下按钮,我可以让它工作。弹出窗口会快速进入,并显示UUID的骑行一直到20,所以我知道UUID是好的,应用程序是有能力的。按下按钮之间的时间超过一分钟左右就存在问题。 我可以做的另一个奇怪的事情是将按钮混合,成功看到应用程序弹出窗口,然后当我处于UUID'死区'范围10-20时停止,比如列表中的UUID 14。如果我等待一分钟然后再次按下,则在按下7个按钮后再次到达第一个UUID后它将无法工作。 其他说明: - 我不总是看到区域退出弹出窗口,有时它们很快退出,有时需要更长时间。 - 模式并不严格一致。它有时会有所不同有时它会工作一个完整的周期,但它通常属于这种间隙模式。 - 我正在测试iOS 11和10上的两部手机。
思路: 奇怪的是,它几乎总是希望在第一个UUID上再次开始工作。所以这让我觉得这不是一个绝对的时间问题,否则差距会及时存在,而不是UUID列表。 很奇怪它在快速循环时有效。
Apple是否将其他UID列入黑名单,因为它认为我是垃圾邮件UUID,或扫描太快了? 这是一个我不了解的更深入的iOS BLE缓存问题吗? 这是某种iOS BLE后台扫描调度程序的时间问题吗? 这听起来像应用程序代码中的错误吗?
感谢您的帮助或想法!
这是我的信标事件的应用代码 -
func locationManager(_ manager:CLLocationManager,didRangeBeacons beacons:[CLBeacon],in region:CLBeaconRegion){
let knownBeacons = beacons.filter{ $0.proximity != CLProximity.unknown }
for closestBeacon in knownBeacons
{
let closestBeaconFound = closestBeacon as CLBeacon
var beaconProximity = ""
switch (closestBeaconFound.proximity)
{
case CLProximity.unknown: beaconProximity = "Unknown"
case CLProximity.far: beaconProximity = "Far"
case CLProximity.near: beaconProximity = "Near"
case CLProximity.immediate: beaconProximity = "Immediate"
}
if inDidRangeShowPopup == false{
HelperClass.showAlert(value: "identifier and proximityUUID in did Range: \(region.identifier) \(region.proximityUUID)")
inDidRangeShowPopup = true
let when = DispatchTime.now() + 5 // change 2 to desired number of seconds
DispatchQueue.main.asyncAfter(deadline: when) {
inDidRangeShowPopup = false
}
}
let status = "Ranging"
let uuid = String(describing: region.proximityUUID)
let major = closestBeaconFound.major as! Int
let minor = closestBeaconFound.minor as! Int
var pressType = ""
print("closestBeacon",closestBeaconFound.proximityUUID, closestBeaconFound.major,closestBeaconFound.minor,closestBeaconFound.proximity.rawValue, beaconProximity, closestBeaconFound.accuracy,closestBeaconFound.rssi)
let majorHex = String( closestBeaconFound.major as! Int, radix : 2)
print("Hex Major : \(majorHex)")
let paddedBits = HelperClass.paddedBinaryString(with: majorHex)
print("paddedBits : \(paddedBits)")
let emergencyCode = paddedBits.substring(to: 8).asBinaryInt()
let deviceID = paddedBits.substring(from: 8).asBinaryInt()
if emergencyCode == 0
{
if iBeaconOption1Done == false
{
iBeaconOption1Done = true
let when = DispatchTime.now() + 10 // change 2 to desired number of seconds
DispatchQueue.main.asyncAfter(deadline: when) {
iBeaconOption1Done = false
}
for addressStr in HelperClass.getMyMacAddres()
{
let address = addressStr as! String
if address == String(deviceID!)
{
pressType = "option1"
self.setDeviceStatusAndSetTimer(mac_address: address , deviceId : deviceID!, status: status, uuid: uuid, major: major, minor: minor, pressType: pressType)
if HelperClass.getAppFlagValueForAttribute (attribute : "isHomeViewed") == true {
self.option1Service(flag : "option1", deviceId: deviceID!)
}
else{
self.option1Service(flag : "test_option1", deviceId: deviceID!)
}
}
}
}
}
else if emergencyCode == 1
{
print("Call option2")
for addressStr in HelperClass.getMyMacAddres()
{
let address = addressStr as! String
if address == String(deviceID!)
{
pressType = "Option2"
self.setDeviceStatusAndSetTimer(mac_address: address , deviceId : deviceID!, status: status, uuid: uuid, major: major, minor: minor, pressType: pressType)
}
}
if HelperClass.getAppFlagValueForAttribute (attribute : "isHomeViewed") == true {
self.sendOption2 (flag : "device" ,deviceId : deviceID!)
}
}
else if emergencyCode == 2
{
print("Call Advertisement")
if iBeaconStatusSent == false
{
iBeaconStatusSent = true
let when = DispatchTime.now() + 5 // change 2 to desired number of seconds
DispatchQueue.main.asyncAfter(deadline: when) {
iBeaconStatusSent = false
}
for addressStr in HelperClass.getMyMacAddres()
{
let address = addressStr as! String
if address == String(deviceID!)
{
pressType = "Check in"
self.setDeviceStatusAndSetTimer(mac_address: address , deviceId : deviceID!, status: status, uuid: uuid, major: major, minor: minor, pressType: pressType)
}
}
}
}
}
}
func getDefaultBeacons() - > [CLBeaconRegion] {
return [
CLBeaconRegion(proximityUUID: UUID(uuidString: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")!, identifier: "Fl\(BeaconIdentifierIntValue+3)"),
CLBeaconRegion(proximityUUID: UUID(uuidString: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")!, identifier: "Fl\(BeaconIdentifierIntValue+4)"),
CLBeaconRegion(proximityUUID: UUID(uuidString: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")!, identifier: "Fl\(BeaconIdentifierIntValue+5)"),
CLBeaconRegion(proximityUUID: UUID(uuidString: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")!, identifier: "Fl\(BeaconIdentifierIntValue+6)"),
CLBeaconRegion(proximityUUID: UUID(uuidString: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")!, identifier: "Fl\(BeaconIdentifierIntValue+7)"),
CLBeaconRegion(proximityUUID: UUID(uuidString: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")!, identifier: "Fl\(BeaconIdentifierIntValue+8)"),
CLBeaconRegion(proximityUUID: UUID(uuidString: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")!, identifier: "Fl\(BeaconIdentifierIntValue+9)"),
CLBeaconRegion(proximityUUID: UUID(uuidString: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")!, identifier: "Fl\(BeaconIdentifierIntValue+10)"),
CLBeaconRegion(proximityUUID: UUID(uuidString: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")!, identifier: "Fl\(BeaconIdentifierIntValue+11)"),
CLBeaconRegion(proximityUUID: UUID(uuidString: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")!, identifier: "Fl\(BeaconIdentifierIntValue+12)"),
CLBeaconRegion(proximityUUID: UUID(uuidString: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")!, identifier: "Fl\(BeaconIdentifierIntValue+13)"),
CLBeaconRegion(proximityUUID: UUID(uuidString: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")!, identifier: "Fl\(BeaconIdentifierIntValue+14)"),
CLBeaconRegion(proximityUUID: UUID(uuidString: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")!, identifier: "Fl\(BeaconIdentifierIntValue+15)"),
CLBeaconRegion(proximityUUID: UUID(uuidString: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")!, identifier: "Fl\(BeaconIdentifierIntValue+16)"),
CLBeaconRegion(proximityUUID: UUID(uuidString: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")!, identifier: "Fl\(BeaconIdentifierIntValue+17)"),
CLBeaconRegion(proximityUUID: UUID(uuidString: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")!, identifier: "Fl\(BeaconIdentifierIntValue+18)"),
CLBeaconRegion(proximityUUID: UUID(uuidString: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")!, identifier: "Fl\(BeaconIdentifierIntValue+19)"),
CLBeaconRegion(proximityUUID: UUID(uuidString: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")!, identifier: "Fl\(BeaconIdentifierIntValue+20)"),
CLBeaconRegion(proximityUUID: UUID(uuidString: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")!, identifier: "Fl\(BeaconIdentifierIntValue+21)"),
CLBeaconRegion(proximityUUID: UUID(uuidString: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")!, identifier: "Fl\(BeaconIdentifierIntValue+22)")
]
}
func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
print("iBeacon delegate:didEnterRegion called")
DispatchQueue.main.async() {
if let beaconRegion = region as? CLBeaconRegion {
print("identifier and proximityUUID in didEnterRegion: \(beaconRegion.identifier) \(beaconRegion.proximityUUID)")
HelperClass.showAlert(value: "identifier and proximityUUID in didEnterRegion: \(beaconRegion.identifier) \(beaconRegion.proximityUUID)")
}
else{
print("Wrong Format in didEnterRegion")
Answers.logCustomEvent(withName: "didEnterRegion", customAttributes: [
"error_text": "CLBeaconRegion Object Not Created",
"app_version" : AppVersion,
"app_build" : AppBuild
])
}
}
}
func locationManager(_ manager:CLLocationManager,didExitRegion region:CLRegion){
print("iBeacon delegate:didExitRegion called")
DispatchQueue.main.async() {
if let beaconRegion = region as? CLBeaconRegion {
print("identifier and proximityUUID in didExitRegion: \(beaconRegion.identifier) \(beaconRegion.proximityUUID)")
HelperClass.showAlert(value: "identifier and proximityUUID in didExitRegion: \(beaconRegion.identifier) \(beaconRegion.proximityUUID)")
}
else{
print("Wrong Format in didExitRegion")
Answers.logCustomEvent(withName: "didExitRegion", customAttributes: [
"error_text": "CLBeaconRegion Object Not Created",
"app_version" : AppVersion,
"app_build" : AppBuild
])
}
}
}
答案 0 :(得分:1)
我怀疑代码在注册所有20个区域之前达到了20个区域限制。这可能发生,因为CoreLocation从应用程序的早期运行中缓存以前注册的区域,因此可能还有其他预先存在的来自早期版本代码的区域。
您可以通过使用kCLErrorRegionMonitoringFailure错误代码查找locationManager:monitoringDidFailForRegion:withError:
的回调来查看是否发生这种情况。
如果情况确实如此,您可以通过调用locationManager.monitoredRegions
清除已注册的区域,然后迭代结果并为每个区域调用locationManager.stopMonitoring(region: region)
。但请注意,每次运行代码时都不应该执行此清除过程,因为尽早使用操作系统注册区域具有检测加速优势,并始终取消注册和重新注册你的地区会让你走到后面。
答案 1 :(得分:0)
@davidgyoug你的答案很有灵感!好像什么都解决了!最初你建议的代码不起作用,但是你提到已注册的区域让我意识到我们用来监视信标的另一个但完全不同的测试应用程序。它已注册到相同的Apple ID,开发人员和测试航班帐户。删除该应用程序,重新启动手机,并清除locationManager缓存,似乎解决了它!另一个提示是,它在我们今天测试的其他iPhone上运行良好,这些iPhone从未成为开发的一部分。
这是否意味着每个Apple ID / Developer帐户只允许20个监控区域,而不是严格按应用程序?监控区域的手机上的其他第三方应用程序是否也会干扰应用程序?
感谢您的帮助!