In iOS11, how to keep a background task running past 10 min? - ios

My question involves keeping an app that monitors user interactions in the background, for example time spent in one or the app. The issue arises when you can not have a background process run for more than 10 min or violate Apple's sandbox restrictions. Since I am relatively new to the Apple API, and could not find a direct answer that didn't involve location services or VOIP (which are both interesting options, but my application will not be able to use either viably), I come to ask for options in which I can understand when another app opens, when it closes, when it fetches data, and when user holds phone in certain orientation (ie when people hold their phone at certain angles to read text and etc.) for certain amount of time.
The purpose of this analyzation is to predict an attention span for the user, so I need to be able to run in the background, as the user will not be using my app while it predicts attention span.
My thoughts on this are possibly accessing the system log, and somehow parse previous statements (I don't think sandbox will allow), but inevitably the iOS system will suspend my processes after some point unless I put a timer. There is also the option of having the system wake up my app via opportunistic fetching, but that won't be useful if I don't collect the data.
Keep in mind this is within IOS 11, so it is much more restrictive than previous iterations. I understand this may seem like a complex problem, but even a direction in which to head could be useful to me.

This solution might work, (not recommended since it drains the battery quicker).
Just update your current location, every 10 mins. It will reset the background thread timer.

Related

Avoid app throttling when Electron is in background

Consider the following example:
setInterval(function()
{
console.log(new Date());
});
If I run it with electron example.js under OS X, it opens up an icon in my dockbar and starts printing out the time on the console. If the app is not focused, however, after a while it starts throttling.
I looked around and found that this is due to OS X power saving strategy. Now, what if I needed it to keep working in background? My app will be a daemon doing a little bit of something every now and then, and I can't have my users blankly staring at my app for ages.
I found out here that I can do
electron.powerSaveBlocker.start('prevent-app-suspension');
Which actually fixes my problem. This however, is quite invasive, since as far as my understanding goes it prevents the system from sleeping at all! I don't need this much, I would just need my app to do something when the computer is active and online without forcing it to stay awake forever.
Isn't there anything in the middle, between having my users keeping the app continuously in the foreground, and making their computer sleepless forever?
As per the current docs:
Note: prevent-display-sleep has higher precedence over
prevent-app-suspension. Only the highest precedence type takes effect.
In other words, prevent-display-sleep always takes precedence over
prevent-app-suspension.
For example, an API calling A requests for prevent-app-suspension, and
another calling B requests for prevent-display-sleep.
prevent-display-sleep will be used until B stops its request. After
that, prevent-app-suspension is used.
What this means is that setting prevent-app-suspension to on, will still allow the system to sleep, and simply does what you desire it to. You can however run the function twice, passing both flags, turning both options on. However, as the docs above say, if both are set to on, then the system will not sleep until that flag has been removed.

battery consuming tasks when launched in the background

Apple states that the app using the background mode shouldn't perform expensive, battery consuming tasks when launched in the background. What exactly is considered a batter consuming task? To be specific: is searching and array of 100 entries acceptable? What about 1000?
A battery-consuming task is just that - a task that uses so much CPU that it makes considerable "dent" on the battery, as measured by the "battery percentage" screen. To that end, searching 100, 1000, or 1000000 items a single time is unlikely to do any damage. On the other hand, searching a 10-item list fifty times a second is very likely to make your task a high energy consumer. Same goes for downloading data multiple times per minute, using location services, etc.
A rule of thumb is very simple: go to the "Last 24 hours" view of the "Battery percentage" screen, and see if your app is listed there. If it is not there, of if it is below "Home & Lock Screen", your app is fine.
The remark from the guidelines you are referencing is meant to keep people form draining a user's battery in the background.
As a lot of applications use GPS and/or radio in the background and those are way more power consuming than searching tiny arrays, searching some array will probably be fine, as long as it has a reasonable size.
You shouldn't create an app that helps SETI or Folding or searches for the next biggest prime - or excessively use radio. But for small tasks like your's, this guideline is not to be concerned. Yet, this is only an estimate and in the end, the review process will decide on this on a case by case basis.
The general rule of thumb from apple is:
Always try to avoid doing any background work unless doing so improves the overall user experience. An app might move to the background because the user launched a different app or because the user locked the device and is not using it right now. In both situations, the user is signaling that your app does not need to be doing any meaningful work right now. Continuing to run in such conditions will only drain the device’s battery and might lead the user to force quit your app altogether. So be mindful about the work you do in the background and avoid it when you can.
So the question for you to decide if this is okay is: Does the user expect that to be done right now? rather than does that use too many battery?. If searching that array cannot be deferred to when the app is active again (I cannot see a reason for not deferring it, but there might be), you are fine searching the array in background.

Can I prevent an iOS user from changing the date and time?

I want to deploy managed iOS devices to employees of the company, and the app they will use will timestamp data that will be recorded locally, then forwarded. I need those timestamps to be correct, so I must prevent the user from adjusting the time on the device, recording a value, then resetting the date and time. Date and time will be configured to come from the network automatically, but the device may not have network connectivity at all times (otherwise I would just read network time every time a data value is recorded). I haven't seen an option in Apple Configurator to prevent changing the date and time, so is there some other way to do this?
You won't be able to prevent a user either changing their clock or just hitting your API directly as other commentators have posted. These are two separate issues and can be solved by having a local time that you control on the device and by generating a hashed key of what you send to the server.
Local Time on Device:
To start, make an API call when you start the app which sends back a timestamp from the server; this is your 'actual time'. Now store this on the device and run a timer which uses a phone uptime function (not mach_absolute_time() or CACurrentMediaTime() - these get weird when your phone is in standby mode) and a bit of math to increase that actual time every second. I've written an article on how I did this for one of my apps at (be sure to read the follow up as the original article used CACurrentMediaTime() but that has some bugs). You can periodically make that initial API call (i.e. if the phone goes into the background and comes back again) to make sure that everything is staying accurate but the time should always be correct so long as you don't restart the phone (which should prompt an API call when you next open the app to update the time).
Securing the API:
You now have a guaranteed* accurate time on your device but you still have an issue in that somebody could send the wrong time to your API directly (i.e. not from your device). To counteract this, I would use some form of salt/hash with the data you are sending similar to OAuth. For example, take all of the parameters you are sending, join them together and hash them with a salt only you know and send that generated key as an extra parameter. On your server, you know the hash you are using and the salt so you can rebuild that key and check it with the one that was sent; if they don't match, somebody is trying to play with your timestamp.
*Caveat: A skilled attacked could hi-jack the connection so that any calls to example.com/api/timestamp come from a different machine they have set up which returns the time they want so that the phone is given the wrong time as the starting base. There are ways to prevent this (obfuscation, pairing it with other data, encryption) but that becomes a very open-ended question very quickly so best asked elsewhere. A combination of the above plus a monitor to notice weird times might be the best thing.
There doesn't appear to be any way to accomplish what you're asking for. There doesn't seem to be a way to stop the user from being able to change the time. But beyond that, even if you could prevent them from changing the time, they could let their device battery die, then plug it in and turn it on where they don't have a net connection, and their clock will be wrong until it has a chance to set itself over a network. So even preventing them from changing the time won't guarantee accuracy.
What you could do is require a network connection to record values, so that you can verify the time on a server. If you must allow it to work without a net connection, you could at least always log the current time when the app is brought up and note if the time ever seems to go backwards. You'll know something is up if the timestamp suddenly is earlier than the previous timestamp. You could also do this check perhaps only when they try to record a value. If they record a value that has a timestamp earlier than any previous recorded value, you could reject it, or log the event so that the person can be questioned about it at a later time.
This is also one of those cases where maybe you just have to trust the user not to do this, because there doesn't seem to be a perfect solution to this.
The first thing to note is that the user will always be able to forge messages to your server in order to create incorrect records.
But there are some useful things you can use to at least notice problems. Most of the time the best way to secure this kind of system is to focus on detection, and then publicly discipline anyone who has gone out of their way to circumvent policy. Strong locks are meaningless unless there's a cop who's eventually going to show up and stop you.
Of course you should first assume that any time mistakes are accidental. But just publicly "noticing" that someone's device seems to be "misbehaving" is often enough to make bad behaviors go away.
So what can you do? The first thing is to note the timestamps of things when they show up at the server. Timestamps should always move forward in time. So if you've already seen records from a device for Monday, you should not later receive records for the previous Sunday. The same should be true for your app. You can keep track of when you are terminated in NSUserDefaults (as well as posting this information to the server). You should not generally wake up in the past. If you do, complain to your server.
Watch for UIApplicationSignificantTimeChangeNotification. I believe you'll receive it if the time is manually changed (you'll receive it in several other cases as well, most of them benign). Watch for time moving significantly backwards. Complain to your server.
Pay attention to mach_absolute_time(). This is the time since the device was booted and is not otherwise modifiable by the user without jailbreaking. It's useful for distinguishing between reboots and other events. It's in a weird time unit, but it can be converted to human time as described in QA1398. If the mach time difference is more than an hour greater than the wall clock time, something is weird (DST changes can cause 1 hour). Complain to your sever.
All of these things could be benign. A human will need to investigate and make a decision.
None of these things will ensure that your records are correct if there is a dedicated and skilled attacker involved. As I said, a dedicated and skilled attacker could just send you fake messages. But these things, coupled with monitoring and disciplinary action, make it dangerous for insiders to even experiment with how to beat the system.
You cannot prevent the user from changing time.
Even the time of an Location is adjusted by Apple, and not a real GPS time.
You could look at mach kernel time, which is a relative time.
Compare that to the time when having last network connection.
But this all sounds not reliable.

