startmonitoringsignificantlocationchanges and querying the network - ios

I've written a geo-fencing iOS app that uses startmonitoringsignificantlocationchanges to trigger an action that requires network query. It does not work reliably because sometimes the network delay causes the app to be terminated. I do realize that the documentation says, iOS may terminate the app if I try to do network query that takes too long:
"If you leave this service running and your application is subsequently suspended or terminated, the service automatically wakes up your application when new location data arrives. At wake-up time, your application is put into the background and given a small amount of time to process the location data. Because your application is in the background, it should do minimal work and avoid any tasks (such as querying the network) that might prevent it from returning before the allocated time expires. If it does not, your application may be terminated."
Is there an alternative way to start network query in the background when iOS launches my app as a result of entering a region?

Yes, you have to call beginBackgroundTaskWithExpirationHandler. See here:
http://www.mindsizzlers.com/2011/07/ios-background-location/

Related

Deleting Parse Objects when App Terminates [duplicate]

I'm creating an application for my school which should check every n minutes if there is a new mark on the website.
To do this when the user login for the first time, the number of the actual mark is saved in the "UserDefaults". When app is terminated, after n minutes, the number of mark is recounted and compared with the previous one and send a notification in case the number is changed.
What I'd like to know if there is a way to perform this task. I've tried to create a timer in -applicationWillTerminate- but it's fired only once.
This is what I tried:
func applicationWillTerminate(_ application: UIApplication) {
DispatchQueue.main.async {
self.timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(AppDelegate.findMark), userInfo: nil, repeats: true)
self.timer.fire()
}
}
Selector findMark is the task.
Thanks in advance!
You have two options
Background App Refresh
Silent push notifications
Easiest one is Background App Refresh. Because later one needs a server to send the notification. You can check following API for the usage. Basically you set Background Fetch capability on Capabilities/Background Modes of your app. Then from time to time, iOS will wake up your app and call application(_:performFetchWithCompletionHandler:) delegate. You will have around 30-45 seconds to call your function and call completion handler. If you don't finish it on time, iOS will kill your app. If you don't obey the rules, iOS will give you less chances to wake up. For more detailed usage of Background Modes, you may check following tutorial
It's not possible to perform tasks like described in your question after the app is terminated. As described in the documentation:
App Termination
Apps must be prepared for termination to happen at any time and should not wait to save user data or perform other critical tasks. System-initiated termination is a normal part of an app’s life cycle. The system usually terminates apps so that it can reclaim memory and make room for other apps being launched by the user, but the system may also terminate apps that are misbehaving or not responding to events in a timely manner.
Suspended apps receive no notification when they are terminated; the system kills the process and reclaims the corresponding memory. If an app is currently running in the background and not suspended, the system calls the applicationWillTerminate: of its app delegate prior to termination. The system does not call this method when the device reboots.
In addition to the system terminating your app, the user can terminate your app explicitly using the multitasking UI. User-initiated termination has the same effect as terminating a suspended app. The app’s process is killed and no notification is sent to the app.
Link: https://developer.apple.com/library/content/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/TheAppLifeCycle/TheAppLifeCycle.html
Edit:
You cannot perform any task after the application is terminated. What you can do is get that calculation done from server side and send a Push Notification to the device.
Accutally , the answers is yes. But you should not.
We can use Location to archive your goal.
Accroding to offical document in here: https://developer.apple.com/library/archive/documentation/UserExperience/Conceptual/LocationAwarenessPG/CoreLocation/CoreLocation.html#//apple_ref/doc/uid/TP40009497-CH2-SW10:
Note: If your app is terminated either by a user or by the system, the
system doesn’t automatically restart your app when new location
updates arrive. A user must explicitly relaunch your app before the
delivery of location updates resumes. The only way to have your app
relaunched automatically is to use region monitoring or the
significant-change location service.
So we can archive it by using Starting the Significant-Change Location Service:
If you leave the significant-change location service running and your
iOS app is subsequently suspended or terminated, the service
automatically wakes up your app when new location data arrives. At
wake-up time, the app is put into the background and you are given a
small amount of time (around 10 seconds) to manually restart location
services and process the location data. (You must manually restart
location services in the background before any pending location
updates can be delivered, as described in Knowing When to Start
Location Services.) Because your app is in the background, it must do
minimal work and avoid any tasks (such as querying the network) that
might prevent it from returning before the allocated time expires. If
it does not, your app will be terminated. If an iOS app needs more
time to process the location data, it can request more background
execution time using the
beginBackgroundTaskWithName:expirationHandler: method of the
UIApplication class.
Call location update in willFinishLaunchingWithOptions and applicationDidBecomeActive Then you can excute your own code right after
[_locationManager startUpdatingLocation];
But is is extremly drain your battery, you may be rejected by app store.

