I am using Local Notifications with UNNotificationAction, when the user triggers the action I need to send an API request to the server.
When the device is on Low power mode and Background App Refresh is turned off and the app is killed, I get a weird behaviour on the action handler.
After the user taps the selected action on the notification - App Delegate didFinishLaunchingWithOptions() start to process but never finish.
It seems like the process is suspended in the middle of this function.
This behaviour also stops the handler function of the Notification Action.
After monitoring with logs (debugging from Xcode it always works), I see the state of the application is background on startup.
Also, I could not see any logs that indicate crush or process suspension
I could not find any documentation of handling such a case.
This is quite a long shot, and I don't know if it will solve your issue, but remember that there are limitations regarding what can you do inside the didFinishLaunchingWithOptions method. IIRC, if that method takes more than 10 seconds to execute, the application gets killed (the system thinks something went reeeally ballistic). So, if you need to trigger something heavy through that method, make sure you run it asynchronously.
Related
I can't seem to find the right documentation, tutorial or SO post to point me towards the right implementation so far... even though there are many posts about this topic floating around.
Problem
I have an app that connects (pairs) to a BT device I built. What I want, is when the user presses the home button or backgrounds the app I want to send an update through a BT service layer to a BT device on a regular interval. I am doing this now (only fires once) in the applicationWillResignActive and applicationDidEnterBackground methods and it works perfectly. The problem is that I can only perform this once it seems. Any loops, timers, background type services that I start up in these methods, die very soon there after.
What I need
Is for the application to keep looking for my BT device in the background and every 10 seconds or so and send an update to the device. I first need to check to make sure the app is still backgrounded, but once I know its in the background I wanna send the update. When the app comes back in focus I can stop the updates to the BT device but its crucial that it sends them when the app is not in focus.
Research
I have found these SO posts (long-running tasks, background task execution handler, using this in an NSTimer loop... but it dies) but they have not helped with implementation at all.
When I wrote this post I was far too new to OBJ-C and didn't quite understand the concept of delegate methods with the proper access level to fire in the background (i.e. info.plist access under bluetooth-central).
The end result a month later was to build a peripheral device that could wake up the application in a timed loop via subscribed characteristic updates. Once the app wakes up, you have 10 seconds to handle the event that has just occurred and since I am only saving off a copy of the data that the device posted, all is well.
HKObserverQuery has the following method that supports receiving updates in the background:
- initWithSampleType:predicate:updateHandler:
The updateHandler has a completionHandler which has the following documentation:
This block is passed to the update handler. You must call this block
as soon as you are done processing the incoming data. Calling this
block tells HealthKit that you have successfully received the
background data. If you do not call this block, HealthKit continues to
attempt to launch your app using a backoff algorithm. If your app
fails to respond three times, HealthKit assumes that your app cannot
receive data, and stops sending you background updates.
From looking at other posts it seems like there's a lot of confusion revolving around this handler. Below are some questions that I have about it:
When should the handler be called? If called too late, then HK might think that the app never received the query update causing you to hit the background update 3-strikes back-off algorithm. The documentation states that it should be called after handling other queries. Depending on how long it would take to run those queries, it sounds like you could get dangerously close to hitting the background update strikes.
Why is this needed? Shouldn't the system know that the app has been launched and has received the background update? When using CoreBluetooth in the background it just wakes your app up in the background for 10 seconds. No need to call any handler or deal with the background update 3-strikes.
If you hit the background update 3-strikes and HK stops sending updates is that permanent? Does HK ever start sending the background updates again? What if there's a bug that prevented the handler to be called and now you've fixed it. Is the app stuck never receiving the updates? Or will it reset when the app is re-launched or updated?
Does HK keep your app running in the background until the handler is called? Is that part of its purpose or just a side effect? If it's part of its purpose how long can we run before needing to stop (and hit the first background update strike)?
When should the handler be called?
Call it after you are done your job. Your code should not do complex operations. The app is in the background and the user does not see what's changed. You can just set a "flag" that data is updated and do complex operations after the user launched the app. If your decision about either notifies the user or not based on complex operations, then try to refactor code so that all necessary data is pre-calculated (e.g. in UserDefaults) and extra data is simply fetched with that data. So, 1-2 seconds is enough for your calculation.
Why is this needed?
All such handlers have completion closures. They are needed for iOS to know if your app works fine. If your app will eat too much CPU time, then iOS could become slow. Hence, Apple wants to be sure that iOS works fine despite bad apps.
If you hit the background update 3-strikes and HK stops sending updates is that permanent?
No.
Does HK ever start sending the background updates again?
Yes. But it depends on many factors. It may try to call your app again in 1-2 days. If nothing changes it will call it rarely.
Does HK keep your app running in the background until the handler is called?
This is unknown. It depends on many factors. Probably if iPhone is charging it will allow running your app longer just to estimate if the completion handle is called or not. If your iPhone is not charging and closed to 0% battery, then more likely iOS will kill your app. So, you should not do any job after you called the completion handler. And try to keep it simple.
Recommendations
You should process new data as quickly as possible. If you need to fetch a lot of data, then try to optimize this and pre-calculate it when the app is in foreground, then save somewhere (UserDefault), and use new data with cached data to make a decision (e.g. notify user about something; I believe you need background updates exactly for that).
1-2 seconds or less is a good time for background updates.
In my app I use background services so the method applicationWillTerminate: will not get called when the user terminated the app. Instead the method applicationDidEnterBackground: gets called when the user pushed the app into to background and when the user terminates the app completely.
The problem I am having is that I need to be able to distinguish between when the user has pushed the app into the background and when he actually quits the app because I have different code that needs to be fired for both events.
Is this possible in my case?
The -applicationWillTerminate: method is called when the system is "nicely" terminating the application in an orderly fashion (ie, it's not being backgrounded for pending tasks or it told the OS those tasks are completed). This is distinctly different from "the user forcibly terminated the app", in which case your app won't receive any messages because it's just been summarily executed by a bullet to the head.
Are you certain you're correctly / fully participating in the background services? That is, are you certain you're correctly telling the system you've finished? You should edit your question and post the relevant backgrounding code. For example, if you're using -beginBackgroundTaskWithExpirationHandler:, the docs say:
If you do not call endBackgroundTask: for each task before time expires, the system kills the app.
...could this be your issue?
This is maybe not possible, but the only way I can imagine to distinguish between both of them is waiting to the next event.
When the next event is a "start", you know that the last event was a kill.
When the next event is a "foreground", you know that the last event was a background.
This implies some processing at server, I know.
Hope this helps.
I have been dealing with this issue for weeks and am unsure if it is my code's fault, forge's appPaused event not triggering quickly enough, or Trigger.io's documentation not being nearly clear enough about just how little time iOS gives us to execute clean-up code.
According to the documentation regarding the appPaused event:
iOS: A short amount of time is given for execution, it is generally best to assume that callbacks and timers may not fire until the app is resumed.
My application deals with websockets and ideally I am able to send a close event to my server when a user minimizes my app, or the phone is locked. Currently, all of my clean-up code runs perfectly on Android, but on iOS, my clean-up code doesn't run until the app is resumed. The strange part is sometimes (maybe 1 out of 20 times) the iOS clean-up is correctly run right after the appPaused event is fired.
To test this I have done two things:
I make the very first thing to run after the appPaused event is fired is a message to my websocket server saying "the app is paused". 95% of the time, this message is not actually sent until the app is resumed, but the other 5% of the time my websocket server receives it right after I pause the app.
I then made it so the first thing to run after the appPaused event is fired is a line that stores Date.now() in a global variable. I then then store Date.now() in another global variable when the app is resumed, and find the difference between them. It gets interesting because around 50% of the time the Date.now() line is correctly fired right after the appPaused event is called, but the other half of the time the 2 Date.now() calls are only milliseconds a part, proving that the clean-up code was not run until the app resumed.
So, can I really only expect to sometimes have enough time after appPaused is fired to even store Date.now() in a variable? Is this what everyone else is experiencing when running their Trigger.io applications on iOS? Let me know if anyone could use more information.
Yes, appPaused is not guaranteed to fire on iOS. Note, that it may only fire when the app is resumed.
Any code you want to run on going into the background will probably not run. See the trigger io docs on events. The note on iOS needs careful reading.
That said I have a similar app to yourself dealing in websockets. What i've found is that I can leave the socket open, iOS automatically kills the socket when it suspends the app (Sometime after the paused event is received which corresponds to the ios background state). The server receives the close event and you can of course attach to that event to set any state or cleanup resources. Android requires a manual shutdown of the socket, but appPaused works in a guaranteed way when the app is put into the background.
I have seen several ways to "make sure that my app is shown again after a phone call which my app has initiated has ended" however that isn't what I want -> what I am looking for is a way to, say my app is currently running in the background doing network communications and someone calls me, I would like my app to either be able to "detect when the phone call has ended and resume my network communications" and/or "launch/resume my app back into the foreground when the phone call has ended". Is there any way to accomplish something like this and how?
This is unsupported within iOS.
From the docs:
http://developer.apple.com/iphone/library/documentation/iphone/conceptual/iphoneosprogrammingguide/BackgroundExecution/BackgroundExecution.html
http://developer.apple.com/iphone/library/documentation/NetworkingInternet/Reference/CTCallCenter/Reference/Reference.html#//apple_ref/doc/uid/TP40009604
If your application is active when a call event takes place, the system dispatches the event to your handler immediately. However, call events can also take place while your application is suspended. While it is suspended, your application does not receive call events. When your application resumes the active state, it receives a single call event for each call that changed state—no matter how many state changes the call experienced while your application was suspended. The single call event sent to your handler, upon your application returning to the active state, describes the call’s state at that time.
The best you can do is detect phone calls while your app is open.
applicationWillResignActive is called when the app is about move from active to inactive state. So if you get a call, this method is called before your app is backgrounded.
Use this method to pause any ongoing tasks and save any settings you need.
applicationDidBecomeActive is called when you app becomes active again.
Use this method to resume any tasks that were suspended. Example would be resuming your network communications