iOS iBeacon/Bluetooth连接,当应用程序停止运行时

iOS iBeacon/Bluetooth连接,当应用程序停止运行时,ios,objective-c,bluetooth,ibeacon,background-service,Ios,Objective C,Bluetooth,Ibeacon,Background Service,我需要什么: 这是一种可预测、可靠的iBeacon委托方法启动方式,如当应用程序死机且设备已插入并靠近时,didDetermineState、didRangeBeacons、didEnterRegion或didExitRegion 当前形势 我正在为父母制作一个应用程序,让他们的孩子在重要的时候关闭手机。应用程序处于Objective-C中,即使在应用程序使用寿命结束后,它也需要保持与蓝牙设备的持久连接 我已经尝试了很长一段时间来实现这一点,我得到了很多S.O.海报的帮助,目前我知道我必须在我的

我需要什么:

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

当前形势

我正在为父母制作一个应用程序,让他们的孩子在重要的时候关闭手机。应用程序处于Objective-C中,即使在应用程序使用寿命结束后,它也需要保持与蓝牙设备的持久连接

我已经尝试了很长一段时间来实现这一点,我得到了很多S.O.海报的帮助,目前我知道我必须在我的设备中使用iBeacon从终止启动(这是我使用它的唯一原因,如果有其他方法从终止启动应用程序,我会很乐意将其转储)。为了澄清这一点,我需要在同一个设备(我已经构建了)中安装两件东西:iBeacon和可靠的BT连接。我需要此设备连接配对,因为这是从BT设备发送/接收命令的唯一方法。我发现,在后台启动的
didRange
didEnter
委托方法最多是不可靠的。它们并不总是立即启动,只启动了几次,整个过程就结束了(我现在知道这个10秒的窗口是一个终止的应用程序的预期行为)。我甚至有整整一天的时间不停地插上/拔下它,寻找任何迹象表明该应用程序已经恢复了活力,但什么都没有发生

当应用程序打开时,一切正常,但是当应用程序靠近我的信标/蓝牙时,我希望它在应用程序内启动一种临时锁定屏幕。当应用程序处于前台时,我已经做得相当好了。如果一个孩子试图关闭应用程序或后台,我想在它终止后让我的BT设备在后台启动(我知道UI不会出现,没关系,我只需要启动一系列功能)。然后,它将连接到蓝牙并从设备接收一些命令。听起来很简单吧?这就是事情变得一团糟的原因

一些背景:我在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:@"%@ %@", 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(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
@interface IbeaconManager()
@属性(非原子、强)BluetoothMgr*btManager;
@属性(非原子,强)CLLocationManager*locationManager;
@性质(非原子,强)CLBeaconRegion*区域;
@属性(非原子)布尔连接设备;
@结束
NSString*const PROXMITY_UUID=@“00000000-1111-2222-3333-aaaaaaaa”;
NSString*const BEACON_REGION=@“我的自定义_REGION”;
const int REGION_MINOR=0;
const int REGION_MAJOR=0;
@实现IbeaconManager
+(IbeaconManager*)共享状态{
静态IbeaconManager*_sharedInstance=nil;
静态调度一次预测;
一次发送(一次预测)^{
_sharedInstance=[[IbeaconManager alloc]init];
});
返回-共享状态;
}
-(id)init{
self=[super init];
如果(自我){
self.locationManager=[[CLLocationManager alloc]init];
self.locationManager.delegate=self;
[self.locationManager请求始终授权];
self.connectedToDevice=否;
self.waitingForDeviceCommand=否;
self.region=[[CLBeaconRegion alloc]initwithproximityuid:[[nsuid alloc]initwithuuids字符串:PROXMITY\u UUID]
专业:地区大学专业
小调:小调区域
标识符:BEACON_区域];
self.region.notifyEntryStateondDisplay=是;
self.region.notifyOnEntry=是;
self.region.notifyOnExit=是;
}
回归自我;
}
-(无效)启动监控{
if(self.region!=nil){
NSLog(@“****已开始监视信标区域****:%@”,self.region);
[self.locationManager startMonitoringForRegion:self.region];
[self.locationManager startrangbeaconregion:self.region];
}
}
-(d)停止监测{
NSLog(@“***停止监测”);
if(self.region!=nil){
[self.locationManager停止监视区域:self.region];
[self.locationManager stoprangingbeaconregion:self.region];
}
}
-(void)triggerCustomLocalNotification:(NSString*)alertBody{
UILocalNotification*localNotification=[[UILocalNotification alloc]init];
localNotification.alertBody=alertBody;
[[UIApplication sharedApplication]presentLocalNotificationNow:localNotification];
}
#pragma标记-CLLocationManager委托方法
-(无效)locationManager:(CLLocationManager*)经理
房地产:(CLRegionState)州
forRegion:(CLRegion*)区域{
NSLog(@“未确定状态:%ld”,(长)状态);
NSLog(@“未确定状态区域:%@”,区域);
[self-triggerCustomLocalNotification:@“已将其放入did确定状态方法”];
NSUPINTEGER appState=[[UIApplication sharedApplication]applicationState];
NSLog(@“应用程序的当前状态:%ld”,(长)appState);
如果(appState==UIApplicationStateBackground | appState==UIApplicationStateInactive){
NSString*notificationText=@“Did范围信标…应用程序为”;
NSString*notificationStateText=(应用程序
<key>UIBackgroundModes</key>
<array>
   <string>bluetooth-central</string>
</array>