应用已经消失,iOS iBeacon /蓝牙连接

我需要的:

一种可预测,可靠且可靠的启动iBeacon委托方法的方法,例如didDetermineState,didRangeBeacons,didEnterRegion或didExitRegion,当应用程序死机且设备已插入并附近时.

目前的情况

我正在为父母制作一个应用程序,让他们的孩子帮助他们在重要时间关闭手机.该应用程序位于Objective-C中,即使在应用程序生命周期之后,它仍需要与蓝牙设备保持持久连接.

我已经尝试了很长时间才能使这个工作,我得到了很多S.O.的帮助.海报,目前我知道我必须在我的设备中使用iBeacon从终止启动(这是我使用它的唯一原因,如果有其他方式从终止启动应用程序,我很乐意转储它).为了澄清,我在同一个设备(我已经建立)iBeacon和一个可靠的BT连接这里需要2件事.我需要此设备连接配对,因为这是从BT设备发送/接收命令的唯一方法.我发现的是,在后台触发的didRange或didEnter委托方法充其量是不可靠的.他们并不总是立即开火,他们只开了几次​​而整个事情都死了(我现在知道这个10秒窗口是终止应用程序的预期行为).我甚至整天都在插拔电源,不断寻找应用程序恢复生机的任何迹象……

当应用程序打开时,工作正常,但是当应用程序在我的灯塔/蓝牙附近时,我希望它在应用程序内部启动一种临时锁定屏幕.当应用程序处于前台时,我已经相当好地完成了这一部分.如果一个孩子试图关闭应用程序或后台,我想通过让我的BT设备一旦终止就启动到后台进行响应(我知道用户界面不会出现,而且没关系,我只需要一系列功能即可开启) .然后它将连接到蓝牙并从设备接收一些命令.听起来很简单呃?事情变得混乱了.

一些上下文:我在info.plist中为蓝牙和信标添加了所有后台模式,当应用程序处于前台时,一切正常……

如果在范围内检测到iBeacon,那么我想使用该10秒窗口通过BT配对连接到我的盒子并通过命令发送.到目前为止,这是不可思议的…当应用程序终止时,iBeacon测距函数不会触发它们只会触发最奇怪的用例.我似乎无法预测它们什么时候开火.

我的代码

ibeaconManager.h

@interface IbeaconManager : NSObject

@property (nonatomic) BOOL waitingForDeviceCommand;
@property (nonatomic,strong) NSTimer *deviceCommandTimer;

+ (IbeaconManager *) sharedInstance;
- (void)startMonitoring;
- (void)stopMonitoring;
- (void)timedLock:(NSTimer *)timer;

@end

ibeaconManager.m

@interface IbeaconManager () <CLLocationManagerDelegate>

@property (nonatomic,strong) BluetoothMgr *btManager;
@property (nonatomic,strong) CLLocationManager *locationManager;
@property (nonatomic,strong) CLBeaconRegion *region;
@property (nonatomic) BOOL connectedToDevice;

@end

NSString *const PROXMITY_UUID = @"00000000-1111-2222-3333-AAAAAAAAAAAA";
NSString *const BEACON_REGION = @"MY_CUSTOM_REGION";

const int REGION_MINOR = 0;
const int REGION_MAJOR = 0;



@implementation IbeaconManager
+ (IbeaconManager *) sharedInstance {
    static IbeaconManager *_sharedInstance = nil;
    static dispatch_once_t oncePredicate;

    dispatch_once(&oncePredicate,^{
        _sharedInstance = [[IbeaconManager alloc] init];
    });

    return _sharedInstance;

}


- (id)init {
    self = [super init];

    if(self) {
        self.locationManager = [[CLLocationManager alloc] init];
        self.locationManager.delegate = self;
        [self.locationManager requestAlwaysAuthorization];
        self.connectedToDevice = NO;
        self.waitingForDeviceCommand = NO;

        self.region = [[CLBeaconRegion alloc] initWithProximityUUID:[[NSUUID alloc] initWithUUIDString:PROXMITY_UUID]
                                                              major:REGION_MAJOR
                                                              minor:REGION_MINOR
                                                         identifier:BEACON_REGION];

        self.region.notifyEntryStateOnDisplay = YES;
        self.region.notifyOnEntry = YES;
        self.region.notifyOnExit = YES;
    }

    return self;
}


