Swift Newbie: porting Objective-C AppleHealth integration code to Swift that is invoked by Flutter/Dart. When I background or lock-screen the legacy Obj-C app, it pretty much immediately suspends all execution. However the same behaviour doesn't happen in my Swift code port, I'm using the same DispatchQueue in Swift as in the legacy Obj-C app,
The reason it's important to suspend, is that once a user locks the iPhone screen, AppleHealth encrypts all its data and it is unavailable. Also is my understanding correct that when you suspend a DispatchQueue the currently executing block will complete, but subsequents blocks won't start execution. As far as I can tell my, Swift code port mimics the Obj-C logic, any hints as to why it behaves differently or what I may be missing would be greatly appreciated.
I'm dispatching using
DispatchQueue.global(qos: .default).async {
/* code */
}
If could get the new app to immediately suspend all executing code I submitted to the DispatchQueue, when backgrounded or lock-screened I would be extremely happy.
When an app is suspended, anything running, whether on the main queue or background global queue, is suspended, too.
Are you doing anything in the app to keep it running in the background? For example, if you run the app via the Xcode debugger (for example, so you can watch your print statements or whatever) it changes the app lifecycle and keeps it running in the background. (The Xcode “observer effect”? Lol.) E.g. are you running this via the Xcode debugger?
Also, if your app has certain background capabilities enabled, that can also keep the app alive.
Since you cannot run an app via Xcode debugger to watch the app lifecycle, I’ll demonstrate the process using Unified Logging to monitor the progress of my App. With Unified Logging, I can monitor these log statements on my iPhone from my macOS Console, without even having Xcode running at all.
Consider something like:
import UIKit
import os.log
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "ViewController")
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
logger.debug(#function)
startTask()
}
func startTask() {
Task {
var i = 0
while true {
try await Task.sleep(for: .seconds(1))
logger.debug("tick \(i)")
i += 1
}
}
}
}
So I then:
We can clearly see that the app stops (including this task running on the background queue) when I suspend the app. (Obviously, in addition to the above Logger messages, I put similar statements in my AppDelegate and SceneDelegate, so that I can see lifecycle events here, too.) Anyway, this is what my console showed:

I left my app when “tick 5” showed up, it took a few seconds before the app was fully suspended, but you can see that the app stopped running after “tick 7” and “sceneDidEnterBackground” appeared. And I restarted the app about 10 seconds later, at which point you see the app come back to life and resume ticking away, right where it left off.
So, if your app is still running, either it’s attached to the Xcode debugger, or you have something keeping the app running in the background. But generally, when you leave an app, it’s suspended, and you’ll see behavior like I outlined above.
By the way, for more information on using Unified Logging, configuring your device, etc., see WWDC 2016 video Unified Logging and Activity Tracing.
Note, I’ve updated this to use Logger. For the os_log rendition (the predecessor of Logger), see previous revision of this answer. I have also updated this to use Task {…} rather than a GCD dispatch queue; the issues above apply whether using main queue/actor, background dispatch queue, detached tasks, or whatever.
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