how to use GANTracker for long running iOS apps, including background audio

My App is typically run overnight as a baby monitor, either as foreground app, or with background audio running.
Goals:
Track total app startups ie. active user count.
Track total usage time in foreground vs background and total session time.
Track various page-views if they navigate the settings screens.
As recommended, I start the tracker in didFinishLaunchingWithOptions, and track my first ViewController as my first 'page-view'. My App might stay on this page then for the next 8 hours...
A couple of issues then appear:
When do I call stopTracker and what does it do? I'm hoping that it terminates the tracking session. But since google kindly hid their code in a static lib, I have no idea what's going on under the covers, and the .h doesn't say much. First instinct is to put stopTracker in applicationWillResignActive however, if the user decides to enable background audio my app is still running...
Next I read that a session can timeout after 30mins with no new pageviews, or at midnight. I could set a repeating timer to send the same page-view every 20mins, that should keep my session alive, at least until midnight, but then my page views are going to be much larger? unless it's smart enough to know I'm on the same page with every call. google analytics blog
[Update: each call seems to be counted as a new pageview, and numbers are thus skewed, so still an issue how to handle this]
If my timer above runs past midnight and the session has expired, I'm going to end up with a new session and double the actual active user count?
If I do call stopTracker in applicationWillResignActive, will the next call to track a page-view restart the tracker? or do I need to call startTrackerWithAccountID again?
If instead I start the tracker in applicationDidBecomeActive, I lose the session that might have been running in the background.
[update: this seems to be the best approach so far, but testing is very slow due to time lag on analytics reports, I will report back soon]
PS EasyTracker doesn't seem to handle this any better.
I got this working by using a pageview called 'Backgrounded', and when the user has selected no background functionality, then instead the app is calling stopTracker. I see multiple hits, with an average session of 20mins, but i can multiple pageview by time to see total time for goal 2. I found two solutions for goal one, events (which were not exposed in easy tracker), and also in my applicationDidBecomeActive (if it's not a restore of backgrounded app) then i track a pageview for AppStarted. I ended up wrapping the whole thing in a utility class and rolled it into a couple of my apps, so will be interesting too see the results. If anyone else tries this, you might want to think about using the custom variables too. I added my app version to this, so I can also monitor how many users are migrating to the latest app releases.

consistency of regionMonitoring

I am curious about why my app does not notify me sometime when I set it to be notified at same spot everyday. On iPhone 4x devices, the app uses regionMonitoring. Somedays the update is received, some days it is not.
Moreover, with significantLocationChanges also (on 3GS), the updates are not received consistently.
1) As I understand, both significantLocationChange and regionMonitoring receive updates when the device is handed off from one cell tower to the next one. Does it mean that users who live in area where the cell towers are not close by, the app will not work?
So, when there are no other parameters that can change, I am really puzzled by this behavior.
2) Does it mean that users who live in area where the cell towers are not close by, the app will not work?
3) Can I rely on regionMonitoring to notify the user consistently? What is the recommendation?
Regards. Sam.
regionMonitoring benefits from a couple other inputs to location monitoring. It triggers based on WiFi connections as well as other applications using location. Any location updates the OS receives will get run for any outstanding regions being monitored. The older significantLocationChanges is basically stuck to cell tower handoffs, and is generally less reliable in sparse cell areas.
Doesn't mean it won't work, it will just be less effective or useful.
My experience says that the regionMonitoring is the most consistent and reliable way to monitor locations without actively using GPS. It is by no means perfect, but for the majority of users, it should work with little to no issues.
I have an app that uses region monitoring and I haven't had any major complaints about accuracy at all. I made the decision to not support the 3GS and the older method for 2 reason. Few users using it, and getting smaller by the day. Secondly, it is a lot more code to support for much less accuracy and I didn't want it to detract from the overall experience, so I left it out. Hope this helps.

Resources