iBeacon Notification when the app is not running
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.
Beacon range in background
When an app is in the background and it gets a didRangeBeacons
callback, it only gets 5 seconds to run by the operating system before it is suspended. This will close any web service connections that are open at that time. You can extend this background running time from 5 seconds to 180 seconds upon request. Below is an example in Swift 3 that shows how to do that.
var threadStarted = false
var backgroundTask: UIBackgroundTaskIdentifier = UIBackgroundTaskInvalid
func extendBackgroundRunningTime() {
if (self.backgroundTask != UIBackgroundTaskInvalid) {
// if we are in here, that means the background task is already running.
// don't restart it.
return
}
print("Attempting to extend background running time")
self.backgroundTask = UIApplication.shared.beginBackgroundTask(withName: "DummyTask", expirationHandler: {
UIApplication.shared.endBackgroundTask(self.backgroundTask)
self.backgroundTask = UIBackgroundTaskInvalid
})
if threadStarted {
print("Background task thread already started.")
}
else {
threadStarted = true
DispatchQueue.global(priority: DispatchQueue.GlobalQueuePriority.default).async {
while (true) {
// A dummy tasks must be running otherwise iOS suspends immediately
Thread.sleep(forTimeInterval: 1);
}
}
}
}
By adding code like this, it is much more likely that your web service call will complete before iOS suspends your app.
You can call the extendBackgroundRunningTime()
from your didRangeBeacons
or didEnterRegion
methods.
iBeacons and local notifications
In order to do this, you need to get the app to run in the background while beacons are in the vicinity. This would allow you to periodically check for updated content associated with the beacon and then display notifications under two conditions:
- When a beacon first appears, and there is a message associated with that beacon.
- The message associated with the beacon above changes during the time the beacon is still visible.
As you mention, the second item is a problem, because you need a way to continually check to see if there is updated content despite the fact that iOS will suspend your app within 5 seconds of beacon detection in the background.
A few options, each of which have downsides:
You can use a custom hardware beacon that changes its identifier every minutes or so (e.g. the minor goes back and forth between 0 and 1). This would allow you to monitor two regions and re-trigger on each every minute the beacon is in the vicinity. Downside: This requires building a custom beacon.
You can make your app request an extra 3 minutes of background running time during which you can check for changed messages. Downside: You only get three minutes to display changed messages.
You can specify extra background modes in your .plist so you can continue running in the background. Downside: Apple won't approve your app for distribution in the store unless you have a good reason to run in the background (e.g. a navigation app or a music player app.)
You can send a push notification to your app each time the message changes, which would wake up your app in the background so you could display an updated notification if a beacon is in the vicinity. Downside: Setting up push notifications are a bit complex, delivery can be slow, and is not guaranteed.
Read here for more info on some of these options: http://developer.radiusnetworks.com/2014/11/13/extending-background-ranging-on-ios.html
iBeacon monitoring and ranging in background
The problem you describe is common. Other than extending background ranging time (which you have already done), there is no magic bullet on iOS.
Two additional techniques may help. Both of these techniques involve getting a new entry event as you pass from one beacon to the next:
Get hardware beacons that allow you to tune down the transmitter power (my company's RadBeacon products allow this) so the transmissions do not overlap. This way you get an exit event then a new enter event as you move from beacon to beacon.
Redesign your identifier schene so the major field is dedicated to identifying up to 20 different regions (based on UUID and major 1-20). You then monitor for all of these regions. Your individual beacons can still use the minor however you want and specifically as the key to trigger messaging. When placing your beacons, you make sure that none with overlapping transmissions share the same major. This will ensure a new background entry event as you move from one to the other.
A simple code to detect any beacon in swift
SWIFT 3:
First of all you should add the CoreLocation.Framework
In the .Plist file add the key/string
NSLocationAlwaysUsageDescription
with appropriate stringIn your Object add the
CLLocationManagerDelegate
Add the
CLLocationManagerDelegate
delegate methods in this example i will just add the didRangeBeacons method
func locationManager(_ manager: CLLocationManager, didRangeBeacons
beacons: [CLBeacon], in region: CLBeaconRegion) {
print(beacons) }
Create and initialise the locationManager
var locationManager : CLLocationManager = CLLocationManager()
Create the
CLBeaconRegion
let beaconRegion : CLBeaconRegion = CLBeaconRegion(
proximityUUID: NSUUID.init(uuidString:"****-****-****-****-******") as! UUID,
identifier: "my beacon")Add the delegate to your Object
locationManager.delegate = self
- Request the Location authorization from the user with
locationManager.requestAlwaysAuthorization()
Now let's start the Range
locationManager.startRangingBeacons(in: beaconRegion)
This will automatically call the delegate method
func locationManager(_ manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], in region: CLBeaconRegion) {
print(beacons)
}
Note : if you want to monitor beacon's entry/ exit state you need to add
locationManager.startMonitoring(for: beaconRegion)
Finally : Make sure that your beacon is turned ON and you are testing the iBeacon Frame Enjoy :D
Related Topics
Issues While Lightweight Core Data Migration
Swift Running Code in Periodically Background
Swift: Method Overriding in Parameterized Class
Cllocationmanager and Tvos - Requestwheninuseauthorization() Not Prompting
Swift-Animate Cashapelayer Stroke Color
Skphysicscontact Not Detecting Categorybitmask Collision
Uislider Handle Changes Width on Different Size Ipads
Having Trouble with Musickit Sample App Provided by Apple
iOS - Could Not Open Obj File When Convert Mdlasset to Mdlmesh
Swift Protocol That Is Using an Enum with Generic Associated Type
How to Read a File from The Filesystem in a Swift Command Line App
How to Convert Curl Request to Swift Using Alamofire
Nsdocumentcontroller.Opendocument Not Allowing Selection of Custom File Type
Need Clarification on Anyobject in Swift