I am fetching items asynchronously using blocks from different sources like …
EKEvents Reminders
Remote JSON feed using NSURLSession
… also I am fetching EKEvents calendar events in the classic synchronous way.
After the data fetching for each source is finished I post a notification and immediately add respective items as subviews.
The problem: Items/views only get displayed after a delay of a couple of seconds, sometimes even longer (long after getting the actual result response). This is not the case for the synchronously fetched items like for calendar events – they show up immediately.
(Curiously using the new Xcode6 live view debugger I can see the new item views there but not in the actual simulator or device.)
Could this have something to do with async fetches not happening on the main queue?
Is there anyway to change this async behaviour have all results show up as generated views immediately?
When you post the notification, are you dispatching that to the main queue? If not, does your handler dispatch the UI updates to the main queue?
When you post a notification from a background thread, the handler is called on a background thread. Thus, if you try to do UI updates in response to a notification sent from a background thread without dispatching the UI updates back to the main queue, you'll see the sort of behavior you describe.
Related
I'd like to get UIApplication.shared.applicationState from a background thread. If I try to get the application state from a background thread, I get errors when accessing it since it's not the main thread (since it's a UIKit API).
The reason I'm doing this is so I can log events that also includes information such as the current application state. Logging events for me is happening in the background so it does not lock up the main thread.
Is there an alternative for getting the application state within a background thread?
Setup notifications for changes to the state on the main thread and assign them atomically to a variable. That variable can now be accessed atomically as well from your background thread.
I am creating a simple watch kit app that shows me the local bus schedule. To get the schedules I have to make a request to the bus company's website and parse the HTML. (They have no public api....)
Alamofire is used to make a request and then parsing the HTML with SwiftySoup. The data is then saved into an array with structs and the table is created. Then the array is saved to a JSON file. So when the app is force closed and starts again, the app checks if the current time is past the first bus time, if true the app request the website again to get the next schedules otherwise it just creates the table with the number of schedules.
Since Alamofire is doing an asynchronous request, the app finishes "starting" even if it does not have any data. How can I "force" the app to wait for the request and parsing to be finished before displaying the UI?
As Josh Caswell mentioned it is not possible. So created UI for the loading state.
If the app did not have any schedules cached or the cached data was old, the app pushed the loading interfacecontroller. When the loading was finished the interfacecontroller poped itself and the main interfacecontroller initilized with the corred UI.
I use HKObserverQuery and background delivery in my iOS application. In the updateHandler of HKObserverQuery I execute a query (HKAnchoredObjectQuery) and send results to the remote server via https. But on a slow connection (EDGE for example) and with big amount of data (steps for example) data sending may take up to one minute.
From the documentation to HKObserverQueryCompletionHandler:
When HealthKit wakes your app, it calls the update handler on any observer queries that match the new data. 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 back off algorithm. If your app fails to respond three times, HealthKit assumes that your app cannot receive data, and stops sending you background updates.
So my question is: How much time do I have to call HKObserverQueryCompletionHandler before my app is assumed by HealthKit as "hanged"?
Or should I call HKObserverQueryCompletionHandler immediately and run a new long-running background task for https request instead?
You should definitely start a separate background task to perform the transfer of data to your server and call the HKObserverQueryCompletionHandler as soon as possible. The documentation gives no indication as how long you can wait before calling the handler so the safest thing to do is call it as soon as possible.
I posted a similar question about confusion around use of HKObserverQueryCompletionHandler here, but there haven't been any updates.
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.
I'm working on an iOS app, uploading videos from the Camera Roll, using NSURLSession with a background configuration. The user can queue up multiple videos for upload (the queue is executed serially).
A single upload consists of:
Getting an AVURLAsset reference to the PHAsset using PHImageManager's requestAVAssetForVideo method.
Exporting the resource to a temp directory (because you cannot upload straight from the AVURLAsset's URL).
Uploading the resource using an NSURLSessionUploadTask
I can queue up multiple videos and the process works well in the foreground. They complete one after another.
But if I queue up several videos and then background the app. As soon as execution reaches the exportAsynchronouslyWithCompletionHandler: stage it stalls until I foreground the app again. (I know this because I'm posting debug statements in local notifications, visible on the lock screen).
Is it possible to use exportAsynchronouslyWithCompletionHandler: when the app is backgrounded?
Edit 1
I've tested this while connected to the debugger and while not, the app never executes the copy command. But does so only when the app is foregrounded again.
Edit 2
I posted a similar question about whether using NSFileManager's copyItemAtURL:toURL:error: is a viable alternative (but I'm seeing the same behavior so don't think it is).
In general, if you need just a little time (up to a few minutes) to finish up some tasks even after the user leaves the app, you just request this from the OS. See the Executing Finite Length Tasks section in the Background Execution Chapter. So, begin the background task when you call exportAsynchronouslyWithCompletionHandler, and end it in the completion handler for that method.
If you are also using a background NSURLSession. In that case, if the app is not in foreground when the tasks finish, it calls the app delegate's handleEventsForBackgroundURLSession method, which passes a completionHandler block. One must:
Save the completionHandler provided to handleEventsForBackgroundURLSession;
Instantiate the NSURLSession with the same background identifier as the original background session;
Let the session call the appropriate delegate methods for the completion of the tasks; and
The session will call URLSessionDidFinishEventsForBackgroundURLSession when they're all done, at which point you'd generally call the completionHandler we originally received in the app delegate.
In your case, you will want to defer the call to the saved completionHandler until after all of the asynchronous exportAsynchronouslyWithCompletionHandler handlers are done, too. There are bunch of ways you could do that (e.g. dispatch groups, etc.), but hopefully that illustrates the moving parts involved in this process.