57

I managed to make an iBeacon which triggers a local push notification on my iPhone when the beacon is in range. It's perfectly working when the app is in background mode.

My question is: Can I trigger the notification even when the app is not running, not even in the background?

I thought this was possible but I'm not sure. If so, how can I accomplish this?

Thanks!

Lapidus
  • 905
  • 1
  • 10
  • 16
  • I have explained the process to discovered iBeacons in background here : http://stackoverflow.com/a/19152814/839566 I hope it helps – manishnath Oct 03 '13 at 07:53
  • You could call the CLLocationManager's `requestStateForRegion:` instance method to determine if your in or out of region when the app starts. – Tobias Apr 07 '14 at 20:50
  • @Lapidus Can you please give me idea how you manage to trigger beacon in background? I am finding issue in this need help. – sinh99 Mar 23 '16 at 11:33

6 Answers6

67

Yes, it's possible and should be automatic.

After you have created a CLBeaconRegion and started monitoring on it, Location Services will keep track of whether your phone is in or out of the region, even when your app isn't running. If you app isn't running during a transition, iOS will launch your app into the background for a few seconds to call the appropriate CLLocationManagerDelegate methods.

I found out the above behavior through experimentation with my own app, but have also witnessed it with Apple's AirLocate sample program. With AirLocate, if you set up a monitoring region then reboot your phone, AirLocate will still deliver a local notification as soon as the phone enters the region.

Take care when testing this, because sometimes it takes up to 4 minutes after turning on/off an iBeacon before iOS recognizes the region state transition. EDIT: As of the iPhone 5, apps will typically use hardware acceleration to detect beacons within a few seconds, and if hardware acceleration is not available, it can take up to 15 minutes.

EDIT 3: AS of iOS 13, you must make sure the user actually grants background permission and not "only once" or "when in use" permission which are heavily pushed by the operating system in the dialogs they present to the user. See here for details.

EDIT 2: As of iOS 8, you need to make sure you have called and successfully obtained locationManager.requestAlwaysAuthorization() as locationManager.requestWhenInUseAuthorization() only lets beacons be detected in the foreground.

I have posted a detailed discussion on how this all works in this blog post.