Handling server being aware if iPhone app is running (heartbeat)

I am not sure how to best implement keeping our server informed that our iPhone application is currently running (even when in the background). There are a few different options but there is some concern as to what is allowed by the Apple approval process as well as what is the most reliable. The application does have the ability to play music in the background, if that factors into what is approved by Apple.
The first option, is to continually send some sort of heartbeat to the server at a set interval through a simple GET/POST; however, the concern is whether or not this is allowed as a background task. In a very roundabout way it can be argued that it is necessary for the playback but I'm not so sure whether or not that is acceptable. If the server does not hear from them in a set amount of time it will assume the app is no longer running.
The second option involves using a presence channel socket connection and have the server just handle when users enter and leave that channel. With this option the main concern is how reliable is a socket connection like this while an app is in the background. Similarly, whether or not this is allowed by Apple. In this case when the app dies, connection closes and server knows app is not running.
Third option can be used in tandem with either of the other options but to use some sort of APNS push to query the phone as to whether or not it has died and have it respond with some data to let us know; however, this seems somewhat counterintuitive as the push itself wakes the app up.
Any other suggestions or input are also welcome.
Not sure if this should be a comment or answer, but let me put my 2 cents here.
Firstly, Can you please elaborate your needs further, because in case you are playing an Audio in background with AVPlayer/AVPlayerItem you would hand over your content URL to iOS and it will make the calls as and when necessary to keep the playback running, you dont need to know about apps' state.
Let me assume, for whatever reasons you want to achieve what the question asks:
There are 3 states your app can be in when it is "Not Running"
i. Suspended State: your app is not killed but its not receiving any CPU time to execute your code.
ii. Killed by OS: Your app can be terminated by iOS to free up the memory or any other resources.
iii. Force Killed by User: If user swipes up your app from app switcher it gets force killed.
Now when your app is Not Running, you CAN NOT query it, but you can move it to Running State. You can achieve this transition by using following methods (Not exhaustive list, but mentions common ways)
i. Background Fetch : You can configure your app to be invoked periodically, so that it can synchronise with the server and updates its state.
ii. Push Notifications (APNS) : You can ping the app from server so that iOS invokes it for some short period of time (few seconds) to update its state.
iii. VOIP Pushes: If your App is VOIP app you can use PushKit to send Silent Pushes which will launch even the Force-Killed Apps, the above two methods does not transition the app to Running state if it was force killed by user
The above point can be helpful in devising overall strategy but does not answer the question, "How to keep syncing the RUNNING state"
i. When your app is Running(Forground/Background), you can do almost anything that is publicly documented, you can keep calling a URL every minute or every 5 seconds, you need to worry about UX on the device rather than approval process, (People will delete app if they see your app in top battery drainers in the settings section)
ii. For making an HTTP call while your app is in background, you can look at Background URL Session, which off loads the HTTP calls to another process so that the call completes even if app gets killed.
iii. For the socket based approach please refer this technical note. The main issue is that you do not/can not know when your app moves from Running to Not Running State.
iv. As you mentioned that your app uses background audio, it will be always be in Running state if the user plays an audio and puts app in background, in such case you can use Periodic Observer to do some Heartbeat call periodically when the content is being played out.

Processing network calls from background. Is it allowed by Apple?

