My understanding of iOS state management is that when the user hits the home button, the app becomes inactive, then enters the background, and then after a few seconds is suspended. A suspended app is then terminated if the system needs to free memory or if the user swipes the app away from the recents list.
My question is, is there any way for me to tell that my app has left the background state and entered the suspended state? I'm aware of the application delegate methods like applicationDidEnterBackground
etc, but is there a way that I can tell the app was suspended? Am I correct in thinking that being suspended is not the same as being terminated?
My context for asking this question is that I'm creating an audio player app. I've enabled background audio in Info.plist
and so when audio is playing and I press the home button I can see that the app remains in the background indefinitely (which is good). However, when audio is not playing there's no need to keep the app in the background and, as far as I understand it, the app ought to suspend. I want to be able to check whether or not this is happening!
Thanks very much - and do correct any misunderstandings I have.
You can appeal your app's removal from Google Play. We will reinstate apps in appropriate circumstances, including if an error was made and we find that your app does not violate the Google Play Developer Program Policies and Developer Distribution Agreement.
Print. Modified on: Mon, Dec 14, 2020 at 4:17 PM. When the app is running in the background (for both iOS and Android), the operating system may suspend it. When that happens, the data from your devices will NOT sync.
However, even if the rating of the short-video platform falls to 1 star it is highly unlikely that it will be removed from Google Play store. “An app with 1 star rating will still be available for download even after it gets poor ratings.
Active - the normal state of "in use" for an app. Background - the app is no longer on-screen but is still executing code. Suspended - the app is still resident in memory but is not executing code.
iOS doesn't report when an app will be suspended (placed from background into a non-processing state) nor does it seem to fire a notification once the app has resumed. There is some confusion about this as there is a notification when the app becomes "active" or will resign the "active" state, however this is not always the right value needed. iOS Apps have a number of states:
In order to capture when app is suspended, you'll have to capture when app is being put into background and use values like backgroundTimeRemaining
to estimate when a suspension will occur. Actual suspension can only be calculated as a "gap" in run loop processing, which can be done with a scheduled recurring timer and subtraction. I've created a helper class for this:
https://gist.github.com/BadPirate/0a480b947744c8c0e326daa4ab479b09
import UIKit
import Foundation
internal extension Notification.Name {
static let applicationWillSuspend = Notification.Name("application-will-suspend")
/// This notification gets called after the fact, but the `object` parameter is set to the `Date` of when the suspend occurred
static let applicationDidSuspend = Notification.Name("application-did-suspend")
static let applicationDidUnsuspend = Notification.Name("application-did-unsuspend")
static let suspendStatusRecorderFailed = Notification.Name("suspend-status-recorder-failed")
}
internal class SuspendStatusRecorder {
private var timer : Timer?
private var task : UIBackgroundTaskIdentifier = UIBackgroundTaskIdentifier.invalid
/// Start monitoring for suspend
/// - parameter stallThreshold: Number of seconds of no processing before reporting a stall event
internal func start() {
stop() // If already going.
startTask()
let timer = Timer(timeInterval: 1, repeats: true) { [weak self] (_) in
self?.checkStatus()
}
RunLoop.main.add(timer, forMode: .common)
}
internal func stop() {
if let timer = timer {
timer.invalidate()
self.timer = nil
}
endTask()
}
private var lastPing : Int = 0
private func willExpire() {
endTask() // Allow app to suspend
NotificationCenter.default.post(name: .applicationWillSuspend, object: nil)
expectingSuspend = true
}
/// Set to an uptime value for when we expect our app to be suspended based on backgroundTimeRemaining
private var expectingSuspend = false
private func checkStatus() {
let ping = uptime()
if expectingSuspend {
if ping - lastPing > 3 ||
UIApplication.shared.applicationState == .active
{
// Timer stalled, either CPU failure or we were suspended.
NotificationCenter.default.post(name: .applicationDidSuspend, object: Date(timeIntervalSinceNow: TimeInterval(lastPing - ping)))
NotificationCenter.default.post(name: .applicationDidUnsuspend, object: nil)
expectingSuspend = false
startTask() // New background task so that we can make sure to catch next event
}
}
lastPing = uptime()
// In background, time is going to expire (resulting in suspend), report and end task
if UIApplication.shared.applicationState == .background &&
UIApplication.shared.backgroundTimeRemaining != Double.greatestFiniteMagnitude &&
task != UIBackgroundTaskIdentifier.invalid
{
willExpire()
}
}
private func endTask() {
if task != UIBackgroundTaskIdentifier.invalid {
UIApplication.shared.endBackgroundTask(task)
self.task = UIBackgroundTaskIdentifier.invalid
}
}
private func startTask() {
task = UIApplication.shared.beginBackgroundTask(expirationHandler: { [weak self] in
self?.willExpire()
})
}
private func uptime() -> Int {
var uptime = timespec()
if 0 != clock_gettime(CLOCK_MONOTONIC_RAW, &uptime) {
NotificationCenter.default.post(name: .suspendStatusRecorderFailed, object: "Could not execute clock_gettime, errno: \(errno)")
stop()
}
return uptime.tv_sec
}
deinit {
stop()
}
}
You don't get a notification about being suspended:
https://developer.apple.com/library/ios/documentation/iphone/conceptual/iphoneosprogrammingguide/ManagingYourApplicationsFlow/ManagingYourApplicationsFlow.html
"Suspended: The app is in the background but is not executing code. The system moves apps to this state automatically and does not notify them before doing so. While suspended, an app remains in memory but does not execute any code.
When a low-memory condition occurs, the system may purge suspended apps without notice to make more space for the foreground app."
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With