I am using transferUserInfo to send stats data to the watch.
When the watch is asleep or idling (showing clock), and the phone app is running, there will be many infos being transferred (triggered by a repeating timer).
How many "userinfo" queues can the app send? Is there a limit?
There is only one queue (provided by the system).
I'm not aware of any limit to the number of transferUserInfo messages that you can add to the queue.
However, you should take two things into account:
While iOS will send messages and be able to drain its queue, those sent (yet unprocessed) messages will be held by watchOS until your app can receive them. At some point, the watch may not be able to hold any more messages for your app.
While I don't know exactly how Apple handles that condition, you should plan that transfers may fail, or worse, be discarded.
Do not design your app around trying to skirt the limits of a particular system, as it will be fragile and break, especially if you don't have a contingency for handling possible situations that may arise.
If you expect to constantly send (a high number of) messages over a (short) period of time, this will definitely not be efficient in terms of memory or energy use.
You should follow Apple's performance tips to create a positive user experience. If it turns out that your app is responsible for poor phone and/or watch battery life, users will stop using it.
Better approaches
Here are a few alternative approaches to avoid chatty communication between the phone and the watch:
Use a different method such as updateApplicationContext so the phone and watch would only have to deal with a single context, instead of a long queue of messages that would have to be individually processed.
Maintain the stats on your phone; avoid transferring the current stats until the watch requests/needs them. (This is an excellent approach for watchOS 3, since your app can be updated in the background, before the user launches it).
Batch your data to send fewer updates over time.
If you're not updating a complication, don't plan to update an inactive app more than once per hour.
If you're updating a complication, definitely don't plan to update more than 4 times per hour (but preferably no more than once per hour).
Again, don't aim for any particular number. The less often you can update, the better, in general.
Whichever methods you choose, make sure your app is robust, and can handle any type of failure, including up to being terminated by the system (such as for excessive memory use, or excessive background CPU use).
For more information
These two watchOS sessions specifically cover when, why, and how to update your watch, and mention scheduling updates around particular usage. E.g., don't update transit information throughout the night, if transit isn't running.
A good introduction explaining when and why to update your watch app is covered in Designing Great Apple Watch Experiences.
For specifics, the Keeping Your Watch App Up to Date session covers everything you need to know to keep your complication, app, and dock snapshot up to date.
Related
I would like to know if there are limitations regarding how often I can execute something in the background. I found this library : background fetch and it says that on the iOS part I cannot execute it more often than 15 minutes.
Are there workarounds for this?
My purpose is to check in the background the available Bluetooth devices detected and to send them to a database.
I only have experience in android and I'm not sure how to convert it to iOS or if it is possible.
Yes, there are limitations imposed by iOS. It will decide when your app is given time to execute background tasks such as a fetch of web data. It can be more frequent than 15 minutes but on average it may be every 20 minutes at best. It might be two hours or more. We are not privy to the algorithm and cannot predict the result.
Known factors are power state, how well your app conforms to the rules and gets its work done in the time allotted, and some measure of how (and when) the user uses your app. Low battery? Fewer background tasks allowed. User never uses the app before 8AM? Few fetches called overnight. It's all beyond developer control.
A developer can use a timer to trigger a routine task but it's only relevant if the app stays active in the foreground.
The app I worked on got an alert from apple wallet: "Some passes are receiving too many updates, which may affect battery life. Automatic updates for *** passes will be disabled. Choose RE-enable if you want automatic updates to continue." I only have 2 passes in the wallet from that app.
Does anyone know what's the reason trigger this alert? If we send 2 push notifications within 5 seconds, will that trigger this kind of alert?
It means pretty much what it says. Apple is respectful of user's battery life and data consumption and recognises that a pass update is a high energy activity that the user has little control over, and so if a pass issuer makes too many updates, then iOS takes action to alert the device owner so they can choose if they would like to continue to receive these updates (at the expense of reduced battery life).
Apple's previous policy was to silently throttle updates, but this led to problems, since users were unaware that they were being throttled and would wonder why their passes were not updating.
Apple recognises that developers may need to push content more frequently, which is why you can disable rate limiting on your device via the developer menu (that appears when you have your iPhone hooked up to Xcode).
Apple doesn't publish the criteria that it uses to throttle, and normally a quick burst of activity (E.g. a one-off burst of 2 or 3 messages in the space of a few minutes) would be tolerated. But if the issuer is sending dozens of messages every few minutes, then you can almost certainly expect they will see this message.
Im quite new to iOS programming and now want to implement a periodic background synchronization to synchronize my server data with client data. What I want to achieve is comparable with Androids SyncAdapter where you can define a time interval (for example each 30 minutes) and the system will trigger the defined task automatically in the background.
Until now I could not find such mechanism for Swift 3.0 so I need to ask if somone has experience or some hints for me how I can achieve this.
What I want to do sounds quite simple:
When the app starts for the first time the app should setup a sync manager which automatically triggers a background task every 30 minutes. The background task is responsible to synchronize server and client data (using Alamofire).
How can I do that?
There is an iOS feature called BackgroundFetch, which you can set up for
regularly downloads and processes small amounts of content from the network
You can setup a minimumBackgroundFetchInterval.
In contrast to the mentioned Android feature, this interval is not guaranteed though.
The OS does some heuristic in a blackbox. It rewards you for using a "reasonable" (to the OS) CPU time/ power consumption and also for being used often by the user. On the other hand you get punished for draining the battery or (even worse) never being used/opened by the user.
See: Apple Sample and Apple Docs
Update: Since iOS13, BackgroundFetchis deprecated.
There is a similar, new API named BGTask, BGAppRefreshTask is the equivalent to deprecated BackgroundFetch.
See Apple Docs
Alternatively, depending on your needs, you can post a Silent (push) Notification whenever the users data changes on server side. A silent push wakes up your app without notifying the user, so you can fetch data and maybe inform the user by scheduling a local notification.
See: Apple Documentation
You can't. Apple doesn't allow 3rd party apps to have regular background time like that. You'll need to come up with another approach like implementing a silent push notification from your server when new content is available.
As #ekscrypto points out in their comment, you can use Background fetch to load small amounts of data when the system decides to fetch it. However, you don't have any control over when that fetching takes place. Search on "Fetching Small Amounts of Content Opportunistically" in the Xcode help system for more information.
I've read this and many other questions. I learned that there is no way to schedule task to run from background at a specific time.
My application uses CLVisit api to send location information to the server. But I want the user to be able to select a time range to send his/her check-ins. Is there any different way to stopMonitoringVisits() and startMonitoringVisits() at a specific time from background? Should my app always monitor and decide to send the location after receiving didVisit call?
Same question can be asked for startMonitoringSignificantLocationChanges()
As long as visit monitoring does exactly what your app needs, keep the monitoring always on and decide on proper handling (whether you should report location to server) when processing didVisit callback.
You are not going to put any noticeable stress on battery this way, as visit tracking is very battery friendly and iOS runs that kind of monitoring by default with or without your app.
Furthermore, any attempt to employ some "smart" tricks designed to circumvent the good practices, which are recommended by vendor and even enforced by API design, inevitably results in "bad practices", which in your case most likely will bring extra battery consumption.
The only adverse effect of keeping monitoring always on is the solid arrow in status bar, which is considered as a sign of high battery usage by many "educated" iPhone users. If this is the primary concern, and if your use case deals with quite a few time spans (for example, leaving office and home locations), you may try to use silent push notifications to activate the monitoring at around the right time.
After working with complications for a few days, I feel confident saying the following about the update process for updates that happen at a prescribed interval:
The system calls requestedUpdateDidBegin()
This is where you can determine if your data has changed. If it hasn't, your app doesn't have to do anything. If your data has changed, you need to call either:
reloadTimelineForComplication if all your data needs to be reset.
extendTimelineForComplication if you only need to add new items to the end of the complication timeline.
Note: the system may actually call requestedUpdateBudgetExhausted() instead of requestedUpdateDidBegin() if you've spent too much of your complication's time budget for the day. This is the reason for this question.
If you called reloadTimelineForComplication, the system will call getCurrentTimelineEntryForComplication (along with the future and past variants that get arrays, depending on your time travel settings)
This is conjecture as I haven't tested it yet, but I believe if you called extendTimelineForComplication that only the getTimelineEntriesForComplication(... afterDate date: NSDate ...) would be called.
The system will then call getNextRequestedUpdateDateWithHandler so you can specify how long until your complication requires a new update.
Apple's documentation is quite clear that you should not ask for updates too often, or conduct too much processing in the complication code or you will exhaust your time budget and your complication will stop updating. So, my question is: where and when do you do the update?
For context, my scenario is a URL with return data that changes up to two times per hour.
The most obvious place in which to put the URL fetch code is func requestedUpdateDidBegin() Fetch the data, store it, and if there's no change, just return. If there was a change then extend or reload the timeline.
However, a URL fetch can be costly. Alternatives:
Put the code on the phone app and send it over with a WCSession, but if the user closes that app then the updates will no longer happen.
Use push updates, but this isn't a web app, so I have no place to send them from.
Obviously I will update all the data when the user interacts with the watch app, but that now means it only gets updated when the user uses the app, which negates the need for a complication.
Is there anywhere else? Can I have a periodic function in the watch app that isn't part of the complication? Where is the right place to fetch the data for a complication update?
For watchOS 3, Apple recommends that you switch from using the complication datasource getNextRequestedUpdateDate scheduled update to update your complication.
The old way for watchOS 2
requestedUpdateDidBegin() is really only designed to update the complication. Keeping your complication (and watch app) up to date usually involves far more than reloading the timeline (and asynchronously retrieving data never fit in well with the old approach).
The new way for watchOS 3
The new and better approach is to use background refresh app tasks. You can use a series of background tasks to schedule and handle your app extension being woken in the background to:
Fetch new data
using WKWatchConnectivityRefreshBackgroundTask to obtain data from the phone, or
using WKURLSessionRefreshBackgroundTask to download data from a server
update your model once the data arrives,
update your complication from the model (by reloading or extending the timeline), and finally
update your app's dock snapshot to show the data on the dock
Call each tasks’s setTaskCompleted method as soon as the task is complete.
Other benefits of using app tasks
One of the key features about this design is that the watch extension can now handle a variety of foreground and background scenarios which cover:
initially loading data when your app/complication starts,
updating data in the background, when the extension is woken by a background task, and
updating data in the foreground, when the user resumes your app from the dock.
Apple recommends that you use each opportunity you are given regardless of whether your app is in the foreground or background to keep your complication, app, and dock snapshot up to date.
Are there any limitations?
The number of total available tasks per day is divided among the number of apps in the dock. The fewer apps in the dock, the more tasks your app could utilize. The more apps in the dock, the fewer you can utilize.
If your complication is active, your app can be woken up at least four times an hour.
If your complication is not active, your app is guaranteed to be woken at least once an hour.
Since your app is now running in the background, you're expected to efficiently and quickly complete your background tasks.
Background tasks are limited by the amount of CPU time and CPU usage allowed them. If you exceed the CPU time (or use more than 10% of the CPU while in the background), the system will terminate your app (resulting in a crash).
For more information
A good introduction explaining when and why to update your watch app is covered in Designing Great Apple Watch Experiences.
For specifics, the Keeping Your Watch App Up to Date session covers everything you need to know to keep your complication, app, and dock snapshot up to date.
WatchBackgroundRefresh sample code demonstrates how to use WKRefreshBackgroundTask to update WatchKit apps in the background.
Edit: El Tea (op) has posted a good answer at https://stackoverflow.com/a/32994055/630614
This is an interesting question/problem, and I've been wondering about a lot of the same!
For the most part, it seems that when I'm working on a new complication I need to step back and see when I really want to update it. A "countdown" complication could set all future timeline entries at one time, when the "end date" is set. An app that shows the current status of a web service could have relevant data stored in NSUserDefaults when an APNS comes through.
If you don't have access to APNS, don't want to run your iOS app in a background mode, and don't want to make HTTP requests from Apple Watch, I can think of 2 other options.
1) Schedule local notifications. The good part is that your Apple Watch should run didReceiveLocalNotification, but the bad part is that the user will get a notification when you're simply trying to check the status without a disruption.
2) Send a message to iOS via sendMessage(_:replyHandler:errorHandler:) in your reloadTimelineForComplication method, setting nil for the replyHandler to make it as quick as possible:
Calling this method from your WatchKit extension while it is active and running wakes up the corresponding iOS app in the background and makes it reachable.
Your iOS app could perform whatever network requests are needed and then store the information or push it to Apple Watch. Unfortunately, I don't think the watch extension will have it's session.didReceive... called until you run it, but you could access the data on the next call to requestedUpdateDidBegin.
As I said, I'm very interested in this same thing, so post some thoughts back and maybe we can extrapolate on some best practices here.