Approachable Concurrency

My environment

  • Xcode 26.0 (17A324)

  • macOS: 26.0

  • Apple Swift version 6.2 (swiftlang-6.2.0.19.9 clang-1700.3.19.1) Target: arm64-apple-macosx26.0

struct ContentView: View {
    
    var body: some View {
        Text("Rob")
            .task {
                try? await fetch()
            }
    }
    
    func fetch() async throws {
        let url = URL(string: "https://robertnash.com")!
        _ = try await client.data(url)
    }
}

struct NetworkClient {
    
    let data: (URL) async throws -> (Data, URLResponse)
    
    init(data: @escaping (URL) async throws -> (Data, URLResponse)) {
        self.data = data
    }
}

/// Let 'client' is not concurrency-safe because non-'Sendable' type 'NetworkClient' may have shared mutable state
let client = NetworkClient(data: {
    try await URLSession.shared.data(for: URLRequest(url: $0))
})

I tested Sendable conformance (missing or present) with the following build settings.

  1. Swift 5 language mode seems pretty dangerous at this point. Folks should probably get the handy feedback from Swift 6!
  2. Enabling approachable concurrency ironically causes a crash here.
  3. A default main actor isolation may just mask poor architecture.

I tend to agree. Is Swift 5 mode still the default for new Xcode 26 projects? What about SPM new projects created by swift package init ?

(can't test Xcode 26 myself, as my iMac 2017 does not support latest macOS and Xcode)

1 Like

It is, which is rather bizarre, given they turned Approachable Concurrency and Default Main Actor Isolation on by default. Those features are more painful without the smarter diagnostics in Swift 6 mode.

I just want to add to this thread with my own experience:

I have a project that targets iOS 15.0 and uses Swift 5.
When I turn on approachable concurrency, I am seeing a crash on iOS 16.7.11. When I turn it off it does not crash. Adding to that, I am not seeing this crash at all on any other iOS version, regardless of whether or not I have it turned on.

We have a lot of singletons so it doesn’t surprise me that we are experiencing things like this, and I can't even begin to understand what the actual causes are.

I guess this just means we turn this setting off and slowly migrate to Swift 6 without it turned on. I'm not necessarily looking for suggestions for a fix; I am just sharing what I have observed.

Environment

  • Xcode 26.0.1
  • Target iOS 15.0
  • Swift 5
Approachable Concurrency OFF Approachable Concurrency ON
iOS 15.7 No crash No crash
iOS 16.7.11 No crash CRASH
iOS 18.6 No crash No crash
iPadOS 26 No crash No crash

Stack trace for 16.7.11:

* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0xfe89e3b8)
  * frame #0: 0x00000001bae41334 libswiftCore.dylib`swift_unknownObjectRetain + 16
    frame #1: 0x00000001bae22cc0 libswiftCore.dylib`swift::metadataimpl::ValueWitnesses<swift::metadataimpl::ClassExistentialBox<1u>>::initializeWithCopy(swift::OpaqueValue*, swift::OpaqueValue*, swift::TargetMetadata<swift::InProcess> const*) + 36
    frame #2: 0x00000001bade6ffc libswiftCore.dylib`merged initializeWithCopy value witness for Swift.ClosedRange< where τ_0_0: Swift.Strideable, τ_0_0.Stride: Swift.SignedInteger>.Index + 108
    frame #3: 0x00000001e26f5d7c AttributeGraph`AG::LayoutDescriptor::compare(unsigned char const*, unsigned char const*, unsigned char const*, unsigned long, unsigned int) + 1056
    frame #4: 0x00000001e26f5a78 AttributeGraph`AG::LayoutDescriptor::compare(unsigned char const*, unsigned char const*, unsigned char const*, unsigned long, unsigned int) + 284
    frame #5: 0x00000001e26f8374 AttributeGraph`AGGraphSetOutputValue + 440
    frame #6: 0x00000001c43c24d8 SwiftUI`___lldb_unnamed_symbol68102 + 68
    frame #7: 0x00000001c4fb5fec SwiftUI`___lldb_unnamed_symbol169143 + 72
    frame #8: 0x00000001bac2fc80 libswiftCore.dylib`withUnsafePointer(to:_:) + 20
    frame #9: 0x00000001c4400318 SwiftUI`___lldb_unnamed_symbol69591 + 248
    frame #10: 0x00000001c4826b00 SwiftUI`___lldb_unnamed_symbol97802 + 28
    frame #11: 0x00000001e26f6ef8 AttributeGraph`AG::Graph::UpdateStack::update() + 512
    frame #12: 0x00000001e26f65c0 AttributeGraph`AG::Graph::update_attribute(AG::data::ptr<AG::Node>, unsigned int) + 420
    frame #13: 0x00000001e26f5478 AttributeGraph`AG::Subgraph::update(unsigned int) + 848
    frame #14: 0x00000001c4303680 SwiftUI`___lldb_unnamed_symbol63529 + 516
    frame #15: 0x00000001c53c4cb4 SwiftUI`___lldb_unnamed_symbol205527 + 20
    frame #16: 0x00000001c4b94eec SwiftUI`___lldb_unnamed_symbol131388 + 44
    frame #17: 0x00000001c4301258 SwiftUI`___lldb_unnamed_symbol63519 + 88
    frame #18: 0x00000001c42fbc5c SwiftUI`___lldb_unnamed_symbol63435 + 92
    frame #19: 0x00000001c42f6ab8 SwiftUI`___lldb_unnamed_symbol63348 + 56
    frame #20: 0x00000001c53c4c80 SwiftUI`___lldb_unnamed_symbol205526 + 148
    frame #21: 0x00000001c53c4bb8 SwiftUI`___lldb_unnamed_symbol205525 + 68
    frame #22: 0x00000001c43a904c SwiftUI`___lldb_unnamed_symbol67447 + 20
    frame #23: 0x00000001c42f48a0 SwiftUI`___lldb_unnamed_symbol63302 + 112
    frame #24: 0x00000001c42f4804 SwiftUI`___lldb_unnamed_symbol63301 + 32
    frame #25: 0x00000001c42f498c SwiftUI`___lldb_unnamed_symbol63306 + 56
    frame #26: 0x00000001c0cc18e8 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 32
    frame #27: 0x00000001c0c5151c CoreFoundation`__CFRunLoopDoObservers + 552
    frame #28: 0x00000001c0cad214 CoreFoundation`__CFRunLoopRun + 1004
    frame #29: 0x00000001c0cb1d20 CoreFoundation`CFRunLoopRunSpecific + 584
    frame #30: 0x00000001f8d81998 GraphicsServices`GSEventRunModal + 160
    frame #31: 0x00000001c2f4434c UIKitCore`-[UIApplication _run] + 868
    frame #32: 0x00000001c2f43fc4 UIKitCore`UIApplicationMain + 312
    frame #33: 0x00000001c446bc68 SwiftUI`___lldb_unnamed_symbol72311 + 172
    frame #34: 0x00000001c43e5f1c SwiftUI`___lldb_unnamed_symbol68927 + 140
    frame #35: 0x00000001c43d2f6c SwiftUI`___lldb_unnamed_symbol68472 + 96
    frame #37: 0x000000010833dad0 RedactedApp.debug.dylib`main at RedactedApp.swift:0
    frame #38: 0x00000001de470344 dyld`start + 1860

Make NetworkClient and its closure Sendable or isolate access via an actor/@MainActor. Use @Sendable on the closure, keep stored state immutable, or convert NetworkClient to an actor/Sendable struct. Enable stricter concurrency checks and fix non-Sendable captures.