Implicitly imported types & namespace priority

The SwiftUI module implicitly includes the private _Concurrency module, such that the full Swift Concurrency API is available to us from SwiftUI views.

import SwiftUI

Task {
    await withCheckedContinuation { $0.resume() }
}

However, when we encounter an environment where a competing type name exists either in the module or even in a separate module that is also imported, the competing type always appears to take precedence:

// MyModule
public protocol Task {}
import SwiftUI
import MyModule

Task { // 'Task' cannot be constructed because it has no accessible initializers
    await withCheckedContinuation { $0.resume() }
}

We can force Swift to find the correct Task by prefixing using the private namespace _Concurrency:

import SwiftUI
import MyModule

_Concurrency.Task {
    await withCheckedContinuation { $0.resume() }
}

However, this does not look kosher and we are now using underscored symbols in our code.

What would be better is if we could allow "language types" to take precedence over our own types. I had half-expected to be able to do this by sorting the imports:

import MyModule
import SwiftUI

Task { // 'Task' cannot be constructed because it has no accessible initializers
    await withCheckedContinuation { $0.resume() }
}

Alas, no such luck. Even explicitly bringing in import _Concurrency here does not appear to help.

Can anyone recommend a solution that avoids the need for underscored symbols and doesn't force me to rename my own API simply because Swift decided to rename async to Task?

1 Like

Actually, the Swift standard library is what re-exports the _Concurrency module [edit: actually, I guess it isn't really a re-export, as you can't refer to Task as Swift.Task]; you don't need to write any explicit import statement to use the Task API.

There is a special case in the compiler specifically to do the opposite: that is, it's deliberate that user types are always favored. Otherwise, every addition to the Swift standard library would be a possibly source-breaking change.

In a separate file within your project, write (without including anything else in the file):

typealias ConcurrencyTask /* or whatever you want to call it */ = Task

You can then use that alias everywhere in your project.

4 Likes

Thanks; I had considered a typealias as well, though I wasn't sure how I felt about redefining standard types into non-standard names. It seems like this would lead to inevitable confusion for other or new developers on the team and antithetical to having a "standard library" of universally recognized language features.

Your three and only three options here are: rename your own Task type, rename Swift's Task type, or fully qualify the latter by writing _Concurrency.Task.

1 Like

Is _Concurrency a safe symbol to reference? (ie. is its name "public" & stable)

Unless you need that other Task, you can also restrain your imports by import struct MyModule.MyStruct, thereby avoiding an implicit import of MyModule.Task.

1 Like

_Concurrency.Task is the correct invocation. the underscore is part of the module name, do not fear the underscore just because it’s there.

however, you may eventually experience some source breakage once the core team decides to drop the underscore, similar to _Distributed -> Distributed. but this would also happen if you used the typealias.