本文介绍了本地通知仅收到一次(iBeacons)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在iPad App中使用iBeacon功能来充当信标发送器,并使用iphone App来接收信标.我能够相应地构建两个应用程序,但是现在遇到了一些奇怪的问题:

I would like to use the iBeacon Feature in an iPad App to act as a beacon transmitter and an iphone App to receive the beacons. I was able to build both apps accordingly but now I ran into some strange problems:

iBeacon发射器iPad应用程序充当信标信号的发送器.我实现了一个操作表,用于选择要发送的信标ID.这是该代码:

iBeacon Transmitterthe iPad app acts as the transmitter of the beacon signal. I implemented an action sheet for selecting a beacon ID I would like to transmit. This is the code for that:

#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;
        }
    });
}

据我所见,传输工作正常……

As far as I can see, the transmitting works perfectly fine...

我想响应收到的信号ID的iPhone应用程序应在收到ID后立即引发本地通知.在第一次运行时,这工作得很好.我可以在ipad操作表中选择3个信标中的每一个,以将该通知发送到iphone上.但是,例如,当我重新选择第一个信标时,什么也没有发生.就应用程序而言,至关重要的是,应用程序每次接收到信标时都要做出响应.我将iPhone代码设置如下:

The iphone App I would like to respond to the received signal ID should throw an local notification as soon as it received the ID. This works perfectly fine on the first run. I can select every of the 3 beacons in the ipad action sheet to throw this notification on the iphone. But when I reselect the first beacon for example nothing happens any more. For the purpose of the app it would be crucial the app responds every time it receives the beacon. I setup the iphone code as follows:

#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中实现.

The location manager throws its delegate calls accordingly and is implemented by me in a 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");
            }
        }
    });
}

当我记录委托方法的调用时,我会收到以下方案:

When I am logging the call of the delegate methods I receive the following scheme:

1)DidDetermineState被调用2)DidEnterRegion被调用3),但此后没有调用DidExitRegion.

1) DidDetermineState is being called2) DidEnterRegion is being called3) but no DidExitRegion is called after that.

我也反复收到此错误:"PBRequester失败,错误错误域= NSURLErrorDomain代码= -1003"找不到具有指定主机名的服务器."UserInfo = 0x166875e0 {NSErrorFailingURLStringKey = https://gsp10-ssl.apple.com/use ,NSErrorFailingURLKey = https://gsp10-ssl.apple.com/use ,NSLocalizedDescription =找不到具有指定主机名的服务器.NSUnderlyingError = 0x1656a9b0找不到具有指定主机名的服务器." }"

also I repeatedly receive this error:"PBRequester failed with Error Error Domain=NSURLErrorDomain Code=-1003 "A server with the specified hostname could not be found." UserInfo=0x166875e0 {NSErrorFailingURLStringKey=https://gsp10-ssl.apple.com/use, NSErrorFailingURLKey=https://gsp10-ssl.apple.com/use, NSLocalizedDescription=A server with the specified hostname could not be found., NSUnderlyingError=0x1656a9b0 "A server with the specified hostname could not be found."}"

这似乎很奇怪.每当我在ipad的操作表中选择一个信标时,有什么方法可以完成以接收本地音符吗?

this seems very strange.. is there a way I can accomplish to receive the local note every time I select a beacon in the action sheet on my ipad?

很有意思的是,当我打开选定的Beacon时,iphone会不时地抛出本地音符,而我之间没有进行任何更改.突然之间,再次调用了DidExitRegion,然后再次调用了DidEnterRegion ...

Interesting enough, when I leave the Beacon I selected turned on, my iphone keeps throwing up the local notes from time to time without me changing anything in between. And suddenly the DidExitRegion is being called and DidEnterRegion after that again...

谢谢!

推荐答案

在不知道自己在做什么的情况下,很难确切地说出正在发生的事情.您是在连续传输3个UUID的广告,还是只是传输一次然后停止?您的意思是即使我删除了已发送的旧邮件,也应该以干净的状态开始"?这是否意味着停止对这些UUID的测距然后重新启动?一些代码片段可能会有所帮助.

It is hard to say exactly what is going on without knowing a bit more what you are doing. Are you continually transmitting the advertisements for the 3 UUIDs, or just transmitting each once then stopping? What do you mean by "Even if I delete the sent old ones and should start with a clean slate"? does this mean stopping ranging on those UUIDs then restarting? Some code snippets might help.

要知道的一件重要事情是,即使没有监视处于活动状态,iOS也会继续跟踪其看到的每个我的信标的状态.这意味着,如果在开始之前 已通过iOS检测到iBeacons A和B,则不会收到didEnterRegion通知.同样,如果在收到此类通知后停止监视并重新启动监视,则在iOS认为iBeacon消失并重新出现之前,您也不会收到新的didEnterRegion通知.

An important thing to know is that iOS continues to track the state of each I beacon it sees even if no monitoring is active. This means that if iBeacons A and B have been detected by iOS before you start monitoring for them, you will get no didEnterRegion notification. Similarly, if after getting such a notification you stop monitoring and restart monitoring, you also won't get a new didEnterRegion notification until iOS thinks the iBeacon disappeared and reappeared.

您确定这种完全过渡正在发生吗?尝试在didEnterRegion和didExitRegion回调中添加NSLog语句,以帮助查看事件触发的时间戳.

Are you sure this full transition is happening? Try adding NSLog statements in the didEnterRegion and didExitRegion callbacks to help see the timestamps of when the events are firing.

重要的是要了解,如果没有活动的iBeacon监视器或范围的应用程序处于前台,则iOS可能需要很长时间才能检测到状态更改.我已经看到,每个状态转换最多需要4分钟.如果要启动和停止变送器,请尝试等待至少8分钟,然后再重新启动,并通过日志验证您是否已获得第二次通知所需的两次转换.

It is also important to understand that iOS can take a long time to detect state changes if no app with active iBeacon monitors or ranges is in the foreground. I have seen it take up to 4 minutes for each state transition. If you are starting and stopping your transmitter, try waiting at least 8 minutes before restarting and and verify via logs that you then get the two transitions needed for a second notification.

这篇关于本地通知仅收到一次(iBeacons)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-24 01:09