我想在iPad應用程序中使用iBeacon功能來充當信標發射器和iphone應用程序來接收信標。我是能夠建立相應的這兩個應用,但現在我遇到了一些奇怪的問題:本地通知只收到一次(iBeacons)
iBeacon顯示變送器 iPad應用程序作爲信標信號的發射器。我實施了一個操作表來選擇我想要傳輸的信標ID。這是該代碼:
#import "BeaconAdvertisingService.h"
@import CoreBluetooth;
NSString *const kBeaconIdentifier = @"identifier";
@interface BeaconAdvertisingService() <CBPeripheralManagerDelegate>
@property (nonatomic, readwrite, getter = isAdvertising) BOOL advertising;
@end
@implementation BeaconAdvertisingService {
CBPeripheralManager *_peripheralManager;
}
+ (BeaconAdvertisingService *)sharedInstance {
static BeaconAdvertisingService *sharedInstance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
- (instancetype)init {
self = [super init];
if (!self) {
return nil;
}
_peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)];
return self;
}
- (BOOL)bluetoothStateValid:(NSError **)error {
BOOL bluetoothStateValid = YES;
switch (_peripheralManager.state) {
case CBPeripheralManagerStatePoweredOff:
if (error != NULL) {
*error = [NSError errorWithDomain:@"identifier.bluetoothState"
code:CBPeripheralManagerStatePoweredOff
userInfo:@{@"message": @"You must turn Bluetooth on in order to use the beacon feature"}];
}
bluetoothStateValid = NO;
break;
case CBPeripheralManagerStateResetting:
if (error != NULL) {
*error = [NSError errorWithDomain:@"identifier.bluetoothState"
code:CBPeripheralManagerStateResetting
userInfo:@{@"message" : @"Bluetooth is not available at this time, please try again in a moment."}];
}
bluetoothStateValid = NO;
break;
case CBPeripheralManagerStateUnauthorized:
if (error != NULL) {
*error = [NSError errorWithDomain:@"identifier.bluetoothState"
code:CBPeripheralManagerStateUnauthorized
userInfo:@{@"message": @"This application is not authorized to use Bluetooth, verify your settings or check with your device's administration"}];
}
bluetoothStateValid = NO;
break;
case CBPeripheralManagerStateUnknown:
if (error != NULL) {
*error = [NSError errorWithDomain:@"identifier.bluetoothState"
code:CBPeripheralManagerStateUnknown
userInfo:@{@"message": @"Bluetooth is not available at this time, please try again in a moment"}];
}
bluetoothStateValid = NO;
break;
case CBPeripheralManagerStateUnsupported:
if (error != NULL) {
*error = [NSError errorWithDomain:@"identifier.blueetoothState"
code:CBPeripheralManagerStateUnsupported
userInfo:@{@"message": @"Your device does not support bluetooth. You will not be able to use the beacon feature"}];
}
bluetoothStateValid = NO;
break;
case CBPeripheralManagerStatePoweredOn:
bluetoothStateValid = YES;
break;
}
return bluetoothStateValid;
}
- (void)startAdvertisingUUID:(NSUUID *)uuid major:(CLBeaconMajorValue)major minor:(CLBeaconMinorValue)minor {
NSError *bluetoothStateError = nil;
if (![self bluetoothStateValid:&bluetoothStateError]) {
NSString *title = @"Bluetooth Issue";
NSString *message = bluetoothStateError.userInfo[@"message"];
[[[UIAlertView alloc] initWithTitle:title
message:message
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil] show];
return;
}
CLBeaconRegion *region;
if (uuid && major && minor) {
region = [[CLBeaconRegion alloc] initWithProximityUUID:uuid major:major minor:minor identifier:kBeaconIdentifier];
} else if (uuid && major) {
region = [[CLBeaconRegion alloc] initWithProximityUUID:uuid major:major identifier:kBeaconIdentifier];
} else if (uuid) {
region = [[CLBeaconRegion alloc] initWithProximityUUID:uuid identifier:kBeaconIdentifier];
} else {
[NSException raise:@"You must at least provide a UUID to start advertising" format:nil];
}
NSDictionary *peripheralData = [region peripheralDataWithMeasuredPower:nil];
[_peripheralManager startAdvertising:peripheralData];
}
- (void)stopAdvertising {
[_peripheralManager stopAdvertising];
self.advertising = NO;
}
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral {
NSError *bluetoothStateError = nil;
if (![self bluetoothStateValid: &bluetoothStateError]) {
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertView *bluetoothIssueAlert = [[UIAlertView alloc] initWithTitle:@"Bluetooth Problem"
message:bluetoothStateError.userInfo[@"message"]
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[bluetoothIssueAlert show];
});
}
}
- (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(NSError *)error {
dispatch_async(dispatch_get_main_queue(), ^{
if (error) {
[[[UIAlertView alloc] initWithTitle:@"Cannot Advertise Beacon"
message:@"There was an issue starting the advertisement of the beacon"
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil] show];
} else {
NSLog(@"Advertising");
self.advertising = YES;
}
});
}
據我所看到的,發射工作完全正常......
我想接收到的信號響應ID應該拋出一個本地iPhone應用程序收到ID後立即通知。這在第一次運行時效果很好。我可以選擇ipad動作表中的3個信標中的每一個,將這個通知發送到iphone上。但是當我重新選擇第一個燈塔時,例如什麼也沒有發生。爲了應用程序的目的,應用程序每次收到信標時都會響應。我設置iphone代碼如下:
#import "BeaconMonitoringService.h"
#import "LocationManagerService.h"
@implementation BeaconMonitoringService {
CLLocationManager *_locationManager;
}
+ (BeaconMonitoringService *)sharedInstance {
static dispatch_once_t onceToken;
static BeaconMonitoringService *_sharedInstance;
dispatch_once(&onceToken, ^{
_sharedInstance = [[self alloc] init];
});
return _sharedInstance;
}
- (instancetype)init {
self = [super init];
if (!self) {
return nil;
}
_locationManager = [[LocationManagerService sharedInstance] getLocationManager];
return self;
}
- (void)startMonitoringBeaconWithUUID:(NSUUID *)uuid major:(CLBeaconMajorValue)major minor:(CLBeaconMinorValue)minor identifier:(NSString *)identifier onEntry:(BOOL)entry onExit:(BOOL)exit {
CLBeaconRegion *region = [[CLBeaconRegion alloc] initWithProximityUUID:uuid major:major minor:minor identifier:identifier];
region.notifyOnEntry = entry;
region.notifyOnExit = exit;
region.notifyEntryStateOnDisplay = YES;
[_locationManager startMonitoringForRegion:region];
}
- (void)stopMonitoringAllRegions {
for (CLRegion *region in _locationManager.monitoredRegions) {
[_locationManager stopMonitoringForRegion:region];
}
}
@end
位置管理器相應地拋出它的委託調用,並由我在locationmanagerservice中實現。
- (void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region {
if ([region isKindOfClass:[CLBeaconRegion class]]) {
CLBeaconRegion *beaconRegion = (CLBeaconRegion *)region;
Beacon *beacon = [[BeaconDetailService sharedService] beaconWithUUID:beaconRegion.proximityUUID];
if (beacon) {
NSDictionary *userInfo = @{@"beacon": beacon, @"state": @(state)};
[[NSNotificationCenter defaultCenter] postNotificationName:@"DidDetermineRegionState" object:self userInfo:userInfo];
}
NSLog(@"Call DidDetermine");
}
}
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region {
dispatch_async(dispatch_get_main_queue(), ^{
if ([region isKindOfClass:[CLBeaconRegion class]]) {
CLBeaconRegion *beaconRegion = (CLBeaconRegion *)region;
Beacon *beacon = [[BeaconDetailService sharedService] beaconWithUUID:beaconRegion.proximityUUID];
if (beacon) {
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.userInfo = @{@"uuid": beacon.uuid.UUIDString};
notification.alertBody = [NSString stringWithFormat:@"Test Beacon %@", beacon.name];
notification.soundName = @"Default";
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
[[NSNotificationCenter defaultCenter] postNotificationName:@"DidEnterRegion" object:self userInfo:@{@"beacon": beacon}];
NSLog(@"Call DidEnter");
}
}
});
}
- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region {
dispatch_async(dispatch_get_main_queue(), ^{
if ([region isKindOfClass:[CLBeaconRegion class]]) {
CLBeaconRegion *beaconRegion = (CLBeaconRegion *)region;
Beacon *beacon = [[BeaconDetailService sharedService] beaconWithUUID:beaconRegion.proximityUUID];
if (beacon) {
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.alertBody = [NSString stringWithFormat:@"Test %@", beacon.name];
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
[[NSNotificationCenter defaultCenter] postNotificationName:@"DidExitRegion" object:self userInfo:@{@"beacon": beacon}];
NSLog(@"Call DidExit");
}
}
});
}
當我登錄的委託方法我收到下列流程的呼叫:
1)DidDetermineState被稱爲 2)DidEnterRegion被稱爲 3),但沒有DidExitRegion之後調用。
也是我不斷收到此錯誤: 「PBRequester失敗,錯誤錯誤域= NSURLErrorDomain代碼= -1003‘與指定的主機服務器無法找到’的UserInfo = 0x166875e0 {NSErrorFailingURLStringKey = https://gsp10-ssl.apple.com/use,NSErrorFailingURLKey = https://gsp10-ssl.apple.com/use, NSLocalizedDescription =找不到指定主機名的服務器,NSUnderlyingError = 0x1656a9b0「找不到指定主機名的服務器」}「
這似乎很奇怪..有沒有一種方法可以完成每次我在我的iPad上的操作表中選擇一個燈塔時,都會收到本地筆記?
有趣的是,當我離開燈塔時,我選擇打開,我的iphone不斷拋出本地音符,而不用改變它們之間的任何東西。突然之間DidExitRegion被稱爲和DidEnterRegion之後,再次...
謝謝你!
嗨大衛,以及我正在不斷傳輸他們,據我所知。當我點擊我的iPad應用程序上的「停止」按鈕時,我會調用stopMonitoringAllRegions方法,正如您在代碼中看到的那樣。 如何強制iOS鬆開信標的跟蹤,以便下次再次選擇信標時我有一個「新鮮開始」?有趣的是,DidEnterRegion和DidDetermineState只在我第一次選擇ipad上的beacon時被調用。第二次我選擇它時,屏幕上沒有再次記錄任何內容..所以代理不會再被調用。 – sesc360
感謝您的編輯,這些幫助。不幸的是,我認爲沒有辦法「強制」iOS開始新鮮事。 iBeacons的跟蹤在操作系統級別進行處理,應用程序不允許操作此跟蹤。調用stopMonitoringAllRegions方法不會影響操作系統級別跟蹤的狀態。您只需等待足夠的時間(至少4分鐘,如果在後臺),然後重新開始傳輸。 – davidgyoung
但是,如果我們以購物中心爲例,您可能會有數百個信標不斷傳輸信號,並且您將有一位用戶漫步,他會收到當地的筆記,然後轉身走開回來......那麼他將無法再接收他們呢? – sesc360