I set up my CLLocationManager as follows
self.locationManager.delegate = self;
if ([CLLocationManager isMonitoringAvailableForClass:[CLBeaconRegion class]]) {
[self.locationManager startMonitoringForRegion:self.region];
}
else {
NSLog(#"CLBeaconRegion monitoring not available");
}
if ([CLLocationManager isRangingAvailable]) {
[self.locationManager startRangingBeaconsInRegion:self.region];
}
else {
NSLog(#"CLBeaconRegion ranging not available");
}
locationManager:didEnterRegion: and locationManager:didRangeBeacons:inRegion: are never called. I am using iOS 7.1 and have the issue on both an iPad and and iPhone. I am using Estimote beacons.
Restarting the iOS device completely resolved the issue. Not sure if this is a bug in iOS or with the Estimote beacons. But it seems restarting is required. That immediately resolved the issues and I have had no issues since.
Related
I'm building an app on iOS 10 using the Estimote SDK. I want to use the range mode to detect nearby beacons. The UUID for the region and beacons is setup correctly as verified in a small sample project.
The app I'm building right now is showing some strange behaviour: After starting the app, the beaconManager:didRangeBeacons:inRegion: method is not called even when right next to the beacon.
Disabling / enabling Bluetooth will cause the method to fire immediately. This is also true for pausing the app and resuming it using the debugger.
What is causing this behaviour? I'm requesting the permission at every launch and wait for the callback to start monitoring (as stated in the documentation). I already tried to setup more startRanging / stopRanging calls (desperate!) but no success.
Any ideas?
#implementation Model {}
- (instancetype)init {
self = [super init];
if (self) {
self.beaconManager = [ESTBeaconManager new];
self.beaconManager.delegate = self;
self.beaconRegion = [[CLBeaconRegion alloc]
initWithProximityUUID:[[NSUUID alloc]
initWithUUIDString:proximityUUID]
identifier:#"Playground"];
[self.beaconManager requestWhenInUseAuthorization];
self.beaconSignal = [self rac_signalForSelector:#selector(beaconManager:didRangeBeacons:inRegion:) fromProtocol:#protocol(ESTBeaconManagerDelegate)];
[[self.beaconSignal throttle:1]
subscribeNext:^(id x) {
NSLog(#"Did range fired");
}];
}
return self;
}
- (void)start {
[self.beaconManager startRangingBeaconsInRegion:self.beaconRegion];
}
- (void)beaconManager:(id)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
if(status == kCLAuthorizationStatusAuthorizedWhenInUse){
[self start];
}
}
The problem is the throttled subscription to RACSignal for the beaconManager:didRangeBeacons:inRegion:selector. Removing the throttle or the delegate method without a signal works fine.
When there is a beacon in range of iOS device, I get notified(CLRegionStateInside) and can start ranging. This works properly. However, when ranging is started and the the iOS devices is not in range anymore, I don't get notified(State doesn't change to CLRegionStateOutside). Not in foreground or background.
Also didEnterRegion and didExitRegion never gets called. I start ranging in didDeterminState when state is CLRegionStateInside.
I do have background refresh settings enabled for my app.
When I start the app for the first time I do get an alert asking for location permission.
So basically, i'm able to start ranging, but i'm not able to stop ranging, because the state doesn't change to CLRegionStateOutside.
I use Xcode 6.3.1, iOS 8.3 on iPhone 4s.
This is my code:
INIT:
- (id)init{
self = [super init];
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
if ([self.locationManager respondsToSelector:#selector(requestAlwaysAuthorization)]) {
[self.locationManager requestAlwaysAuthorization]; //or requestWhenInUseAuthorization
}
NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:#"B75FA2E9-3D02-480A-B05E-0C79DBB922AD"];
self.myBeaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:uuid
identifier:#"TESTREGIO"];
[self.locationManager startMonitoringForRegion:self.myBeaconRegion];
[self.locationManager requestStateForRegion:self.myBeaconRegion];
self.myBeaconRegion.notifyEntryStateOnDisplay = YES;
self.myBeaconRegion.notifyOnEntry = YES;
self.myBeaconRegion.notifyOnExit = YES;
return self;
}
DETERMINESTATE:
- (void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region {
if (state == CLRegionStateInside)
{
[self.locationManager startRangingBeaconsInRegion:self.myBeaconRegion];
}else{
[self.locationManager stopRangingBeaconsInRegion:self.myBeaconRegion];
}
}
DIDENTERREGION and DIDEXITREGION:
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion*)region
{
NSLog(#"DidenterRegion================================================");
}
- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region {
NSLog(#"DidexitRegion================================================");
}
You are not going to get a didExitRegion notification unless you were already inside a region. I suggest you track the state of the region your are looking for separately.
I don't understand what you are doing in didDetermineState. You started ranging for your beacon in init(). You would not need to start ranging again.
A few tips:
You do not need to request any background permissions for this to work.
calls to didExitRegion happen 3 seconds after a beacon is no longer detected when the device is ranging in the foreground. In the background, or in the foreground when not ranging, these calls can be delayed for up to 15 minutes. These delays always exist on iPhone 4S models. On newer models, the delays may or may not exist depending on whether there are enough hardware assist slots for detecting beacons. See details here: http://developer.radiusnetworks.com/2014/03/12/ios7-1-background-detection-times.html
I suspect the failure to get exit events is really an issue of them taking longer than expected. Wait 15 minutes and see if the callbacks come.
You should also call the following function to start ranging
[self.locationManager startRangingBeaconsInRegion:self.myBeaconRegion];
I've been working on iOS app which interacts with iBeacon devices. Workflow is next:
if user near iBeacon then app receiving push notification from internet.
So for recognizing if user near some iBeacon needs to be turned on next modules:
gps
bluetooth
wifi/3G
push notification
The issue is that without turned on GPS module app can't find any iBeacons. It's weird since iBeacon technology works using bluetooth only.
How to solve the following problem?
I use Xcode 6.1.1, iOS 8, CoreLocation and CoreBluetooth frameworks.
Here is a code how I implemented:
if ([CLLocationManager locationServicesEnabled]) {
_locationManager = [[CLLocationManager alloc] init];
_locationManager.delegate = self;
if([_locationManager respondsToSelector:#selector(requestAlwaysAuthorization)]) {
[_locationManager requestAlwaysAuthorization];
}
NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:#"12345678-1234-1234-1234-123456789012"];
NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];
CLBeaconRegion *beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:uuid
identifier:bundleIdentifier];
[_locationManager startMonitoringForRegion:beaconRegion];
[_locationManager startRangingBeaconsInRegion:beaconRegion];
}
else {
NSLog(#"location service is disabled");
}
You don't need GPS for iBeacon to work, but you do need Location Services.
This why I asked how you were "turning off the GPS", as I am not aware of any way in iOS that you can turn off the GPS receiver specifically.
When the user disables Location Services in the Settings app they aren't just turning off GPS - as the name says, they are turning off Location Services. Location Services in iOS refers to anything that can locate the user, which includes GPS, WiFi location and iBeacon.
No, without GPS, iBeacon will not work properly. CLLocation Manager is a class in the core location framework. The delegates of CLocation Manager will not get triggered without GPS. Here iBeacon works with the help of CLocation Manager.
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region;
This is the delegate that is getting triggered while an iBeacon is identified in a region. This delegate will not work without GPS.
I am testing on an iPad 3rd gen on iOS 7.1 since I have no other iOS device for the moment.
The first time I run my app, it starts monitoring for several regions. The status bar and the Location Services settings page are showing the outlined location services icon (my app is the only one in the list that has the outlined icon). When I kill my app, the icon is still showing on both places since I do not stop monitoring the regions yet. Until then everything is fine.
My problem is when I run my app for a second time, I stop monitoring for all monitored regions, but the location services outlined icon does not disappear on the status bar and the Location Services settings page...
Here is my code called at the first run :
- (void) getLocationManagerInstance {
if (!self.locationManager) {
self.locationManager = [CLLocationManager new];
}
self.locationManager.delegate = self;
}
- (void) startLocationGathering {
if(self.shouldUpdateGPSLocations) {
[self.locationManager startMonitoringSignificantLocationChanges];
}
}
- (void) startMonitoringBeaconRegions {
if(self.rootRegion) {
[self.locationManager startMonitoringForRegion:self.rootRegion];
}
if (self.beaconRegions && self.beaconRegions.count < 20) {
[self.beaconRegions enumerateObjectsUsingBlock:^(CLBeaconRegion* region, NSUInteger idx, BOOL *stop) {
[self.locationManager startMonitoringForRegion:region];
}];
}
}
- (void) startMonitoringCircularRegions {
if (self.gpsRegions && self.gpsRegions.count) {
[self.gpsRegions enumerateObjectsUsingBlock:^(CLCircularRegion* region, NSUInteger idx, BOOL *stop) {
[self.locationManager startMonitoringForRegion:region];
}];
}
}
And my code called at the second run :
- (void) getLocationManagerInstance {
if (!self.locationManager) {
self.locationManager = [CLLocationManager new];
}
self.locationManager.delegate = self;
}
- (void) locationManagerCleanup {
[AWRUtils dlog:#"locationManagerCleanup"];
NSArray* monitoredRegions = [self.locationManager monitoredRegions].allObjects;
for (CLRegion* r in monitoredRegions) {
[self.locationManager stopMonitoringForRegion:r];
}
NSArray* rangedRegions = [self.locationManager rangedRegions].allObjects;
for (CLBeaconRegion* r in rangedRegions) {
[self.locationManager stopRangingBeaconsInRegion:r];
}
[self.locationManager stopUpdatingLocation];
[self.locationManager stopMonitoringSignificantLocationChanges];
}
If I uninstall my app, the outlined location services icon disappear. But why is the icon not disappearing when I stop monitoring the monitored regions?
EDIT: After more testing, I found that the instance of the CLLocationManager that I have on the second run has no monitored regions ([self.locationManager monitoredRegions] returns nil)...
EDIT 2: I also found that if, on the second run, I start monitoring all the same regions that started monitoring on the first run AND THEN I stop monitoring them, the outlined location services icon disappear. Is this a normal behavior? I read nothing about that in all my internet researches...
I am using a custom BeaconManager delegate so that beacon ranging is not determined by the life-cycle of the view controller. Everything works great but every once in a while (1-2 days) beacon ranging will stop working and didRangeBeacons will never get called. The only way to fix this is for me to reset my iPhone, once I do this, it works perfectly. Below is the code that I am using. The basic flow is that when my ViewController calls ViewDidLoad it sends a notification back to the AppDelegate to tell it to start ranging for beacons, I never tell it to stop then because I want it to continue to range for beacons no matter where the user navigates to in the app. I'm wondering if my code is causing this or if this is just a bug with Bluetooth. Thanks for your help!
BeaconManager.m
#import "BeaconManager.h"
#import "AppDelegate.h"
#interface BeaconManager()<CLLocationManagerDelegate>
#property (nonatomic, strong) CLLocationManager *locationManager;
#property (nonatomic, strong) CLBeaconRegion *beaconRegion;
#end
#implementation BeaconManager
+ (id)sharedManager
{
static BeaconManager *sharedBeaconManager = nil;
static dispatch_once_t once;
dispatch_once(&once, ^{
sharedBeaconManager = [[self alloc] init];
});
return sharedBeaconManager;
}
- (id)init
{
self = [super init];
if(self)
{
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
}
return self;
}
- (void)startBeaconMonitoring:(NSString*)forUUID
{
NSUUID * uuid = [[NSUUID alloc] initWithUUIDString:forUUID];
self.beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:uuid identifier:#"com.beacons.publicRegion"];
[self.locationManager startMonitoringForRegion:self.beaconRegion];
[self.locationManager startRangingBeaconsInRegion:self.beaconRegion];
}
- (void)stopBeaconMonitoring
{
//Stop the region monitoring
if(self.locationManager != nil && self.beaconRegion != nil) {
[self.locationManager stopRangingBeaconsInRegion:self.beaconRegion];
}
}
#pragma mark - CLLocationManagerDelegate
- (void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region
{
self.beacons = beacons;
if(self.delegate != nil) {
[self.delegate beaconManager:self didRangeBeacons:self.beacons];
}
}
#end
ViewController.m
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] postNotificationName:#"startRanging" object:nil userInfo:nil];
}
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(startRangingForZombies) name:#"startRanging" object: nil];
return YES;
}
- (void)startRanging
{
//Start the beacon region monitoring when the controller loads
BeaconManager *beaconManager = [BeaconManager sharedManager];
beaconManager.delegate = self;
[beaconManager startBeaconMonitoring:#"1234-54324-34242-34242-43243"];
}
We have received many reports at Radius Networks of phones stopping detecting iBeacons and requiring a reboot or turning Bluetooth off and back on again to resolve the situation. Folks have reported this on iPhone 4S, iPhone 5s, iPhone 5c and iPads.
I do not have any hard evidence that this is something that broke as of iOS 7.1, but the report frequency has gone way up since its release. The circumstantial evidence is therefore pretty strong.
When this phone gets into this state, the phone can still scan for bluetooth devices, and can still transmit as an iBeacon. It is therefore not a hardware problem with Bluetooth. Based on the available evidence, it is most likely a newly introduced bug in CoreLocation.
Actually, it's a known bug in iOS 7.1. It's strictly a software problem with the Bluetooth stack for the latest version of iOS. Bluetooth devices detection sometimes just stops working - unfortunately, it's the case for all iOS 7.1-compatible devices. The bug has already been reported to Apple, but as long as they do not issue a fix for that, the best solution is to just reboot the device.
If reboot doesn't help, there is handy guide at SmartRobotic on how to solve it: http://www.smartbotics.com/#!4-Tips-to-Fix-Bluetooth-Problems-After-iOS-71-Upgrade/c118r/031A86F6-C8E8-4768-B4FD-E6F83D9E4317
If you are experiencing Bluetooth connectivity issues after upgrading to iOS 7.1, give these 4 tips a try.
Shut down and restart - some people have reported that this is all that was needed to fix their device after the iOS 7.1 upgrade.
Toggle Bluetooth OFF and back ON - Swipe up to access Control Center and tap the Bluetooth icon, wait at least 30 seconds, then toggle it back on. This can often repair a device which is experiencing connection issues.
Kill (force quit) the offending app - Start by double clicking Home to launch the multitasking cards interface. Touch and hold the card for the app, then toss it up and away. This will force the app to quit and it will be fully reloaded the next time the app is opened.
Clear and reset your Bluetooth device pairing - Go to Settings>Bluetooth and tap on the (i) icon for the offending device. Tap on Forget this Device. Now you should be able to re-add and re-pair the Bluetooth hardware to your device. To clear all paired devices, go to Settings > General > Reset > Reset Network Settings and then set up your Bluetooth pairings again.
Hopefully these suggestions will resolve your Bluetooth connectivity problems.
Cheers.
Apple introduced important changes to Bluetooth LE in 7.1 but also broke something inside.
From my experiments:
iPhone 4S 7.x - iBeacons work like a charm
2 x iPhone 4S 7.1, 2 x iPhone 5 7.1 - works fine but require restarts from time to time (undeterministic).
It looks like system issue - a big one.
I have contacted Estimote - they know about it.
Interesting fact: You can't find beacons - even estimote demo app can't, delegate methods are not called but You can turn (broken)device into a beacon and it will be discovered by other devices.
Disclaimer: I currently work for sensorberg, we´re selling beacons and a SDK.
We´ve hat numerous reports of this bug as well. We asked all our customers to file a bug report with Apple. Here is a template that you can use: https://gist.github.com/anonymous/5283b6941e1f7d4e4461
I personally had the behaviour twice, once I was able to record it: https://www.youtube.com/watch?v=6a6IJzaxxJg Only restarting the device helped.
Continue to file Apple rdar bug reports!
I had the similar issue, but after some time of investigation I realized that it is wrong to call the startMonitoringForRegion(region) and startRangingBeaconsInRegion(region) after each other. That is what you (Patrick) do:
- (void)startBeaconMonitoring:(NSString*)forUUID {
NSUUID * uuid = [[NSUUID alloc] initWithUUIDString:forUUID];
self.beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:uuid identifier:#"com.beacons.publicRegion"];
[self.locationManager startMonitoringForRegion:self.beaconRegion];
[self.locationManager startRangingBeaconsInRegion:self.beaconRegion];
}
Instead the startRangingBeaconsInRegion(region) should to be called in the locationManagerDelegate method locationManager(manager: CLLocationManager!, didDetermineState state: CLRegionState, forRegion region: CLRegion!) (In this case the Swift code). This was my solution.
It make sense, because the first step is to find any beacon in the region, the second one is to get specific information from the already monitored beacon.