- (void)startMonitoring {
    if(self.region != nil) {
        NSLog(@"**** started monitoring with beacon region **** : %@",self.region);

        [self.locationManager startMonitoringForRegion:self.region];
        [self.locationManager startRangingBeaconsInRegion:self.region];
    }
}


- (void)stopMonitoring {
    NSLog(@"*** stopMonitoring");

    if(self.region != nil) {
        [self.locationManager stopMonitoringForRegion:self.region];
        [self.locationManager stopRangingBeaconsInRegion:self.region];
    }
}


- (void)triggerCustomLocalNotification:(NSString *)alertBody {
    UILocalNotification *localNotification = [[UILocalNotification alloc] init];
    localNotification.alertBody = alertBody;
    [[UIApplication sharedApplication] presentLocalNotificationNow:localNotification];
}




#pragma mark - CLLocationManager delegate methods

- (void)locationManager:(CLLocationManager *)manager
      didDetermineState:(CLRegionState)state
              forRegion:(CLRegion *)region {

    NSLog(@"did determine state STATE: %ld",(long)state);
    NSLog(@"did determine state region: %@",region);

    [self triggerCustomLocalNotification:@"made it into the did determine state method"];

    NSUInteger appState = [[UIApplication sharedApplication] applicationState];
    NSLog(@"application's current state: %ld",(long)appState);

    if(appState == UIApplicationStateBackground || appState == UIApplicationStateInactive) {
        NSString *notificationText = @"Did range beacons... The app is";
        NSString *notificationStateText = (appState == UIApplicationStateInactive) ? @"inactive" : @"backgrounded";
        NSString *notificationString = [NSString stringWithFormat:@"%@ %@",notificationText,notificationStateText];

        NSUserDefaults *userDefaults = [[NSUserDefaults alloc] init];
        bool isAppLockScreenShowing = [userDefaults boolForKey:@"isAppLockScreenShowing"];

        if(!isAppLockScreenShowing && !self.waitingForDeviceCommand) {
            self.waitingForDeviceCommand = YES;

            self.deviceCommandTimer = [NSTimer scheduledTimerWithTimeInterval:2.0
                                                                       target:self
                                                                     selector:@selector(timedLock:)
                                                                     userInfo:notificationString
                                                                      repeats:NO];
        }

    } else if(appState == UIApplicationStateActive) {

        if(region != nil) {
            if(state == CLRegionStateInside) {
                NSLog(@"locationManager didDetermineState INSIDE for %@",region.identifier);
                [self triggerCustomLocalNotification:@"locationManager didDetermineState INSIDE"];

            } else if(state == CLRegionStateOutside) {
                NSLog(@"locationManager didDetermineState OUTSIDE for %@",region.identifier);
                [self triggerCustomLocalNotification:@"locationManager didDetermineState OUTSIDE"];

            } else {
                NSLog(@"locationManager didDetermineState OTHER for %@",region.identifier);
            }
        }

        //Upon re-entry,remove timer
        if(self.deviceCommandTimer != nil) {
            [self.deviceCommandTimer invalidate];
            self.deviceCommandTimer = nil;
        }
    }
}


