我想在iPad应用程序中使用iBeacon功能作为信标发射器和iphone应用程序来接收信标。我能够相应地构建这两个应用程序,但现在我遇到了一些奇怪的问题:
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;
}
});
}
据我所知,传输效果非常好......
iphone App我想响应收到的信号ID,一旦收到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 = A server找不到指定的主机名。,NSUnderlyingError = 0x1656a9b0“无法找到具有指定主机名的服务器。”}“
这看起来很奇怪..每次我在ipad上的操作表中选择信标时,有什么办法可以接收本地音符吗?
有趣的是,当我离开我选择的Beacon时,我的iphone不时地不断地抛出本地音符,而我之间没有任何改变。然后突然再次调用DidExitRegion和DidEnterRegion ......
谢谢!!答案 0 :(得分:2)
如果不了解你正在做的事情,很难准确地说出发生了什么。你是在不断传输3个UUID的广告,还是只传输一次然后停止?你是什么意思“即使我删除了已发送的旧版本并且应该从一个干净的平板开始”?这是否意味着停止那些UUID然后重新启动?一些代码片段可能有所帮助。
重要的是要知道iOS即使没有监控处于活动状态,它也会继续跟踪它看到的每个信标的状态。这意味着如果在之前已经通过iOS 检测到iBeacons A和B,则开始监视它们,您将不会收到didEnterRegion通知。同样,如果在收到此类通知后停止监控并重新启动监控,则在iOS认为iBeacon消失并重新出现之前,您也不会收到新的didEnterRegion通知。
你确定这种完全过渡正在发生吗?尝试在didEnterRegion和didExitRegion回调中添加NSLog语句,以帮助查看事件触发时的时间戳。
了解如果没有具有活动iBeacon监视器或范围的应用程序位于前台,iOS可能需要很长时间来检测状态更改。我看到每次州过渡最多需要4分钟。如果您正在启动和停止发射器,请尝试至少等待8分钟再重新启动,然后通过日志验证您是否获得了第二次通知所需的两次转换。