I am having a really weird problem here. I am trying to build an app to detect beacons. I don't have any real beacons yet so I am testing with an iPhone 5 and an iPad 3. The strange thing is that the transmitting is only working on the iPad, the iPhone doesn't work as a transmitter even though I used the same app on it.
But even with the iPad as a transmitter, the app only works sometimes - sometimes the iPhone will notify me that it has found a beacon, sometimes it doesn't. I've force-closed the app on the iPad and after the restart it worked, but then another time it doesn't. Since everything is working sporadically I think it can't be the code causing that behaviour, but it might be - I am not an experienced coder, I've just started this. My code is based on this tutorial http://www.devfright.com/ibeacons-tutorial-ios-7-clbeaconregion-clbeacon/
I first thought this might be the answer, but taht didn't solve it: it still did work sometimes, and sometimes it didn't.
Can anybody tell me what I am dealing with her?
Here is my code for the Tracker:
ladBeaconTracker.h
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#import <CoreBluetooth/CoreBluetooth.h>
@interface ladBeaconTracker : UIViewController <CLLocationManagerDelegate>
@property (strong, nonatomic) CLBeaconRegion *beaconRegion;
@property (strong, nonatomic) CLLocationManager *locationManager;
@property (weak, nonatomic) IBOutlet UILabel *beaconFoundLabel;
@end
ladBeaconTracker.m
#import "ladBeaconTracker.h"
@interface ladBeaconTracker ()
@property NSUUID *uuid;
@end
@implementation ladBeaconTracker
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
[self initRegion];
}
- (void)initRegion {
NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:@"A5456D78-C85B-44C6-9F20-8268FD25EF8A"];
self.beaconRegion = [[CLBeaconRegion alloc]initWithProximityUUID:uuid identifier:@"Museum"];
[self.locationManager startMonitoringForRegion:self.beaconRegion];
NSLog(@"Region %@ initated", _beaconRegion.identifier);
}
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region {
[self.locationManager startRangingBeaconsInRegion:self.beaconRegion];
self.beaconRegion.notifyEntryStateOnDisplay = YES;
NSLog(@"Region %@ entered", _beaconRegion.identifier);
}
- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region {
[self.locationManager stopRangingBeaconsInRegion:self.beaconRegion];
NSLog(@"Region %@ exit", _beaconRegion.identifier);
}
- (void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region{
CLBeacon *beacon = [[CLBeacon alloc] init];
beacon= [beacons lastObject];
self.beaconFoundLabel.text =@"Yes";
NSLog(@"Ranged Region %@", _beaconRegion.identifier );
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
@end
And this is the transmitter-Code:
configViewController.h
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#import <CoreBluetooth/CoreBluetooth.h>
@interface ConfigViewController : UIViewController <CBPeripheralManagerDelegate>
@property (strong,nonatomic) CLBeaconRegion *beaconRegion;
@property(strong,nonatomic) NSDictionary *beaconPeripheralData;
@property (strong, nonatomic) CBPeripheralManager *PeripheralManager;
@end
ConfigViewController.m
#import "ConfigViewController.h"
@interface ConfigViewController ()
@end
@implementation ConfigViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
[self initBeacon];
}
- (void) peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral {
if (peripheral.state == CBPeripheralManagerStatePoweredOn) {
NSLog(@"Powered ON");
[self.PeripheralManager startAdvertising:self.beaconPeripheralData];
}
else if (peripheral.state == CBPeripheralManagerStatePoweredOff){
NSLog(@"Powered OFF");
[self.PeripheralManager stopAdvertising];
}
}
- (void)initBeacon {
NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:@"A5456D78-C85B-44C6-9F20-8268FD25EF8A"];
self.beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:uuid
major:1
minor:1
identifier:@"Museum"];
}
- (IBAction)transmitBeacon:(UIButton *)sender {
self.beaconPeripheralData = [self.beaconRegion peripheralDataWithMeasuredPower:nil];
self.PeripheralManager = [[CBPeripheralManager alloc]initWithDelegate:self queue:nil options:nil];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
Your code looks fine for detecting beacons in the background. Two suggestions:
I suspect the problem when the iPad is transmitting is not with the iPad but that the iPhone cannot receive. Try cycling power to the iPhone tto clear a known bug in iOS 7.1.
iOS can detect iBeacons much more quickly in the foreground if you set up ranging at the same time you set up monitoring. Move [self.locationManager startRangingBeaconsInRegion:self.beaconRegion];
into initRegion
and take out stopRangingBeaconsInRegion
entirely.
Once you have done (2), repeat your tests in the foreground and look for your log statement Ranged Region
. You should see this every second when the beacon is on.
In the background, know that it can take up to 15 minutes to both detect an iBeacon and detect that an iBeacon is no longer around. Again, watch your log lines for hints about the current state, and know that you cannot get a new region entry event until you first get a region exit event or reboot your phone.