Beacon monitoring in background iOS - ios

I am developing an iOS app in Swift that monitors for beacon events. This is my first real beacon endeavor.
I am using Estimote beacons, but am not using the Estimote SDK. I am using core location and a CLLocationManager with didExit and didEnter events.
I am only listening for beacons that are registered with the current user that is signed in to my app. For example, John Doe could be registered with beacons A and B, while Mary Sue is only registered to beacon C. I am experiencing a lot of false leaves, and wonder if it is because of where I am implementing my code.
I understand that there is a default 30 second latency when validating a leave event, but I am experiencing periods of longer than 30 seconds without a bluetooth signal from point blank range. Perhaps implement a 30 minute window rather than 30 seconds for a leave validation?
Since a user has to sign-in in order to know what beacons to monitor, the location manager resides within the user's default profile view controller. I successfully get beacon interaction even when the phone is locked, but it is not consistent. I am concerned because I know that the view controller itself is suspended/activated at the iPhone's discretion and may be revealing flaws in my logic.
Should all location oriented code be placed within the app delegate file? If I implement a protocol from my profile view to the app delegate, I can instantiate it there within the app delegate first and then retrieve the beacon data later, once the user is signed in.
I have struggled to find an "iOS beacon convention" in my research, just examples that provide some results. Not too sure whats actually considered proper practice.
Thanks!

It is common to use a software filter to ignore spurious region exit events if an entry event happens soon afterward.
To make this independent of any one ViewController, it is important to have the logic triggered by the AppDelegate. Two choices here:
Put region monitoring callbacks and filter logic directly in the AppDelegate. This is appropriate for small and simple apps.
Put callbacks and filter logic in a custom class, and initialize it from the AppDelegate's didFinishLaunching method. This is the best approach for larger and more complex apps to keep the AppDelegate simple and clean.
Either way, it is critical to trigger starting of monitoring from the didFinishLaunching method. This ensures proper background CoreLocation setup should your app be auto launched by region transitions.

Related

Appropriate way to get user's location(also in killed state) for an "hyperlocal" app