We are working in a groupon-like app where alerts are displayed to the user when he/she enters in the range of an offer.
The client insists on having alerts even when the app is in the background.
Due to the architecture of the system, the app gets the location of the client at intervals and checks with the server if there is any new alert. If so, the app does some processing in the local database and displays a notification.
APN cannot be used since changes in the server are out of reach for this project.
My question is whether Apple would accept it in AppleStore as I have read different opinions about it and Apple discourages its use as in this extract from iOS Developer Library
http://developer.apple.com/library/ios/#documentation/userexperience/conceptual/LocationAwarenessPG/CoreLocation/CoreLocation.html
At wake-up time, your app is put into the background and given a small amount of time to process the location data. Because your app is in the background, it should do minimal work and avoid any tasks (such as querying the network) that might prevent it from returning before the allocated time expires. If it does not, your app may be terminated
Thanks
How often does your app get location from the user? According to Apple's Background Execution and Multitasking, if you are getting the location updates on a regular schedule (I think it's less than 10 minutes), your app can still process in the background if you add location to UIBackgroundModes in your info.plist. With those CLLocation coordinates, you can then process your web service requests.
I personally have not done something like this, so I can't tell you for sure if Apple will reject your app or not. However, if everything is within the guideline and requirements set by Apple, I don't see why they would reject your app.
EDIT:
From the Apple doc:
An app that provides continuous location updates to the user (even
when in the background) can enable background location services by
including the UIBackgroundModes key (with the location value) in its
Info.plist file. The inclusion of this value in the UIBackgroundModes
key does not preclude the system from suspending the app, but it does
tell the system that it should wake up the app whenever there is new
location data to deliver. Thus, this key effectively lets the app run
in the background to process location updates whenever they occur.
I guess even if the app is suspended, it will wake up whenever there is a new location data to deliver.
I think you should reconsider your approach for this application. It sounds like you have decided to build a set of features which are not necessarily well informed by, or a good fit for, the characteristics of the devices the app will run on.
You write that "the app gets the location every n minutes" but that's not how iOS location services work. Querying location services for the current location occasionally is a good approach when your app is running in the foreground but that's not an option once it is suspended or terminated. Instead you need to subscribe to location events, at some level of accuracy, and your app will be notified when the device's location changes. There are no guarantees about the schedule on which you receive these events and it varies depending on the accuracy you request and the speed at which the device is moving.
Additionally, obtaining a location is an expensive operation and can quickly drain the device's battery. Burning through a user's available battery power in an hour or two is a very good way to get your app uninstalled quickly. Where possible you should be using the significant location change service to get low accuracy location updates with minimal power consumption. If you need more precision then consider using boundary crossing events for a defined region or at least reduce the accuracy your have requested as much as possible.
With all that out of the way you still need to work within the limited time your app has to run once started by a location update. That's probably not long enough to make a round trip to the server. If a network connection is already active and the device happens to have low latency you will probably get a response some of the time but I would expect to see the app terminated by the OS frequently. When that happens I don't know that you will continue to receive location updates which might otherwise re-launch the app.
Instead of downloading a list of alerts and displaying them locally a better solution might be to attempt to send your current location to the server via UDP when you see a significant location change. That way you can fire off a network request without waiting for a response. Only some of those requests will still succeed but at least your app won't be terminated. You can then process the locations you receive on the server and send push notifications when appropriate.
I realize that you don't seem to be able to make server side changes. In that case the best you might be able to do is pre-fetch alerts for the nearby region when the app runs (and if you ever manage to complete a round-trip while in the background). That way you could compare location updates to that list and not need to fire off a network request on every location update. Unfortunately it sounds like you might be backed into a corner here with no reliable solution available under your current constraints.

guaranteed delivery for uploads after network reconnect, even if my app is not running

