swiftswift-concurrency

Swift Concurrency - Why my Task didn't inherit the parent Task's executor and priority


When creating Task A from Task B, I thought Task A will inherit from Task B. But when running the following code I created, why did line 14 printed debug 4: <NSThread: 0x30361a300>{number = 6, name = (null)}. Why it isn't main thread, just like the other prints?

enter image description here

Here's the code for anyone who wants to run locally:

struct Test {
    func test() {
        print("debug 3:", Thread.current)
        Task {
            print("debug 4:", Thread.current)
        }
    }
}

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        
        Task { @MainActor in
            print("debug 1:", Thread.current)
            Task {
                print("debug 2:", Thread.current)
                Test().test()
            }
        }
    }
}

Solution

  • The function test is not actor-isolated. Thus, its Task {…} is not actor-isolated either. (If test was actor-isolated, then its Task {…} would be a top level task on the same actor. But test not actor-isolated, so that is moot.)

    In Swift concurrency, a function’s isolation is either explicitly defined or inherited from the type in which it is defined, if any, but it does not “inherit” it from the caller.


    While the above answers the original question, it should be noted that Swift 6.2 introduces a new wrinkle: As part of the broader “Approachable Concurrency” initiative, there is a new “Default Actor Isolation” build setting. Legacy projects will follow the historical pattern of a “Default Actor Isolation” setting of “nonisolated”, but new Xcode 26 projects will default to “MainActor”. Obviously, if you use a “Default Actor Isolation” of “MainActor”, then Test will be isolated to the main actor, and then the “debug 4” will obviously run on the main actor, too.

    Also, above I mentioned how nonisolated functions do not inherit the isolation of the caller: The exception to this rule in Swift 6.2’s “Approachable Concurrency” is nonisolated async functions, which will do so (unless explicitly marked with @concurrent). But this question was about calling a synchronous method, so this caveat is not immediately relevant, either.

    For more information about this new paradigm, see WWDC 2025’s Embracing Swift concurrency.