davidgyoung
  • 59,109
  • 12
  • 105
  • 181
  • Still not working for me for some reason. I tried configuring an iPad as a beacon and using an iPhone to monitor it. It works fine when app is running in the background. But when the app is not running, it does not do anything at all. I am using Apple's AirLocate without any changes. Am I missing something? Any help is greatly appreciated please. – Dinesh Reddy Parne Oct 04 '13 at 07:05
  • In AirLocate, the notifyEntryStateOnDisplay=YES option must also be set for this to work. That app does this with a toggle switch under the Monitoring menu option, do you have this enabled? – davidgyoung Oct 04 '13 at 12:22
  • Yes I did. I enabled all options on the monitoring menu. – Dinesh Reddy Parne Oct 04 '13 at 13:07
  • David, how were you testing this? I myself started monitoring a region which was configured as beacon. I killed the app in the device listening for beacons and also killed beacon. I turned on the beacon to see if the AirLocate app in the device listening for beacons is launched. Nothing happened. Is there something wrong with my approach? – Dinesh Reddy Parne Oct 04 '13 at 16:24
  • OK, I have been unable to reproduce with AirLocate -- only with my own app. I am still investigating what might be the difference. – davidgyoung Oct 04 '13 at 22:45
  • @davidgyoung what is your understanding of op's term "app is not running". If it is app is available in the multitasking menu, but is terminated. Then you are correct, but if it is terminated & removed from the multitasking menu, I have yet to confirm that monitoring works in that case. Do you have any advice? – Kaan Oct 11 '13 at 11:44
  • I have it working across a reboot in my app. It seems Location Services in iOS7 saves your app`s monitoring Regions across a reboot. This isn't super surprising, because it is how regular iOS geofences work. – davidgyoung Oct 11 '13 at 20:01
  • @davidgyoung Watching the Apple developer video: "What's New in Core Location," (https://developer.apple.com/wwdc/videos/index.php?id=307) The Apple engineer indicates that closing an app (by terminating through the multitasking menu) is seen as a clear indication that the user does not want the app to be functioning. Thus, it kills the CoreLocation functionality along with the app. – Brian A Bird Oct 18 '13 at 20:55
  • This question is actually pretty complicated and difficult to answer here for all use cases. See my blog post here: https://github.com/RadiusNetworks/radiusnetworks.github.io/blob/master/_posts/2013-11-13-ibeacon-monitoring-in-the-background-and-foreground.md – davidgyoung Nov 14 '13 at 00:54
  • @BrianABird I saw this as well. Although, the documentation and the Apple engineer state that if your app is terminated but enters a region it will launch the app and call the appropriate delegate methods. Which is frustrating as hell because I can't seem to get this functionality out of my iPhone5 and a actual BLE sensor. It's random even when the app is running in the background. argh! frustrating >:( – random Nov 14 '13 at 17:25
  • If the user elects to terminate the app through task manager, you are out of luck. – davidgyoung Nov 15 '13 at 04:39
  • If nobody can reproduce this solution and this answer is wrong, then why is this answer selected correct answer and got 19 upvotes? – xecute Dec 31 '13 at 10:26
  • The comment about what happens when the app is terminated is correct, but not part of the original question. Most users do not manually terminate apps in normal use. This may deserve a different question with a different answer. In my full answer in the blog post linked from above, I give explicit instructions on how to reproduce, even providing code on github. – davidgyoung Dec 31 '13 at 16:04
  • Beacon regions are NOT monitored or detected when the app is not running. Setting region.notifyEntryStateOnDisplay to true or false makes no difference when the application is not running on foreground or background. – John Doe Feb 04 '14 at 04:02
  • I agree that region.notifyEntryStateOnDisplay makes no difference. Details are in blog post referenced above. I disagree with your statement abot detecting and monitoring. If an app serts up monitoring and a phone is power cycled, it will detect. – davidgyoung Feb 04 '14 at 04:34
  • @davidgyoung ivn my application iOS only relaunches my app when display is turned on not if it's already on or not when display is off. Is this the expected behavior?? Or am I missing something?? – vipul mittal Feb 04 '14 at 09:02
  • @vippul-mitral, there are lots of variables here, but what you say should work. You might try posting a new question with more details: code snippet, detailed test conditions, detailed observations. – davidgyoung Feb 04 '14 at 12:52
  • @davidgyoung have you tried having 2 apps monitor the same UUID region? Have you found that one ever gets the didEnterRegion notification faster than the other? AirLocate is always first to get notified after a reboot for me. My own app seems to have delays, not sure if its because of my setup (my location delegate is a separate singleton class). If I force quit my app, then activate my beacon, the notification is instant. – Ted Avery Apr 10 '14 at 06:29
  • @ted-avery I have not seen this. Probably worthy of its own question if you can share a code snippet. – davidgyoung Apr 10 '14 at 12:08
  • 2
    @davidgyoung OK, after lots of testing of everything I could possibly think of, for some reason after a device reboot, `- (void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region` will get triggered, but `didEnterRegion` will NOT. I've moved my logic into that method instead. Very bizarre. – Ted Avery Apr 11 '14 at 06:10
  • @davidgyoung when you say "iOS will launch your app into the background for a few seconds", do you know how long exactly ? I try to save date/time when user enter and exit a beacon region. Delegate is called when app is in background but nothing was saved. It's like the app could not stay "awake" enough time. – Jonathan Feb 13 '15 at 07:13
  • You have 10 seconds, so this should be plenty of time. Something else must be wrong. – davidgyoung Feb 13 '15 at 08:20
  • @davidgyoung If App is not in background, this time can we perform a webservice ? – Vinu David Jose Dec 21 '16 at 10:07
18

OK I've gotten this to work correctly and experimented around with it so here is the answer. This is what you need to do to get your app to be invoked when crossing a beacon region boundary after the app has been terminated (assuming your app works properly when in the foreground):

  1. You must implement a CLLocation delegate inside your AppDelegate.m module. This delegate is what gets invoked by iOS so if you don't have the CLLocation delegate code in AppDelegate.m, you won't be able to respond to iOS when your app has been terminated. This is what Apple's AirLocate sample app does.

So, inside AppDelegate.m you need the following (you also need to link in CoreLocation.h):

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.

// This location manager will be used to notify the user of region state transitions when the app has been previously terminated.
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
return YES;
}
  1. Inside AppDelegate.m, you need to implement the locationManager didDetermineState method, like this:

    -(void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region{
    
      UILocalNotification *notification = [[UILocalNotification alloc] init];
    
      if(state == CLRegionStateInside)
      {
        notification.alertBody = [NSString stringWithFormat:@"You are inside region %@", region.identifier];
      }
      else if(state == CLRegionStateOutside)
      {
        notification.alertBody = [NSString stringWithFormat:@"You are outside region %@", region.identifier];
      }
     else
     {
       return;
     }
    
      [[UIApplication sharedApplication] presentLocalNotificationNow:notification];
     }
    

--> So if your app has been terminated (it must be run at least ONCE), when the device transitions across a beacon boundary that you are monitoring, iOS will invoke your app and call the locationManager:didDetermineState method in your AppDelegate.m module. Inside this method you can then set up and call presentLocalNotificationNow. If your app is NOT in the foreground when this happens, iOS will present the notification on the screen even if it is locked. The user will then have to invoke the app for more information.

I'm pretty sure that memory pressure has nothing to do with this. Also, the setting notifyEntryStateOnDisplay has nothing to do this this issue either. Setting notifyEntryStateOnDisplay is only used when the user turns on the iOS device display (ie hits "home" or top left button). If the user does this and notifyEntryStateOnDisplay is TRUE, AND the device is INSIDE the beacon region you are monitoring for, THEN you get a notification on the display at that time. If this property is set to FALSE, you don't.

Of course, you need to be running iOS 7.1 in order for this stuff to work correctly.

For more details, visit Apple's documentation

MaxZoom
  • 6,949
  • 5
  • 24
  • 41
TNBtech
  • 482
  • 4
  • 7
  • 3
    +1 for the CLLocation delegate to AppDelegate, thanks ! – M to the K Apr 03 '14 at 10:39
  • Can the CLLocationManagerDelegate protocol be added to the AppDelegate dynamically with `class_addMethod` and `class_addProtocol`? – Joe Beuckman Jul 25 '14 at 20:06
  • When the relevant Core Location method is triggered by an iBeacon, and the app was previously terminated, do you know if applicationDidFinishLaunchingWithOptions: is called first? – UberJason Aug 06 '14 at 14:14
  • I can confirm that this answer works with Estimote beacons (using the Estimote SDK). – adbie Jan 07 '15 at 10:03
  • @TNBtech Can you please let me know if it works in iOS8? bcoz I have tested in iOS8 but its not works so kindly tell me what is the exact issue? – Nikunj Jadav Mar 06 '15 at 07:21
10

You need to switch notifyEntryStateOnDisplay=YES for CLBeaconRegion for the system to wake your app for iBeacon entry/exit event.

But there is one tricky part. If your app is not running the system will only wake your app for beacon entry/exit handling if your app was terminated previously due to system memory pressure. If the user kills the app by swiping it up in the task view, the system will not wake your app. To verify this behaviour, launch you app, put it to background, then consecutively launch several memory consuming apps. I launched several 3D games before my app gets terminated by the system due to memory pressure.

Jian Yin Shen
  • 257
  • 2
  • 4
6

Just upgrade your iOS version to 7.1 and set "notifyEntryStateOnDisplay=YES" and it should work like a charm even when your app is not running. I was having this problem earlier but it got fixed once I did this upgrade! Enjoy..

Hugh Mbaezue
  • 93
  • 2
  • 7
2

The only way I have been able to make this work is by monitoring for major location changes which seem to do the trick. Be warned I have not tested this for all of the device or use case scenarios.

Ronny Khan
  • 31
  • 2
  • The user asked how to do this when the app is not running at all. When the app is not running at all, does it have any access to location services? – Andrew Oct 26 '13 at 20:43
  • 1
    Yes it has. The os will apparently wake the applications that subscribe to this service even after a restart when the application never has been started. Now exactly what the criteria for major location change is I can not tell, but for the test cases I have performed which are entry into a beacon region it does work and prompts the user to choose to start the application. – Ronny Khan Oct 27 '13 at 09:28
  • I think it's a change of cell tower – NickG Jun 26 '15 at 16:32
2

Yes, we can present the local notification in kill state or in the background state, just follow the steps,

1) Start location manager using CLLocationManager class.

locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.desiredAccuracy=kCLLocationAccuracyBest;
locationManager.distanceFilter=kCLDistanceFilterNone;

2) Create CLBeaconRegion like,

CLBeaconRegion *beacon_Region = [[CLBeaconRegion alloc] initWithProximityUUID:uuid major:mjorVa minor:minorVa identifier:identifier];
beacon_Region.notifyEntryStateOnDisplay = YES;
beacon_Region.notifyOnEntry=YES;
beacon_Region.notifyOnExit=YES;

3) Implement two location manager delegate method like,

-didEnterRegion
-didExitRegion

The above two location manager method will work even your app is kill or in background. The system will keep track of your beacon and when it goes out of the range the system will fire the didExitRegion method and when comes in the system will fire the didEnterRegion method.

Gautam Sareriya
  • 1,807
  • 18
  • 30