It should not be the first question about this subject, but after reading lots of articles and documents, i still doubt about the feasibility.
According to Apple's document.
State Preservation and Restoration Because state preservation and
restoration is built in to Core Bluetooth, your app can opt in to this
feature to ask the system to preserve the state of your app’s central
and peripheral managers and to continue performing certain
Bluetooth-related tasks on their behalf, even when your app is no
longer running. When one of these tasks completes, the system
relaunches your app into the background and gives your app the
opportunity to restore its state and to handle the event
appropriately. In the case of the home security app described above,
the system would monitor the connection request, and re-relaunch the
app to handle the centralManager:didConnectPeripheral: delegate
callback when the user returned home and the connection request
completed.
Does it mean that we can wake up the application even if it is not running in background? and by which BT event, and implementation of code, we can wake up our application and make it run in background?
Related
I figured out about it is possible to download in background mode of application. I have implemented Background Fetching Mode in XCode and registered background task and its working fine.
Is it possible to resume downloading task after force closing my application by user? How?
No, you can't continue download when your app get terminated by user! Your app must require to remains in background state!!! Because if user force close app that means, he doesn't want to run it anymore. If your app is suspended by system then it can be wake up but not if it's terminated by user!
If an iOS app is terminated by the system and relaunched, the app can use the same identifier to create a new configuration object and session and retrieve the status of transfers that were in progress at the time of termination. This behavior applies only for normal termination of the app by the system. If the user terminates the app from the multitasking screen, the system cancels all of the session’s background transfers. In addition, the system does not automatically relaunch apps that were force quit by the user. The user must explicitly relaunch the app before transfers can begin again.
Update : (As asked in comment)
Refer the apple documentation, It states,
This method lets your app know that it is about to be terminated and purged from memory entirely. You should use this method to perform any final clean-up tasks for your app, such as freeing shared resources, saving user data, and invalidating timers. Your implementation of this method has approximately five seconds to perform any tasks and return. If the method does not return before time expires, the system may kill the process altogether.
For apps that do not support background execution or are linked
against iOS 3.x or earlier, this method is always called when the user
quits the app. For apps that support background execution, this method
is generally not called when the user quits the app because the app
simply moves to the background in that case. However, this method may
be called in situations where the app is running in the background
(not suspended) and the system needs to terminate it for some reason.
After calling this method, the app also posts a
UIApplicationWillTerminate notification to give interested objects a
chance to respond to the transition.
When any task completes, the NSURLSession object calls the delegate’s URLSession:task:didCompleteWithError: method with either an error object or nil (if the task completed successfully).
If the task failed, most apps should retry the request until either the user cancels the download or the server returns an error indicating that the request will never succeed. Your app should not retry immediately, however. Instead, it should use reachability APIs to determine whether the server is reachable, and should make a new request only when it receives a notification that reachability has changed.
If the download task can be resumed, the NSError object’s userInfo dictionary contains a value for the NSURLSessionDownloadTaskResumeData key. Your app should pass this value to call downloadTaskWithResumeData: or downloadTaskWithResumeData:completionHandler: to create a new download task that continues the existing download.
If the task cannot be resumed, your app should create a new download task and restart the transaction from the beginning.
checkout here: Life cycle of URL Session
Yes—if I understood your need right—Apple allows this with State Preservation and Restoration APIs:
Return your app to its previous state after it is terminated by the system.
Check Apple's article: Preserving Your App's UI Across Launches, for an overview of this framework.
Details about preservation process can be found in article: About the UI Preservation Process
Details about restoration process can be found here: About the UI Restoration Process
Raywenderlich have—a little outdated—tutorial implementation of this framework # State Restoration Tutorial: Getting Started
This is from the Core Bluetooth Background Processing for iOS Apps section found in official docs:
Performing Long-Term Actions in the Background
Some apps may need to use the Core Bluetooth framework to perform
long-term actions in the background. As an example, imagine you are
developing a home security app for an iOS device that communicates
with a door lock (equipped with Bluetooth low energy technology). The
app and the lock interact to automatically lock the door when the user
leaves home and unlock the door when the user returns—all while the
app is in the background. When the user leaves home, the iOS device
may eventually become out of range of the lock, causing the connection
to the lock to be lost. At this point, the app can simply call the
connectPeripheral:options: method of the CBCentralManager class, and
because connection requests do not time out, the iOS device will
reconnect when the user returns home.
Okay, we have an app which locks/unlocks doors as appropriate... So as pointed out, this works when app is in the background (most likely in the suspended mode). Now, lets move on (with quoting docs):
Now imagine that the user is away from home for a few days. If the app
is terminated by the system while the user is away, the app will not
be able to reconnect to the lock when the user returns home, and the
user may not be able to unlock the door. For apps like these, it is
critical to be able to continue using Core Bluetooth to perform
long-term actions, such as monitoring active and pending connections.
So, if the user was away from home for a few days, and app has been terminated by the iOS, we will have to implement state preservation and restoration, so that iOS relaunch the app when connection request is detected, and let the app to unlock the door. Related quotes:
In the case of the home security app described above, the system
would monitor the connection request, and re-relaunch the app to
handle the centralManager:didConnectPeripheral: delegate callback when
the user returned home and the connection request completed.
This all make sense, but pay attention to this part again:
Now imagine that the user is away from home for a few days. If the app
is terminated by the system while the user is away, the app will not be able to reconnect to the lock when the user returns home, and
the user may not be able to unlock the door. For apps like these, it
is critical to be able to continue using Core Bluetooth to perform
long-term actions...
Does this means, if the app is forcefully killed by the user at some moment while he was away from home, that this will work as well ? Means when user comes home, the door will unlock anyway, or he must manually relaunch the app to unlock the door?
I am asking this, because of how relaunch of terminated apps works. It is not the same when user kills the app, and when iOS kills the app which supports background execution:
Apps that support background execution may be relaunched by the system
to handle incoming events. If an app is terminated for any reason
other than the user force quitting it, the system launches the app
when one of the following events happens...
Source
So once again, if the user was away for a few days and he has closed the app by double tapping Home button and dragging up, will he be able to enter his home without manually relaunching the app?
No. If the app is forcefully killed by the user then it will not be woken up again. The only scenario where it will be woken up is if the app was terminated by iOS itself, which will happen sooner or later when the app has not been it the foreground for a while. It will also not be relaunched if the device is rebooted.
Having said that, from my experience with Core Bluetooth I have come to the conclusion that State Preservation is way too unreliable. I would believe that the use-case that you are trying to implement will not work well enough, which is ironic since it is exactly the use-case that Apple is promoting it their documentation.
For example, you will have issues with the following:
State restoration will only relaunch your app due to bluetooth related activity if the event originates from a peripheral accessory that you are communicating with, such as connect/disconnect events and characteristics notifications. For other events, most importantly general bluetooth-state-change events, your app will not be relaunched and notified of this. The reason why this is so bad is because any bluetooth-state-change events will cause all pending connections to be tossed, meaning that your pending connections to the door lock will be lost. However, since your app is not relaunched to be notified of this, then it effectively means that your application will still believe that the connections are still pending when in fact they are not. Since your application is terminated at this time, the only way for it to wake up again is by having the user manually launch it again (or alternatively “hack” other background modes for this purpose, which will NOT work very reliably either).
This scenario happens if the user toggles Flight Mode, toggles Bluetooth, power cycles the iOS device, or any other undefined reasons that many cause state changes… And it is very unlikely that a bluetooth-state-change will not happen if "... the user is away from home for a few days.".
This "issue" has been reported by me and others multiple times, but Apple does not appear to want to fix it for some reason.
Apart from this, many other issues exists as well, such as the XPC connection being interrupted at different times for no apparent reason. I have also noticed that the pending connection can go into “limbo” mode where the peripheral state gets set to Connecting, but in fact it will never connect unless you cycle the connection state. Etc, etc, ...
/A
Before asking a question, let me clarify what I understand about iOS App states:
Backgrounded: In this state, your app is not in the foreground anymore but it is still able to run code.
Suspended: Your app enters this state when it’s no longer able to run code.
Now, I wanted to keep downloading going on when App is in background, i.e. it still does exist in multitasking screen. It is working as expected with background transfer service.
But, in some tutorial reference, I have read that you can perform downloading even when App is Backgrounded / Suspended. Can it work even when my App is suspended, i.e. removed from multitasking screen ?
I have been reading many documents including Apple class reference regarding background transfer service with download task, but no one clarifies that the download will not work when App is suspended (killed).
Appreciate your thoughts and advices !!!
If your app has been suspended by the system (without force quiting from multitasking screen) your background session will continue to work.
If you force quit the application all download tasks will be canceled.
The following is from backgroundSessionConfigurationWithIdentifier(_:) documentation :
If an iOS app is terminated by the system and relaunched, the app can use the same identifier to create a new configuration object and session and retrieve the status of transfers that were in progress at the time of termination. This behavior applies only for normal termination of the app by the system. If the user terminates the app from the multitasking screen, the system cancels all of the session’s background transfers. In addition, the system does not automatically relaunch apps that were force quit by the user. The user must explicitly relaunch the app before transfers can begin again.
Apps displayed in the multitasking UI aren’t necessarily executing code or fetching data. Listed apps may be suspended or not running at all
[[UIApplication sharedApplication] applicationState] will check your application state, you can test your app.
NSURLSession class can hand off downloads and uploads to the operating system when the app becomes inactive. As with almost all background execution APIs, if the user force quits from the multitasking UI, the background operation will terminate
In iOS 7, Apple added support for background fetch—a kind of smart, per-app crontab that wakes up at opportunistic times. There is no way to force background fetches to execute at exact intervals. iOS checks how much data and battery power was used during previous background fetches when scheduling future callbacks.
Background fetches can also be triggered by remote push notification and have a very similar delegate method with the same completion handler.
Full Tutorial is here
https://blog.newrelic.com/2016/01/13/ios9-background-execution/
I have a question regarding state Preservation and Restoration for Core Bluetooth on iOS 7.
I can't seem to get it to work properly. I have followed every guidline that apple mentions in their documentation for core bluetooth as well as the general documentation for state preservation.
For example here: Core Bluetooth Background Processing for iOS Apps and here: iOS State Preservation and Restoration
I can get the general state preservation to work on the device (for view controllers and objects etc.), but not for the bluetooth manager.
As far as I know the cheklist look like this:
Opt in preservation and restoration when you allocate and initialize a central manager object by assigning a restoration identifier in the options dictionary for the key CBCentralManagerOptionRestoreIdentifierKey.
Reinstantiate any central manager objects after your app is relaunched by the system. This is done in app delegate when the application:didFinishLaunchingWithOptions: is called. Here I am supposed to look for the UIApplicationLaunchOptionsBluetoothCentralsKey in the options dictionary and then reinstatiate the CBManager with that key. It is here where things go wrong since there are never any identifiers for that key, and thus I can not reinstatitate it.
Implement the appropriate restoration delegate method. I have done this step also, but since the manager is never re-instantiated I never receive this delegate callback.
The app is working fine in the background and I have followed all the steps for that part as well.
Now, having said all this, I am not entierly sure how to test this and that might be a part of the broblem. The way I do it now is to press the home button on the iOS device (actuall device) so that it puts the app into background and goes back to the home screen. While doing this I can tell that all the regular state preservation calls are acheived by looking at my log output. After this i quit the app by pressing the stop button in Xcode to kill the background process. I now restart the app through Xcode and once again I can now see all the regular state preservation code being executed and the state gets restored on everything except the bluetooth manager.
If this is wrong, then please let me know. But in all, I am very confused about this since it says in the Core Bluetooth docs that the preservation occurs only when "your app is relaunched by the system". What does that really mean? I also read a post on the Apple developer forum that since iOS 7 the OS will now never relaunch the app for any reason if the user kills the app manually which I am doing..
Any help regarding this would be much appreciated!
/A
When you click home button to send app to background, it it suspended, and can handle Bluetooth delegates and run in background for 10s, this feature could be realize by " add bluetooth central in background mode in info.plist", and do not use State Preservation & Restoration.
If your app is terminated by IOS, due to memory pressure, it can't handle bluetooth delegates anymore. In this case, if you used State Preservation & Restoration, your app can be relaunched to background to run again, also for only 10s. After 10s, it would move to suspended state.
Only in this situation, CBCentralManager's willRestoreState can be triggered.
You can add code
[kill(getpid(), SIGKILL);]
to a button action, when you click the button, your app will be terminated by IOS just like killed by memory pressure, and then "willRestoreState" will be triggered.
Good luck.
First, note that state preservation of view controllers has nothing to do with restoration of Core Bluetooth managers.
Important: Restoration doesn't work for scanning, static characteristics and generally any use cases that do not generate connection related events.
Now the steps:
Make sure any of the following on the tested app:
peripheral manager is advertising
peripheral manager has connected centrals
central is trying to connect to a peripheral
central is connected to some peripheral
Use this app to kill your app: https://github.com/ddaddy/BackgroundKill (kudos to ddaddy, give a star on the repo)
Switch to the killer app
Start the killing process and wait until it is terminated by the system
You app is now killed
Do some connection event
(Tested peripheral) subscribe to characteristics on the peripheral
(Tested peripheral) start read requests on dynamic characteristics
(Tested central) make the connection request succeed
(Tested central) update subscribed characteristics on the peripheral
Depending on what you want to test, consider the applicable points in the list. Use logging in your tested app and watch the logs in the organizer to see what happens over time.
I was able to get this working with scanning in the background (along with connecting and transmitting data). There are important difference between how your app was terminated and if iOS will preserve and restore your Core Bluetooth Manager.
Adding "App communicates using CoreBluetooth" to "Required background modes" in Info.plist covers most situations. Your central manager will continue to work in the background — either when the user presses the home button or locks the phone (or both). However you will never get the "willRestoreState" delegate call in these scenarios as your central manager is still being handled by your application (it's still in memory).
Preservation and restoration only comes into effect in one scenario — your app was terminated by iOS due to memory constraints. The easiest way to force this for testing it to load 3-4 memory intensive games while your app is in the background. If you review the device console, you're waiting for this message:
"Apr 4 13:16:47 Michaels-iPhone SpringBoard[58] <Warning>: Application 'UIKitApplication:com.oculeve.TearBud[0x6df4]' was killed by jetsam.”
iOS will take over you central manager at this point. If it was scanning, it will continue scanning, if it was connect to a peripheral, it will continue to be connected. In the event that you receive a central manager delegate call (didDiscoverPeripheral, didUpdateValueForCharacteristic) iOS will launch your application again in the background. At this point you will get the willRestoreState delegate call. At this point your app is back in memory and will work as describe above. Note that you need may need to do some restoration in willRestoreState if you were connected to a device. This is all covered in the WWDC 2013 Core Bluetooth video demo.
The kicker seems to be that restoration/preservation does not work if you manually close the app from the system tray (swiping up on your app). I'm assuming Apple's reasoning for that is in this case the user is explicitly closing the app and all Bluetooth communication should cease. This is also true is the user restarts their phone. I'm assuming this is because a restart is basically equal to swiping up to close all the apps in the system tray. If you get to this point, you can only reconnect once the user opens your application again.
Something to point out is that just because your app is in the system tray doesn't mean it's in memory.
Why Apple doesn't just tell you this in the documentation is beyond me.
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.