- (void)locationManager:(CLLocationManager *)manager
        didRangeBeacons:(NSArray *)beacons
               inRegion:(CLBeaconRegion *)region {

    NSLog(@"Did range some beacons");

    NSUInteger state = [[UIApplication sharedApplication] applicationState];
    NSString *notificationStateText = (state == UIApplicationStateInactive) ? @"inactive" : @"backgrounded";
    NSLog(@"application's current state: %ld",(long)state);

    [self triggerCustomLocalNotification:[NSString stringWithFormat:@"ranged beacons,application's current state: %@",notificationStateText]];

    if(state == UIApplicationStateBackground || state == UIApplicationStateInactive) {
        NSString *notificationText = @"Did range beacons... The app is";
        NSString *notificationString = [NSString stringWithFormat:@"%@ %@",notificationStateText];

        NSUserDefaults *userDefaults = [[NSUserDefaults alloc] init];
        bool isAppLockScreenShowing = [userDefaults boolForKey:@"isAppLockScreenShowing"];

        if(!isAppLockScreenShowing && !self.waitingForDeviceCommand) {
            self.waitingForDeviceCommand = YES;

            self.deviceCommandTimer = [NSTimer scheduledTimerWithTimeInterval:2.0
                                                                       target:self
                                                                     selector:@selector(timedLock:)
                                                                     userInfo:notificationString
                                                                      repeats:NO];
        }

    } else if(state == UIApplicationStateActive) {
        if(self.deviceCommandTimer != nil) {
            [self.deviceCommandTimer invalidate];
            self.deviceCommandTimer = nil;
        }
    }
}


- (void)timedLock:(NSTimer *)timer {
    self.btManager = [BluetoothMgr sharedInstance];

    [self.btManager sendCodeToBTDevice:@"magiccommand"
                        characteristic:self.btManager.lockCharacteristic];

    [self triggerCustomLocalNotification:[timer userInfo]];

    self.waitingForDeviceCommand = NO;
}


- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region {
    NSLog(@"Did Enter Region: %@",region);
    [self triggerCustomLocalNotification:[NSString stringWithFormat:@"Did enter region: %@",region.identifier]];
}


- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region {
    NSLog(@"Did Exit Region: %@",region);
    [self triggerCustomLocalNotification:[NSString stringWithFormat:@"Did exit region: %@",region.identifier]];

    //Upon exit,remove timer
    if(self.deviceCommandTimer != nil) {
        [self.deviceCommandTimer invalidate];
        self.deviceCommandTimer = nil;
    }
}


- (void)locationManager:(CLLocationManager *)manager monitoringDidFailForRegion:(CLRegion *)region withError:(NSError *)error {
    NSLog(@"monitoringDidFailForRegion EPIC FAIL for region %@ withError %@",region.identifier,error.localizedDescription);
}




@end

解决方法

我为iOS构建了一个类似的系统,它使用iBeacon传输在后台唤醒,然后连接到蓝牙LE以交换数据.请放心,这一切都是可能的,让工作变得棘手并且调试起来比较棘手.

关于蓝牙LE连接的一些提示:

>当应用程序被杀死时,信标测距功能不会触发,除非您还监视信标并获得didEnter或didExit转换,这将按照您的描述将应用程序重新启动到后台10秒.同样,只有当您从区域转移到区域外或反之亦然时,才会发生这种情况.这很难测试,因为您可能没有意识到CoreLocation在您杀死应用程序时认为您处于“区域内”,但您不会获得用于检测信标的唤醒事件.
>为了在后台获取蓝牙事件,您需要确保您的Info.plist声明:

<key>UIBackgroundModes</key>
<array>
   <string>bluetooth-central</string>
</array>

如果不存在,你绝对不会在后台获得对didDiscoverPeripheral的回调.>当您的应用程序启动时,您需要开始扫描蓝牙,并在收到func centralManager的回调时进行连接(_ central:CBCentralManager,didDiscover peripheral:CBPeripheral,advertisementData:[String:Any],rssi RSSI:NSNumber)>从上面保存外围设备实例的副本,因为您只能在后台获得一个回调,以便从每个独特的蓝牙设备中进行发现.如果连接失败,则可以使用相同的外围对象实例重试.>为了调试从kill状态重新启动,我添加了许多NSLog语句(我添加了在代码中打开和关闭它们的能力),然后在XCode的Windows中查找这些 – >设备 – >我的iPhone面板,您可以在其中展开屏幕底部的小箭头,以显示设备上所有应用的日志.如果从被杀死状态重新启动,您绝对会在此处看到针对您的应用的日志.

以上是来客网为你收集整理的应用已经消失,iOS iBeacon /蓝牙连接全部内容,希望文章能够帮你解决应用已经消失,iOS iBeacon /蓝牙连接所遇到的程序开发问题。

如果觉得来客网网站内容还不错,欢迎将来客网网站推荐给程序员好友。