I want to detect motion activity when app is pause (not stop).
I used this code and it working if user use app:
let isActivityAvailable = CMMotionActivityManager.isActivityAvailable()
if isActivityAvailable {
CMMotionActivityManager().startActivityUpdates(to: OperationQueue.main) { (motionActivity) in
if (motionActivity!.confidence.rawValue > 0) {
if (motionActivity?.walking)! {
print("User is walking")
}
}
}
}
Apps can be in several states:
Running in the foreground
Running in the background (actively performing background tasks)
Suspended (still in memory but not getting processor time.)
Terminated. Suspended apps can be terminated at any time without warning.
Only the first 2 states will receive motion updates. To quote the docs:
...updates are not delivered while your app is suspended.
Apps only stay in state 2 (running in background) for a few seconds when the user swaps them out or locks their phone, unless you ask for background time. Background time is limited to a short interval (3 minutes?) except for a small set of app types like music players and turn-by-turn navigation apps.
Because of these things, the short answer is no, you can't receive motion updates while your app "is pause" because paused becomes suspended.
For your own use (or perhaps internal enterprise use) you could set up an app as a music player and have it receive motion updates continuously, but such an app would be rejected in app store review.
Related
As far as I know there are 3 states an app can be in.
a) Foreground
b) Background
c) Sleep (which is a state of the whole device)
The difference between the first two seems simple. But what exactly is the third state? And when does it happen? Is it when the device is locked?
And, quite importantly, does this happen after a long time, or does the device go to sleep if the user doesn't use it for a second, and reawakes the moment there's user action?
I've read different and seemingly contradictory pages about it, so either they were referring to different states or I just misunderstood what they were saying.
What am I trying to achieve: several things. But one of them is a timer that will execute an action every 10 seconds, and if the device was asleep so timers didn't run - I want it to run at the earliest possible time. Should I set a timer for every second and check if 10 seconds have passed (so that if a user checks the time on the phone for one second, the action will happen) or will it work automatically even if I set the timer for every 10 seconds?
The application lifecycle and the potential states your app can be in are more complex than you have stated.
Apple has comprehensive documentation on the application lifecycle and includes this diagram:
The main states you need to be aware of are:
Foreground Active - your app is actively executing in the foreground (ie it is the onscreen app)
Background - your app is actively executing in the background
Suspended - your app is in memory, but not currently executing.
It is important to note that your app only executes in the background for limited durations and under specific conditions.
It will execute in the background briefly as you transition from active to suspended in order to give you a chance to perform any required housekeeping prior to being suspended
It will execute in the background in response to specific background execution modes and permissions, such as location updates, Bluetooth or streaming audio.
When many people talk of their app "being in the background", what they really mean is that it is suspended. The Background state is an executing state while the Suspended state is a non-executing state. While suspended your app is still held in memory and will resume executing where it left off when it resumes an execution state.
Timers do not fire when your app is suspended. That is, expiration or firing of a timer will not bring your app from the suspended to background execution state.
Your best strategy for tracking missed events is to record when an event should fire (ie the fire Date) and check if that is the past when your app enters the Foreground Active state.
When your app is the foreground app and the device screen locks, timers will stop "ticking", so your strategy of setting a one second timer is probably the best approach for that scenario.
E.g. You have a 10 second timer, with 5 seconds elapsed when the device sleeps. After waking the device a further 5 seconds will elapse before the timer fires.
Note, however, there is a good chance that when the user does wake the device they will open some other app your app will be suspended.
You should also be aware that your app can be jettisoned (removed from memory) at any time when it is in the Suspended state if iOS needs memory. In this case when the user re-enters your app, it will restart from the top-left Unattached state and any in-memory variables will be lost.
I'm creating an application for my school which should check every n minutes if there is a new mark on the website.
To do this when the user login for the first time, the number of the actual mark is saved in the "UserDefaults". When app is terminated, after n minutes, the number of mark is recounted and compared with the previous one and send a notification in case the number is changed.
What I'd like to know if there is a way to perform this task. I've tried to create a timer in -applicationWillTerminate- but it's fired only once.
This is what I tried:
func applicationWillTerminate(_ application: UIApplication) {
DispatchQueue.main.async {
self.timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(AppDelegate.findMark), userInfo: nil, repeats: true)
self.timer.fire()
}
}
Selector findMark is the task.
Thanks in advance!
You have two options
Background App Refresh
Silent push notifications
Easiest one is Background App Refresh. Because later one needs a server to send the notification. You can check following API for the usage. Basically you set Background Fetch capability on Capabilities/Background Modes of your app. Then from time to time, iOS will wake up your app and call application(_:performFetchWithCompletionHandler:) delegate. You will have around 30-45 seconds to call your function and call completion handler. If you don't finish it on time, iOS will kill your app. If you don't obey the rules, iOS will give you less chances to wake up. For more detailed usage of Background Modes, you may check following tutorial
It's not possible to perform tasks like described in your question after the app is terminated. As described in the documentation:
App Termination
Apps must be prepared for termination to happen at any time and should not wait to save user data or perform other critical tasks. System-initiated termination is a normal part of an app’s life cycle. The system usually terminates apps so that it can reclaim memory and make room for other apps being launched by the user, but the system may also terminate apps that are misbehaving or not responding to events in a timely manner.
Suspended apps receive no notification when they are terminated; the system kills the process and reclaims the corresponding memory. If an app is currently running in the background and not suspended, the system calls the applicationWillTerminate: of its app delegate prior to termination. The system does not call this method when the device reboots.
In addition to the system terminating your app, the user can terminate your app explicitly using the multitasking UI. User-initiated termination has the same effect as terminating a suspended app. The app’s process is killed and no notification is sent to the app.
Link: https://developer.apple.com/library/content/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/TheAppLifeCycle/TheAppLifeCycle.html
Edit:
You cannot perform any task after the application is terminated. What you can do is get that calculation done from server side and send a Push Notification to the device.
Accutally , the answers is yes. But you should not.
We can use Location to archive your goal.
Accroding to offical document in here: https://developer.apple.com/library/archive/documentation/UserExperience/Conceptual/LocationAwarenessPG/CoreLocation/CoreLocation.html#//apple_ref/doc/uid/TP40009497-CH2-SW10:
Note: If your app is terminated either by a user or by the system, the
system doesn’t automatically restart your app when new location
updates arrive. A user must explicitly relaunch your app before the
delivery of location updates resumes. The only way to have your app
relaunched automatically is to use region monitoring or the
significant-change location service.
So we can archive it by using Starting the Significant-Change Location Service:
If you leave the significant-change location service running and your
iOS app is subsequently suspended or terminated, the service
automatically wakes up your app when new location data arrives. At
wake-up time, the app is put into the background and you are given a
small amount of time (around 10 seconds) to manually restart location
services and process the location data. (You must manually restart
location services in the background before any pending location
updates can be delivered, as described in Knowing When to Start
Location Services.) Because your app is in the background, it must do
minimal work and avoid any tasks (such as querying the network) that
might prevent it from returning before the allocated time expires. If
it does not, your app will be terminated. If an iOS app needs more
time to process the location data, it can request more background
execution time using the
beginBackgroundTaskWithName:expirationHandler: method of the
UIApplication class.
Call location update in willFinishLaunchingWithOptions and applicationDidBecomeActive Then you can excute your own code right after
[_locationManager startUpdatingLocation];
But is is extremly drain your battery, you may be rejected by app store.
I'm creating an application for my school which should check every n minutes if there is a new mark on the website.
To do this when the user login for the first time, the number of the actual mark is saved in the "UserDefaults". When app is terminated, after n minutes, the number of mark is recounted and compared with the previous one and send a notification in case the number is changed.
What I'd like to know if there is a way to perform this task. I've tried to create a timer in -applicationWillTerminate- but it's fired only once.
This is what I tried:
func applicationWillTerminate(_ application: UIApplication) {
DispatchQueue.main.async {
self.timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(AppDelegate.findMark), userInfo: nil, repeats: true)
self.timer.fire()
}
}
Selector findMark is the task.
Thanks in advance!
You have two options
Background App Refresh
Silent push notifications
Easiest one is Background App Refresh. Because later one needs a server to send the notification. You can check following API for the usage. Basically you set Background Fetch capability on Capabilities/Background Modes of your app. Then from time to time, iOS will wake up your app and call application(_:performFetchWithCompletionHandler:) delegate. You will have around 30-45 seconds to call your function and call completion handler. If you don't finish it on time, iOS will kill your app. If you don't obey the rules, iOS will give you less chances to wake up. For more detailed usage of Background Modes, you may check following tutorial
It's not possible to perform tasks like described in your question after the app is terminated. As described in the documentation:
App Termination
Apps must be prepared for termination to happen at any time and should not wait to save user data or perform other critical tasks. System-initiated termination is a normal part of an app’s life cycle. The system usually terminates apps so that it can reclaim memory and make room for other apps being launched by the user, but the system may also terminate apps that are misbehaving or not responding to events in a timely manner.
Suspended apps receive no notification when they are terminated; the system kills the process and reclaims the corresponding memory. If an app is currently running in the background and not suspended, the system calls the applicationWillTerminate: of its app delegate prior to termination. The system does not call this method when the device reboots.
In addition to the system terminating your app, the user can terminate your app explicitly using the multitasking UI. User-initiated termination has the same effect as terminating a suspended app. The app’s process is killed and no notification is sent to the app.
Link: https://developer.apple.com/library/content/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/TheAppLifeCycle/TheAppLifeCycle.html
Edit:
You cannot perform any task after the application is terminated. What you can do is get that calculation done from server side and send a Push Notification to the device.
Accutally , the answers is yes. But you should not.
We can use Location to archive your goal.
Accroding to offical document in here: https://developer.apple.com/library/archive/documentation/UserExperience/Conceptual/LocationAwarenessPG/CoreLocation/CoreLocation.html#//apple_ref/doc/uid/TP40009497-CH2-SW10:
Note: If your app is terminated either by a user or by the system, the
system doesn’t automatically restart your app when new location
updates arrive. A user must explicitly relaunch your app before the
delivery of location updates resumes. The only way to have your app
relaunched automatically is to use region monitoring or the
significant-change location service.
So we can archive it by using Starting the Significant-Change Location Service:
If you leave the significant-change location service running and your
iOS app is subsequently suspended or terminated, the service
automatically wakes up your app when new location data arrives. At
wake-up time, the app is put into the background and you are given a
small amount of time (around 10 seconds) to manually restart location
services and process the location data. (You must manually restart
location services in the background before any pending location
updates can be delivered, as described in Knowing When to Start
Location Services.) Because your app is in the background, it must do
minimal work and avoid any tasks (such as querying the network) that
might prevent it from returning before the allocated time expires. If
it does not, your app will be terminated. If an iOS app needs more
time to process the location data, it can request more background
execution time using the
beginBackgroundTaskWithName:expirationHandler: method of the
UIApplication class.
Call location update in willFinishLaunchingWithOptions and applicationDidBecomeActive Then you can excute your own code right after
[_locationManager startUpdatingLocation];
But is is extremly drain your battery, you may be rejected by app store.
Because the apple doc is quite vague concerning this point :
Upon being woken up, an app has around 10 seconds to complete a task
Does this mean that after 10 sec the app will go back to its "suspended" state ? Theoretically, is it possible to "force" the app to stay awake after these 10 sec ? (private app).
In my case the device would be connected to a car.
Does this mean that after 10 sec the app will go back to its
"suspended" state ?
If you read carefully :
Upon being woken up, the app has around 10 seconds to process the
data. Ideally, it should process the data as fast as possible and
allow itself to be suspended again. However, if more time is needed,
the app can use the beginBackgroundTaskWithExpirationHandler: method
to request additional time; it should do so only when absolutely
necessary, though.
Apps generally awake by Background modes(like location service, audio, push notification and so on) and for some modes like location service it will stay awake until the location data is being captured and for some, it doesn't stay awake for much time like push notification. So it depends on the task that is being performed. For Bluetooth, if it's connected with another Bluetooth device then it will stay awake.
Theoretically, is it possible to "force" the app to stay awake after
these 10 sec ? (private app).
Yes, it's possible.
Each of the preceding modes lets the system know that your app should
be woken up or launched at appropriate times to respond to relevant
events. For example, an app that begins playing music and then moves
to the background still needs execution time to fill the audio output
buffers. Enabling the Audio mode tells the system frameworks that they
should continue to make the necessary callbacks to the app at
appropriate intervals. If the app does not select this mode, any audio
being played or recorded by the app stops when the app moves to the
background.
Note : If you need it for your private app then you can check this here. Your app will be rejected if you add the solution and apply for appstore.
In XCode, go to your project->Capabilities->Background Modes and mark the Background Modes to On. Mark also Uses Bluetooth LE accessories:
Apps that work with Bluetooth peripherals can ask to be woken up if
the peripheral delivers an update when the app is suspended. This
support is important for Bluetooth-LE accessories that deliver data at
regular intervals, such as a Bluetooth heart rate belt. You enable
support for using bluetooth accessories from the Background modes
section of the Capabilities tab in your Xcode project. (You can also
enable this support by including the UIBackgroundModes key with the
bluetooth-central value in your app’s Info.plist file.) When you
enable this mode, the Core Bluetooth framework keeps open any active
sessions for the corresponding peripheral. In addition, new data
arriving from the peripheral causes the system to wake up the app so
that it can process the data. The system also wakes up the app to
process accessory connection and disconnection notifications.
For more detail you can refer Apple documentation.
Try this:
[[UIApplication sharedApplication] setIdleTimerDisabled:YES];
I have a iOS app that interact with a beacon.
Also have a function to detect the signal from a beacon and if the signal is out of range I want to store something in the database (in this case core data)
func updateDistance(distance: CLProximity) {
UIView.animateWithDuration(1.0) { [unowned self] in
switch distance {
case .Unknown:
self.view.backgroundColor = UIColor.grayColor()
self.distanceReading.text = "No Beacon Signal Detected"
updateDatabaseWithLocation()
}
}
}
So the app works fine as long as the app is running in foreground.
My understanding is I can also have the app in the background when user not actively using the app and still get signal from beacon.
If so, how can I accomplish this task? I added the following to info.plist and still didn't work.Also is it possible to update the database as well?
You can only range beacons (which is what gives you access to the distance approximations) when the app is running. As soon you leave the app (press the home button, navigate to another app, etc.), iOS will suspend the app, and ranging will stop working. Continuous usage of Bluetooth radio in the background would drain the smartphone's battery quickly.
You can, however, have your app subscribe to be woken up into the background when the smartphone enters and exits range of a beacon (or a group of beacons). This is called region monitoring, and it's the same mechanism that geofencing uses.
Now, "enter" and "exit" events in and on themselves won't give you access to distance approximations. However, since iOS will wake your app into the background for a few seconds to handle these events, ranging will actually resume for the duration (assuming you haven't stopped it before the app got suspended), before iOS puts the app back to sleep again.
You can even extend the "few seconds" into up to a few minutes with a background execution task.
All of the above doesn't require the use of background modes—only the "always" authorization to use Location Services.
You can't ordinarily keep the app running in the background indefinitely with beacons. Background support is heavily regulated by Apple, and is only allowed, e.g., for navigation apps, or music apps. People do on occasion try using the "location" background mode to keep the app alive in the background (and thus capable of ranging beacons), and some even reported being able to get it past the review process, but that seems to be more of an exception than a rule.
Should you decide to give it a try anyway, you need to:
enable the "location" background mode,
set allowsBackgroundLocationUpdates to true on your CLLocationManager instance,
start regular location updates: startUpdatingLocation.
This should keep the app running in the background even if you leave it.