While trying to adopt Swift 6 mode I've noticed difference in behaviour for existential vs generic.
Target APIClient
has adopted Swift 6 mode while target DataClient
hasn't:
// swift-tools-version: 6.0
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "TestPackage",
platforms: [.iOS(.v16), .macOS(.v14)],
products: [
.library(name: "APIClient", targets: ["APIClient"]),
.library(name: "DataClient", targets: ["DataClient"]),
],
targets: [
.target(
name: "APIClient",
swiftSettings: [
.swiftLanguageMode(.v6),
]
),
.target(
name: "DataClient",
dependencies: ["APIClient"],
swiftSettings: [
.swiftLanguageMode(.v5),
]
),
]
)
Target APIClient
defines an APIClient
protocol:
import Foundation
public protocol APIClient {
func fetch() -> String
}
Target DataClient
defines and implements some data client:
import Foundation
import APIClient
struct UserClient: Sendable {
let fetchUserId: @Sendable () async throws -> String
}
extension UserClient {
static func liveA(
apiClient: APIClient
) -> Self {
.init(
fetchUserId: {
apiClient.fetch() // Capture of 'apiClient' with non-sendable type 'any APIClient' in a `@Sendable` closure; this is an error in the Swift 6 language mode
}
)
}
static func liveB(
apiClient: some APIClient
) -> Self {
.init(
fetchUserId: {
apiClient.fetch()
}
)
}
static func liveC<T: APIClient>(
apiClient: T
) -> Self {
.init(
fetchUserId: {
apiClient.fetch()
}
)
}
}
Calling the APIClient
when passed as existential produces the following warning: Capture of 'apiClient' with non-sendable type 'any APIClient' in a
@Sendable closure; this is an error in the Swift 6 language mode
, but if the APIClient
is passed as generic, either with some keyword or type placeholder, the warning is not produced. I've been able to produce the warning only in this setup, when all targets are v5 compatibility mode, no warning is reported.
Could someone help me understand why is the warning not being produced for the generic?