I'm spec-ing an iOS app (which will be built outside of our company) which will upload a user's data entry to a server. If the device is not connected to the Internet, we'd like to save data on the device and upload it when the network is re-connected. (The app will primarily run on iPod Touch devices that will be disconnected most of the time).
If the user unlocks the device and re-opens our app after the network is reconnected, then uploading to the server should be easy because the app is running.
But what if the app is not running, where "not running" can mean one or more of:
device was power cycled
user has locked the device and it's sitting in his pocket
app crashed
user exited the app
user started using other apps so our app isn't running in the foreground anymore
are there other cases?
In the cases above, is there a way (ideally a battery-efficient way) to ensure that local data is uploaded soon after Internet connectivity is restored? Is the answer different depending on which of the cases above caused the app not to be running?
And is there a minimum iOS version the device will need in order to enable some (or all) of the above not-running cases to still upload when the app is not running?
My apologies if these are obvious newbie questions-- I'm not an iOS expert.
There is an interesting technique that is used by among others Instapaper and News.me(the pioneers of this technique) where you use region monitoring to initiate background downloads or uploads. Marco (Instapaper) blogged and talked (in episode 80 of the Build and Analyze podcast) about his communication with Apple so it should be a allowed in the App Store.
In brief the technique is that you set up certain regions (geofences) like "home" or "work" and respond to the locationManager:didEnterRegion: (and similar) callback(s). Your app will wake up from the background once you enter the pre-specified region and you can check to see if there is any data to upload.
This technique won't guarantee that the data is uploaded when the network reconnects but it will allow your app to automatically upload the information when the iPod Touch users gets home to their WiFi network.
That should most likely be at least once a day which may or may not be frequent enough for you. You could add a timestamp to when the initial upload was attempted and send that along the upload once it succeeds to get the correct order of events (data entries) on your server.
There is no way to ensure this. If your application is "not running" (by the definition described in your question), it will not be capable of responding to a change in the device's network status. It should be setup to resume upload operations the next time the application runs again.
EDIT:
Some of the cases you've described may indeed provide different opportunities for your application. Specifically, if the user "exits" the app by pressing the home button or launches another app in the foreground, your application may continue to run the in the background and could potentially respond to a change in network reachability.
The nature of what may be done in the background and for how-long is well documented, and supported by any version of iOS that supports multi-tasking. I recommend you review the documentation pertaining to App States and Background Services.
device was power cycled --> really NO WAY of resuming, unless you open the App!!!
user has locked the device and it's sitting in his pocket --> apps applicationStatus is UIApplicationStateInactive but it is running in the background. You still are able to react to notifications and i.e. accelerometer events. Try the Reachability Class and Log the changes!
app crashed --> NO WAY, unless opening the App
user exited the app --> App is sitting in the background. There you have a maximum of 10 Minutes Restriction of fully using your App (like the App "Pastebot" does)
user started using other apps so our app isn't running in the
foreground anymore --> Same as user exited the app
On multitasking Apple says the following:
Real multitasking only for certain kinds of usage, as there is Audio Background playing, VOIP (like Skype), navigation applications
All the other apps can request a specific amount of time after the app is closed/in the background, to finish certain tasks (as sending an email, sms or uploading/downloading important data)
Important Quote from dev docs:
Your app delegate’s applicationDidEnterBackground: method has approximately 5 seconds to finish any tasks and return. In practice, this method should return as quickly as possible. If the method does not return before time runs out, your app is killed and purged from memory. If you still need more time to perform tasks, call the beginBackgroundTaskWithExpirationHandler: method to request background execution time and then start any long-running tasks in a secondary thread. Regardless of whether you start any background tasks, the applicationDidEnterBackground: method must still exit within 5 seconds.
If you're building a restful API then I would recommend using RestKit, it has a request queue that checks the network status on the device and starts uploading once network access has been assured. You can read more about this here: http://mobile.tutsplus.com/tutorials/iphone/advanced-restkit-development_iphone-sdk/. Read the sections about Request Queue and background download/upload. It should be noted that RestKit is a big library which has it's advantages and disadvantages. I'm not completely sure how this que works with the app lifecycle, if it saves the request que even if the app is terminated. You would have to investigate that. RestKit does support background uploading/downloading, but as already noted, I think it's impossible to do any uploading if the app is terminated and not in background state.
I wouldn't recommend using RestKit if the API isn't Rest though.
You can download and experiment with RestKit here: https://github.com/RestKit/RestKit.

iOS Significant Location Change while Asleep

Does the iOS Location Manager's SignificantLocationChange wake the application if the phone is asleep (e.g., in pocket of user)?
From Location awareness programming guide:
If you leave this service running and your application is subsequently suspended or terminated, the service automatically wakes up your application when new location data arrives. At wake-up time, your application is put into the background and given a small amount of time to process the location data. Because your application is in the background, it should do minimal work and avoid any tasks (such as querying the network) that might prevent it from returning before the allocated time expires. If it does not, your application may be terminated.

Resources