3

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.

Random Joe
  • 69
  • 6
  • 1
    You can't suspend `DispatchQueue` tasks. If you need more control you should use `OperationQueue`. This is the main difference between two, if something is written inside `DispatchQueue` block, that task will be executed at some point. – Sharad Chauhan Apr 01 '19 at 04:43
  • @SharadChauhan - Operation queues don’t differ from custom dispatch queues in this regard. If your app is still running, suspending a queue won’t suspend operations running on the `OperationQueue`, either. When you suspend an operation queue, it only prevents new operations from starting. But operations that are in progress aren’t suspended. – Rob Apr 01 '19 at 07:35

1 Answers1

3

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 log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "ViewController")

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        os_log("viewDidLoad", log: log, type: .debug)
        startTask()
    }

    func startTask() {
        DispatchQueue.global().async {
            var i = 0
            while true {
                let start = Date()
                while Date().timeIntervalSince(start) < 1 { }
                os_log("tick %d", log: log, type: .debug, i)
                i += 1
            }
        }
    }
}

(Note, generally you should never spin like this, but I’ve done this to have a process that deliberately keeps the CPU busy.)

So I then:

  • installed on my device,
  • quit Xcode,
  • fired up the macOS Console app,
  • in Console app, I enabled debug logging with “Action” » “Include Debug Messages”,
  • filter the log so I’m only seeing activity from my subsystem (I personally always use my bundle identifier for that), and
  • run my app directly on my iOS device.

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 os_log messages, I put similar statements in my AppDelegate, so that I can see lifecycle events here, too.) Anyway, this is what my console showed:

Console

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 “applicationDidEnterBackground” 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.

Rob
  • 415,655
  • 72
  • 787
  • 1,044