Requirement - I am building a hyperlocal app which will provide offers to user based on his location. When in app I can get his current location and show offers accordingly but what I need now is to send push notification to user based on his location. So I want to find out user's location and send offers based on his location.
I have read Apple doc for Significant-Change Location Service but then this answer is saying that it won't work once app is killed.
I have also read about Tracking the User’s Location but that didn't work for me properly. I was not getting more than 5 updates in background.
My current code in viewDidLoad -
if (self.locationManager == nil)
{
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
self.locationManager.delegate = self;
self.locationManager.allowsBackgroundLocationUpdates = true;
self.locationManager.pausesLocationUpdatesAutomatically = false;
if ([CLLocationManager authorizationStatus] != AVAuthorizationStatusAuthorized) {
[self.locationManager requestAlwaysAuthorization];
}
}
[self.locationManager startUpdatingLocation];
And my delegate method looks like this -
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
if (UIApplication.sharedApplication.applicationState == UIApplicationStateActive) {
// Get updated data according to location
} else {
// Send location to server
}
}
My apps capabilities And my plist -
Please suggest some appropriate way, I can live with accuracy of around 1km.
Another approaches -
Can I get user's location in "Background Mode- Background Fetch"'s fetchNewDataWithCompletionHandler: ?
Can I get user's location using Silent Push notification's application:didReceiveRemoteNotification:fetchCompletionHandler:? ): Not possible according to this answer
So, your question is basically "How can I get around Apple's specifically designed user interaction for people wanting to make my app shut up?"
The answer to that is "Not if you want to stay within the rules for the AppStore".
Listen, if the user decides to terminate your app (i.e. they swipe it up and out of the task manager), the entire point of this design is for your app to stop do anything. Apple designed this so that people can quickly turn off exactly the kind of behavior you seem to want to implement. I don't quite get why you would want to circumvent that. I agree that this interaction is a bit obscure perhaps (after all, users can also allow/disallow an app's right to receive location updates in the background in the Settings app as well), but that's the interaction iOS defines.
If I understand you correctly, you succeeded in properly setting up the background location modes, so your app can receive the location updates even if it is not in the foreground (i.e. in background or suspended, in the latter situation iOS wakes it up briefly so you can process location updates and e.g. send a local notification to inform the user). That's as good as it gets.
Oh, and don't fear a device reboot. Yes, after the reboot your app is technically not running, but since the user didn't explicitly kill it the last time, iOS treats it like it was in suspended mode, IIRC, so you will still get significant location updates and can react properly.
(In a more general way: people often seem to think the actual app process state reflects the app state as it is defined in the documentation and/or that whether the app is shown in the task manager is linked to that. Both isn't entirely true.)
Edit after your comment asking me specifically about background fetch:
Sorry, that was perhaps not entirely clear. I didn't answer on this sub-question specifically, because after the user quits your app intentionally, you should not, as explained, "cheat" on their intention. Silent push notifications won't work because of this, yes.
I don't know whether the background fetch will be suppressed in a similar way (could be, but I haven't tried it), but I think that won't help you either, even if it were still working (which I doubt, Apple's probably going to be hard on this). I have also never tried to (re)start location updates in this method, so I can't say whether that even works (once the completion handler is called the system will likely suspend your app again at least, and I am not sure that this "resets" the "user killed the app flag" that the system seems to use for deciding which app to wake up and deliver location updates to).
application:performFetchWithCompletionHandler: (as it is fully called) will be called by the OS based on heuristics, it tries to be "clever" on this. Since the method is meant to fetch some data from a backend that doesn't provide push notifications, the times it is called at all can be severely limited, as is explained in the documentation. The actual mechanism that decides when the OS calls is a black box and it is a gamble to try to trick it into getting called when you need it. Could be very likely that this happens hours later. Think of the following scenario:
Your app runs fine in the background location modes (I understood you correctly in that you successfully set that up? see here and here)
The user manually kills your app. For the sake of this argument let's assume that doesn't keep the system from later restarting it to give it a background fetch opportunity
Since other apps are running on the system and the user only has an edge connection at the moment, the OS decides it is a bad time to initiate a background fetch (I just assume here that those factors play a role, as said it's a blackbox, but those appear reasonable to me). Your user walks around some more, bypassing several locations you would be interested in.
Two hours later, the user is by now in a completely different area, your app is started again by the OS and gets a application:performFetchWithCompletionHandler: call. You start location updates again (lets assume that even works and the system doesn't immediately terminate the app again, i.e. even after the fake background fetch it delivers location updates in background). You missed several locations, but nevertheless the app now handles new ones. All your logic is basically messed up, because you didn't plan for so many location updates being missing...
(Optional: After a while your user realizes that your app apparently does something, even although they terminated it (for example they notice battery drain). They will delete your app and leave a one-start review...)
(Optional 2, worst-case: Once apple realizes it is possible to re-init background location updates after a user killed an app this way, they simply close the loop-hole in an iOS update and your app is back where we started...)
For the record: I am not defending any design decisions made by apple, heck I know this is confusing to wrap your head around and just as one can make a stand for "preserve battery and user intent under all circumstances" one can make one for better background tracking. I am merely pointing out that they're in control here and trying to weasel around any specific interaction paradigms they set on the platform is likely not a good idea.
That all being said, I am afraid the "user terminated the app, now I won't get any location updates" is simply something you have to live with. Why would that even be a problem for you, considering you didn't say anything about background location modes not being enough for you? The only (questionable) scenario I could imagine is a kind of tracker application, maybe given on devices handed out to employees of a delivery service or something. If that is it (and putting aside the ethics behind such stuff, in some countries that is even illegal to do, mind you...), I have to say that iOS is simply not the correct platform for this.
Hi just create a UILocalNotification set it with your details and most important for notification to be triggered when you enter a Region add the region property see docs
Hope it helps

How does iOS Google Map detect I am entering a car?

The latest iOS Google Map application(currently of version 4.9.0) has a very nice feature, but so far I can not figure out how this is possible.
The feature is: whenever I am entering my car, try to drive somewhere, Google map will send me a notification to my phone, guess where I am going, giving me an estimate of the traffic, even when the phone is in locked state, and the Google map app hasn't been used for days. A screenshot is provied as following:
This is quite amazing to me as an iOS developer. How can it live in the background and detect I am entering a car with such accuracy?
One of my guess is, Google map is using significant location change API. By using this way, the app can be waken in background mode, but, there is still no guarantee it is waken when I am entering my car. And I have already written a test app using this mechanism, although it can be waken in the background, but the timing is not correct, failed to detect I am entering a car or not.
CLLocationManager Class Reference
If you start this service and your app is subsequently terminated, the system automatically relaunches the app into the background if a new event arrives. In such a case, the options dictionary passed to the application:willFinishLaunchingWithOptions: and application:didFinishLaunchingWithOptions: methods of your app delegate contains the key UIApplicationLaunchOptionsLocationKey to indicate that your app was launched because of a location event. Upon relaunch, you must still configure a location manager object and call this method to continue receiving location events. When you restart location services, the current event is delivered to your delegate immediately. In addition, the location property of your location manager object is populated with the most recent location object even before you start location services.
Any suggestion and help appreciated.
One comment from #alexkent points out my mistake. This is Apple's Maps.app, not Google Maps. All credits belongs to #alexkent.
That notification is from Apple's Maps.app not Google Maps. A new
feature in iOS9 (which you must be running) is the ability to detect
when you are in your car. I imagine this is done by detecting the
Bluetooth signal from the car radio (I have not checked this). I do
not believe there is developer API available for this feature.
#alexkent is right. It works using bluetooth and there is no public API for that. If you close your bluetooth this stops happening.
Geofencing and in general Geolocation accuracy is not that granular.

How to do BG NW download after geofence didEnterRegion/didExitRegion notice

I am developing a client/server app for a client where the clients are iOS devices.
One client module is for traveling salespeople.
My client wants the salesperson's app to automatically download it's local database when the salesperson leaves the office, and when the salesperson returns to the office.
I am setting up a geofence using the location manager's startMonitoringForRegion method, and will look for the UIApplicationLaunchOptionsLocationKey on launch, as well as looking for calls to the location manager locationManager:didEnterRegion/locationManager:didExitRegion methods from the background.
My question is, how do I ask the system to allow me time to make a network connection and download new data in response to either an app launch with a UIApplicationLaunchOptionsLocationKey, or background calls to locationManager:didEnterRegion/locationManager:didExitRegion?
None of the background mode keys (UIBackgroundModes) look like the right fit.
I don't need the location key, since I don't need live location information, just geofence enter/exit messages. The fetch key isn't what I need either, since that causes the system to wake my app up in the background at times it chooses.
Do I have to have one of the background mode keys set in order to make a beginBackgroundTaskWithExpirationHandler call?
I guess what I should do is submit my network request in my beginBackgroundTaskWithExpirationHandler in my locationManager:didEnterRegion/locationManager:didExitRegion methods. Will the system allow me to do that without having a UIBackgroundModes key set?
And can I make a call to beginBackgroundTaskWithExpirationHandler if I'm already running from the background? (In the case where the app is running but not frontmost and a locationManager:didEnterRegion or locationManager:didExitRegion call comes in.) My goal is to ask for enough background time to process my network download and save the data to my local database.
Any guidance from somebody who's done something like this would be a big help. The docs are pretty vague, and figuring out how to use the background APIs by trial and error is likely to be pretty time-consuming.
P.S. I'm developing this app in Swift, although I'm a longtime Objective-C developer. I'm comfortable translating Objective-C to Swift or mixing Objective-C and Swift as needed, so examples in either language would be useful.
It is OK to call beginBackgroundTaskWithExpirationHandler from background. As of iOS8 this call will extend your run time to 180 seconds from 10s.
Keep in mind that you need to request Always authorization for location services, that's what is required to run region monitoring.
I suggest you do use the background location key. I forgot to set it in one of my apps, and it still was receiving the region enter/exit events. However,
-- the behavior might change with minor iOS upgrade, and also
-- the app might be rejected by Apple

HTTP request in case of didEnterRegion, didExitRegion

I am a little bit confused about this question.
Somewhere I read, it is not allowed by Apple to make network requests, when an app not in the foreground. Is also the case, when app is woken up by locationManager events?
What I'd like to: define a Beacon region, handle locationManager:didEnterRegion: and locationManager:didExitRegion: to call my simple web service methods. It should be done even if the app is not active and woken up by location manager.
Technically it is easy to implement - basically, I have already made it - but I don't know if it is allowed by Apple, and would take app review successfully.
Thanks!
Yes, it is allowed. It is a common technique, and I have several apps in the AppStore that do this. The Beacon Scavenger Hunt, for example, sends stats back to our server when participants use the iOS app to find beacon targets in the hunt.
When you detect a beacon in the background, however, you only have about 5 seconds of running time from iOS. Take care that your server responds quickly enough. This small time window is the way Apple enforces that people don't abuse this.

Geo location notification in iOS

I want implement a geo location notification in iOS, but just in a specific date and time.
The notification will only be launched if the user is in a certain location and at a certain date and time.
Ex.: The user is in Rio de Janeiro and is 12 o'clock.
Does anyone know how to merge these two conditions to launch a notification?
There is nothing built in that I know of -- you will have to code the logic yourself. So you will simply get geo updates from CoreLocation, and you can create a timer to give you time updates, then write some logic that execs periodically and, if the time/place matches your business rules, fire the notification. Note that the app will have to be running to accomplish this. You could send a push notification from an external server, but that server must know where the phone is, and your app must tell it.
Does this help?
It seems that this can be done elegantly as long as location updates are given precedent.
Use this CoreLocation startMonitoringSignificantLocationChanges
To get updates about location, and these updates will be able to start your app in the background (as explained in the docs). Then, in the "application:didFinishLaunchingWithOptions" method of your AppDelegate, include your logic for checking if it is the right time to send a notification (by checking with stuff in CoreData or otherwise).
There shouldn't be a need to create a timer with periodic checks. Just let CoreLocation handle the event's entry point since it'll launch your app in the background at the right location.
This is simple. I assume that you are familiar using the CLLocationManager and the MKReverseGeocoder classes. For your purpose monitoring for only significant location changes would probably be fine (even if that sometimes only happens for moving kilometers). It will help you save battery power on the device.
So, for CLLocationManager's delegate there is a method called locationManager:didUpdateToLocation:fromLocation what you can use. All you need to do is to use reverse geocoding here to determine the actual city's name depending the actual location using the MKReverseGeocoder class. Also, you have to check the local time on the device, match the two, and act accordingly (set up a local notification to wake the app from the background for example).

Resources