Concurrency

RSS for tag

Concurrency is the notion of multiple things happening at the same time.

Posts under Concurrency tag

115 Posts

Post

Replies

Boosts

Views

Activity

SwiftUI View cannot conform custom Equatable protocol in Swift 6.
In Swift 6, stricter concurrency rules can lead to challenges when making SwiftUI views conform to Equatable. Specifically, the == operator required for Equatable must be nonisolated, which means it cannot access @MainActor-isolated properties. This creates an error when trying to compare views with such properties: Error Example: struct MyView: View, Equatable { let title: String let count: Int static func ==(lhs: MyView, rhs: MyView) -> Bool { // Accessing `title` here would trigger an error due to actor isolation. return lhs.count == rhs.count } var body: some View { Text(title) } } Error Message: Main actor-isolated operator function '==' cannot be used to satisfy nonisolated protocol requirement; this is an error in the Swift 6 language mode. Any suggestions? Thanks FB: FB15753655 (SwiftUI View cannot conform custom Equatable protocol in Swift 6.)
6
0
966
Mar ’25
How to implement thread-safe property wrapper notifications across different contexts in Swift?
I’m trying to create a property wrapper that that can manage shared state across any context, which can get notified if changes happen from somewhere else. I'm using mutex, and getting and setting values works great. However, I can't find a way to create an observer pattern that the property wrappers can use. The problem is that I can’t trigger a notification from a different thread/context, and have that notification get called on the correct thread of the parent object that the property wrapper is used within. I would like the property wrapper to work from anywhere: a SwiftUI view, an actor, or from a class that is created in the background. The notification preferably would get called synchronously if triggered from the same thread or actor, or otherwise asynchronously. I don’t have to worry about race conditions from the notification because the state only needs to reach eventuall consistency. Here's the simplified pseudo code of what I'm trying to accomplish: // A single source of truth storage container. final class MemoryShared<Value>: Sendable { let state = Mutex<Value>(0) func withLock(_ action: (inout Value) -> Void) { state.withLock(action) notifyObservers() } func get() -> Value func notifyObservers() func addObserver() } // Some shared state used across the app static let globalCount = MemoryShared<Int>(0) // A property wrapper to access the shared state and receive changes @propertyWrapper struct SharedState<Value> { public var wrappedValue: T { get { state.get() } nonmutating set { // Can't set directly } } var publisher: Publisher {} init(state: MemoryShared) { // ... } } // I'd like to use it in multiple places: @Observable class MyObservable { @SharedState(globalCount) var count: Int } actor MyBackgroundActor { @SharedState(globalCount) var count: Int } @MainActor struct MyView: View { @SharedState(globalCount) var count: Int } What I’ve Tried All of the examples below are using the property wrapper within a @MainActor class. However the same issue happens no matter what context I use the wrapper in: The notification callback is never called on the context the property wrapper was created with. I’ve tried using @isolated(any) to capture the context of the wrapper and save it to be called within the state in with unchecked sendable, which doesn’t work: final class MemoryShared<Value: Sendable>: Sendable { // Stores the callback for later. public func subscribe(callback: @escaping @isolated(any) (Value) -> Void) -> Subscription } @propertyWrapper struct SharedState<Value> { init(state: MemoryShared<Value>) { MainActor.assertIsolated() // Works! state.subscribe { MainActor.assertIsolated() // Fails self.publisher.send() } } } I’ve tried capturing the isolation within a task with AsyncStream. This actually compiles with no sendable issues, but still fails: @propertyWrapper struct SharedState<Value> { init(isolation: isolated (any Actor)? = #isolation, state: MemoryShared<Value>) { let (taskStream, continuation) = AsyncStream<Value>.makeStream() // The shared state sends new values to the continuation. subscription = state.subscribe(continuation: continuation) MainActor.assertIsolated() // Works! let task = Task { _ = isolation for await value in taskStream { _ = isolation MainActor.assertIsolated() // Fails } } } } I’ve tried using multiple combine subjects and publishers: final class MemoryShared<Value: Sendable>: Sendable { let subject: PassthroughSubject<T, Never> // ... var publisher: Publisher {} // ... } @propertyWrapper final class SharedState<Value> { var localSubject: Subject init(state: MemoryShared<Value>) { MainActor.assertIsolated() // Works! handle = localSubject.sink { MainActor.assertIsolated() // Fails } stateHandle = state.publisher.subscribe(localSubject) } } I’ve also tried: Using NotificationCenter Making the property wrapper a class Using NSKeyValueObserving Using a box class that is stored within the wrapper. Using @_inheritActorContext. All of these don’t work, because the event is never called from the thread the property wrapper resides in. Is it possible at all to create an observation system that notifies the observer from the same context as where the observer was created? Any help would be greatly appreciated!
2
0
540
Mar ’25
Swift 6 Concurrency errors with ModelActor, or Core Data actors
In my app, I've been using ModelActors in SwiftData, and using actors with a custom executor in Core Data to create concurrency safe services. I have multiple actor services that relate to different data model components or features, each that have their own internally managed state (DocumentService, ImportService, etc). The problem I've ran into, is that I need to be able to use multiple of these services within another service, and those services need to share the same context. Swift 6 doesn't allow passing contexts across actors. The specific problem I have is that I need a master service that makes multiple unrelated changes without saving them to the main context until approved by the user. I've tried to find a solution in SwiftData and Core Data, but both have the same problem which is contexts are not sendable. Read the comments in the code to see the issue: /// This actor does multiple things without saving, until committed in SwiftData. @ModelActor actor DatabaseHelper { func commitChange() throws { try modelContext.save() } func makeChanges() async throws { // Do unrelated expensive tasks on the child context... // Next, use our item service let service = ItemService(modelContainer: SwiftDataStack.shared.container) let id = try await service.expensiveBackgroundTask(saveChanges: false) // Now that we've used the service, we need to access something the service created. // However, because the service created its own context and it was never saved, we can't access it. let itemFromService = context.fetch(id) // fails // We need to be able to access changes made from the service within this service, /// so instead I tried to create the service by passing the current service context, however that results in: // ERROR: Sending 'self.modelContext' risks causing data races let serviceFromContext = ItemService(context: modelContext) // Swift Data doesn't let you create child contexts, so the same context must be used in order to change data without saving. } } @ModelActor actor ItemService { init(context: ModelContext) { modelContainer = SwiftDataStack.shared.container modelExecutor = DefaultSerialModelExecutor(modelContext: context) } func expensiveBackgroundTask(saveChanges: Bool = true) async throws -> PersistentIdentifier? { // Do something expensive... return nil } } Core Data has the same problem: /// This actor does multiple things without saving, until committed in Core Data. actor CoreDataHelper { let parentContext: NSManagedObjectContext let context: NSManagedObjectContext /// In Core Data, I can create a child context from a background context. /// This lets you modify the context and save it without updating the main context. init(progress: Progress = Progress()) { parentContext = CoreDataStack.shared.newBackgroundContext() let childContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType) childContext.parent = parentContext self.context = childContext } /// To commit changes, save the parent context pushing them to the main context. func commitChange() async throws { // ERROR: Sending 'self.parentContext' risks causing data races try await parentContext.perform { try self.parentContext.save() } } func makeChanges() async throws { // Do unrelated expensive tasks on the child context... // As with the Swift Data example, I am unable to create a service that uses the current actors context from here. // ERROR: Sending 'self.context' risks causing data races let service = ItemService(context: self.context) } } Am I going about this wrong, or is there a solution to fix these errors? Some services are very large and have their own internal state. So it would be very difficult to merge all of them into a single service. I also am using Core Data, and SwiftData extensively so I need a solution for both. I seem to have trapped myself into a corner trying to make everything concurrency save, so any help would be appreciated!
6
0
870
Mar ’25
Use of `for await` with `AyncStream`, and yielding async closures to its continuation
Hello, I was hoping to clarify my understanding of the use of for await with an AsyncStream. My use case is, I'd like to yield async closures to the stream's continuation, with the idea that, when I use for await with the stream to process and execute the closures, it would only continue on to the following closure once the current closure has been run to completion. At a high level, I am trying to implement in-order execution of async closures in the context of re-entrancy. An example of asynchronous work I want to execute is a network call that should write to a database: func syncWithRemote() async -> Void { let data = await fetchDataFromNetwork() await writeToLocalDatabase(data) } For the sake of example, I'll call the intended manager of closure submission SingleOperationRunner. where, at a use site such as this, my desired outcome is that call 1 of syncWithRemote() is always completed before call 2 of it: let singleOperationRunner = SingleOperationRunner(priority: nil) singleOperationRunner.run { syncWithRemote() } singleOperationRunner.run { syncWithRemote() } My sketch implementation looks like this: public final class SingleOperationRunner { private let continuation: AsyncStream<() async -> Void>.Continuation public init(priority: TaskPriority?) { let (stream, continuation) = AsyncStream.makeStream(of: (() async -> Void).self) self.continuation = continuation Task.detached(priority: priority) { // Will this loop only continue when the `await operation()` completes? for await operation in stream { await operation() } } } public func run(operation: @escaping () async -> Void) { continuation.yield(operation) } deinit { continuation.finish() } } The resources I've found are https://developer.apple.com/videos/play/wwdc2022-110351/?time=1445 and https://forums.swift.org/t/swift-async-func-to-run-sequentially/60939/2 but do not think I have fully put the pieces together, so would appreciate any help!
2
0
311
Mar ’25
iOS Share Extension Warning: Passing argument of non-sendable type outside of main actor-isolated context may introduce data races
Consider this simple miniature of my iOS Share Extension: import SwiftUI import Photos class ShareViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() if let itemProviders = (extensionContext?.inputItems.first as? NSExtensionItem)?.attachments { let hostingView = UIHostingController(rootView: ShareView(extensionContext: extensionContext, itemProviders: itemProviders)) hostingView.view.frame = view.frame view.addSubview(hostingView.view) } } } struct ShareView: View { var extensionContext: NSExtensionContext? var itemProviders: [NSItemProvider] var body: some View { VStack{} .task{ await extractItems() } } func extractItems() async { guard let itemProvider = itemProviders.first else { return } guard itemProvider.hasItemConformingToTypeIdentifier(UTType.url.identifier) else { return } do { guard let url = try await itemProvider.loadItem(forTypeIdentifier: UTType.url.identifier) as? URL else { return } try await downloadAndSaveMedia(reelURL: url.absoluteString) extensionContext?.completeRequest(returningItems: []) } catch {} } } On the line 34 guard let url = try await itemProvider.loadItem ... I get these warnings: Passing argument of non-sendable type '[AnyHashable : Any]?' outside of main actor-isolated context may introduce data races; this is an error in the Swift 6 language mode 1.1. Generic enum 'Optional' does not conform to the 'Sendable' protocol (Swift.Optional) Passing argument of non-sendable type 'NSItemProvider' outside of main actor-isolated context may introduce data races; this is an error in the Swift 6 language mode 2.2. Class 'NSItemProvider' does not conform to the 'Sendable' protocol (Foundation.NSItemProvider) How to fix them in Xcode 16? Please provide a solution which works, and not the one which might (meaning you run the same code in Xcode, add your solution and see no warnings). I tried Decorating everything with @MainActors Using @MainActor in the .task @preconcurrency import Decorating everything with @preconcurrency Playing around with nonisolated
2
0
553
Mar ’25
Rewrite `UNNotificationServiceExtension` sub class into Swift 6 async await notation
I'm trying to rewrite a Swift code to Swift 6 language mode and am stuck with this problem. How do I safely pass the bestAttemptContent and contentHandler to the Task? This is from the UNNotificationServiceExtension subclass. final class NotificationService: UNNotificationServiceExtension { var contentHandler: ((UNNotificationContent) -> Void)? var bestAttemptContent: UNMutableNotificationContent? var customNotificationTask: Task<Void, Error>? override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { self.contentHandler = contentHandler bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent) guard let bestAttemptContent = bestAttemptContent else { invokeContentHandler(with: request.content) return } do { let notificationModel = try PushNotificationUserInfo(data: request.content.userInfo) guard let templatedImageUrl = notificationModel.templatedImageUrlString, let imageUrl = imageUrl(from: templatedImageUrl) else { invokeContentHandler(with: bestAttemptContent) return } setupCustomNotificationTask( imageUrl: imageUrl, bestAttemptContent: bestAttemptContent, contentHandler: contentHandler ) } catch { invokeContentHandler(with: bestAttemptContent) } } // More code private func downloadImageTask( imageUrl: URL, bestAttemptContent: UNMutableNotificationContent, contentHandler: @escaping (UNNotificationContent) -> Void ) { self.customNotificationTask = Task { let (location, _) = try await URLSession.shared.download(from: imageUrl) let desiredLocation = URL(fileURLWithPath: "\(location.path)\(imageUrl.lastPathComponent)") try FileManager.default.moveItem(at: location, to: desiredLocation) let attachment = try UNNotificationAttachment(identifier: imageUrl.absoluteString, url: desiredLocation, options: nil) bestAttemptContent.attachments = [attachment] contentHandler(bestAttemptContent) } } } I tried using the MainActor.run {}, but it just moved the error to that run function. The UNNotificationRequest is not sendable, and I don't think I can make it so. Wrap the setupCustomNotification in a Task will move the errors to the didReceive method. It seems like the consuming keyword will help here, but it leads to a compilation error, even with the latest Xcode (16.2). Any pointers?
2
1
476
Mar ’25
Is Metal usable from Swift 6?
Hello ladies and gentlemen, I'm writing a simple renderer on the main actor using Metal and Swift 6. I am at the stage now where I want to create a render pipeline state using asynchronous API: @MainActor class Renderer { let opaqueMeshRPS: MTLRenderPipelineState init(/*...*/) async throws { let descriptor = MTLRenderPipelineDescriptor() // ... opaqueMeshRPS = try await device.makeRenderPipelineState(descriptor: descriptor) } } I get a compilation error if try to use the asynchronous version of the makeRenderPipelineState method: Non-sendable type 'any MTLRenderPipelineState' returned by implicitly asynchronous call to nonisolated function cannot cross actor boundary Which is understandable, since MTLRenderPipelineState is not Sendable. But it looks like no matter where or how I try to access this method, I just can't do it - you have this API, but you can't use it, you can only use the synchronous versions. Am I missing something or is Metal just not usable with Swift 6 right now?
1
0
608
Mar ’25
MainActor and NSInternalInconsistencyException: 'Call must be made on main thread'
Hello, When attempting to assign the UNNotificationResponse to a Published property on the main thread inside UNUserNotificationCenterDelegate's method func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse) async both Task { @MainActor in } and await MainActor.run are throwing a NSInternalInconsistencyException: 'Call must be made on main thread'. I thought both of them were essentially doing the same thing, i.e. call their closure on the main thread. So why is this exception thrown? Is my understanding of the MainActor still incorrect, or is this a bug? Thank you Note: Task { await MainActor.run { ... } } and DispatchQueue.main.async don't throw any exception.
5
5
3.9k
Feb ’25
Why Actor-isolated property cannot be passed 'inout' to 'async' function call?
Considering below dummy codes: @MainActor var globalNumber = 0 @MainActor func increase(_ number: inout Int) async { // some async code excluded number += 1 } class Dummy: @unchecked Sendable { @MainActor var number: Int { get { globalNumber } set { globalNumber = newValue } } @MainActor func change() async { await increase(&number) //Actor-isolated property 'number' cannot be passed 'inout' to 'async' function call } } I'm not really trying to make an increasing function like that, this is just an example to make everything happen. As for why number is a computed property, this is to trigger the actor-isolated condition (otherwise, if the property is stored and is a value type, this condition will not be triggered). Under these conditions, in function change(), I got the error: Actor-isolated property 'number' cannot be passed 'inout' to 'async' function call. My question is: Why Actor-isolated property cannot be passed 'inout' to 'async' function call? What is the purpose of this design? If this were allowed, what problems might it cause?
0
0
412
Feb ’25
Implement UNUserNotificationCenterDelegate in iOS app using Swift6
I've got a problem with compatibility with Swift6 in iOS app that I have no idea how to sort it out. That is an extract from my main app file @MainActor @main struct LangpadApp: App { ... @State private var notificationDataProvider = NotificationDataProvider() @UIApplicationDelegateAdaptor(NotificationServiceDelegate.self) var notificationServiceDelegate var body: some Scene { WindowGroup { TabView(selection: $tabSelection) { ... } .onChange(of: notificationDataProvider.dateId) { oldValue, newValue in if !notificationDataProvider.dateId.isEmpty { tabSelection = 4 } } } } init() { notificationServiceDelegate.notificationDataProvider = notificationDataProvider } } and the following code shows other classes @MainActor final class NotificationServiceDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate { var notificationDataProvider: NotificationDataProvider? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -&amp;gt; Bool { UNUserNotificationCenter.current().delegate = self return true } func setDateId(dateId: String) { if let notificationDataProvider = notificationDataProvider { notificationDataProvider.dateId = dateId } } nonisolated func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse) async { // After user pressed notification let content = response.notification.request.content if let dateId = content.userInfo["dateId"] as? String { await MainActor.run { setDateId(dateId: dateId) } } } nonisolated func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification) async -&amp;gt; UNNotificationPresentationOptions { // Before notification is to be shown return [.sound, .badge, .banner, .list] } } @Observable final public class NotificationDataProvider : Sendable { public var dateId = "" } I have set Strict Concurrency Checking to 'Complete.' The issue I'm facing is related to the delegate class method, which is invoked after the user presses the notification. Current state causes crash after pressing notification. If I remove "nonisolated" keyword it works fine but I get the following warning Non-sendable type 'UNNotificationResponse' in parameter of the protocol requirement satisfied by main actor-isolated instance method 'userNotificationCenter(_:didReceive:)' cannot cross actor boundary; this is an error in the Swift 6 language mode I have no idea how to make it Swift6 compatible. Does anyone have any clues?
1
15
1.5k
Feb ’25
Why is UIViewController.dismissViewControllerAnimated marked as NS_SWIFT_DISABLE_ASYNC?
In the header for UIViewController, the method dismissViewControllerAnimated is declared like this: - (void)dismissViewControllerAnimated: (BOOL)flag completion: (void (^ __nullable)(void))completion NS_SWIFT_DISABLE_ASYNC API_AVAILABLE(ios(5.0)); NS_SWIFT_DISABLE_ASYNC means that there's no async version exposed like there would normally be of a method that exposes a completion handler. Why is this? And is it unwise / unsafe for me to make my own async version of it using a continuation? My use case is that I want a method that will sequentially dismiss all view controllers presented by a root view controller. So I could have this extension on UIViewController: extension UIViewController { func dismissAsync(animated: Bool) async { await withCheckedContinuation { continuation in self.dismiss(animated: animated) { continuation.resume() } } } func dismissPresentedViewControllers() async { while self.topPresentedViewController != self { await self.topPresentedViewController.dismissAsync(animated: true) } } var topPresentedViewController: UIViewController { var result = self while result.presentedViewController != nil { result = result.presentedViewController! } return result }
0
0
358
Feb ’25
Swift 6 crash calling requestAutomaticPassPresentationSuppression
I found a similar problem here https://developer.apple.com/forums/thread/764777 and I could solve my problem by wrapping the call to requestAutomaticPassPresentationSuppression in a call to DispatchQueue.global().async. But my question is if this is really how things should work. Even with strict concurrency warnings in Swift 6 I don't get any warnings. Just a runtime crash. How are we supposed to find these problems? Couldn't the compiler assist with a warning/error. Why does the compiler make the assumptions it does about the method that is declared like this: @available(iOS 9.0, *) open class func requestAutomaticPassPresentationSuppression(responseHandler: @escaping (PKAutomaticPassPresentationSuppressionResult) -> Void) -> PKSuppressionRequestToken Now that we have migrated to Swift 6 our code base contains a bunch of unknown places where it will crash as above.
2
0
488
Feb ’25
Nested method calls in `context.perform` with Swift 6
I'm calling a method with the context as parameter, within the context's perform block – is this really not legal in Swift 6? actor MyActor { func bar(context: NSManagedObjectContext) { /* some code */ } func foo(context: NSManagedObjectContext) { context.performAndWait { self.bar(context: context) // WARN: Sending 'context' risks causing data races; this is an error in the Swift 6 language mode // 'self'-isolated 'context' is captured by a actor-isolated closure. actor-isolated uses in closure may race against later nonisolated uses // Access can happen concurrently } } } The warning appears when I call a method with a context parameter, within the performAndWait-block. Background: In my app I have methods that takes in API data, and I need to call the same methods from multiple places with the same context to store it, and I do not want to copy paste the code and have hundreds of lines of duplicate code. Is there a well-known "this is how you should do it" for situations like this? This is related to a previous post I made, but it's a bit flimsy and got no response: https://developer.apple.com/forums/thread/770605
1
1
888
Feb ’25
SwiftUI Main Actor Isolation Error with PhotosPicker
I'm getting the following error in my SwiftUI code: "Main actor-isolated property 'avatarImage' can not be referenced from a Sendable closure" I don't understand how to fix it. This happens in the following code: You can copy-paste this into an empty project and make sure to have Swift 6 enabled under the Build Settings &gt; Swift Language Version import PhotosUI import SwiftUI public struct ContentView: View { @State private var avatarItem: PhotosPickerItem? @State private var avatarImage: Image? @State private var avatarData: Data? public var body: some View { VStack(spacing: 30) { VStack(alignment: .center) { PhotosPicker(selection: $avatarItem, matching: .images) { if let avatarImage { avatarImage .resizable() .aspectRatio(contentMode: .fill) .frame(width: 100, height: 100) .foregroundColor(.gray) .background(.white) .clipShape(Circle()) .opacity(0.75) .overlay { Image(systemName: "pencil") .font(.title) .shadow(radius: 5) } } else { Image(systemName: "person.circle.fill") .resizable() .aspectRatio(contentMode: .fit) .frame(width: 100, height: 100) .foregroundColor(.gray) .background(.white) .clipShape(Circle()) .opacity(0.75) .overlay { Image(systemName: "pencil") .font(.title) .shadow(radius: 5) } } } } } .onChange(of: avatarItem) { Task { if let data = try? await avatarItem?.loadTransferable( type: Data.self ) { if let processed = processImage(data: data) { avatarImage = processed.image avatarData = processed.data } else { } } } } } private func processImage(data: Data) -&gt; (image: Image?, data: Data?)? { guard let uiImage = UIImage(data: data)?.preparingForDisplay() else { return nil } // Check original size let sizeInMB = Double(data.count) / (1024 * 1024) // If image is larger than 1MB, compress it if sizeInMB &gt; 1.0 { guard let compressedData = uiImage.compress() else { return nil } return (Image(uiImage: uiImage), compressedData) } return (Image(uiImage: uiImage), data) } } #Preview { ContentView() } public extension UIImage { func compress(to maxSizeInMB: Double = 1.0) -&gt; Data? { let maxSizeInBytes = Int( maxSizeInMB * 1024 * 1024 ) // Convert MB to bytes var compression: CGFloat = 1.0 let step: CGFloat = 0.1 var imageData = jpegData(compressionQuality: compression) while (imageData?.count ?? 0) &gt; maxSizeInBytes, compression &gt; 0 { compression -= step imageData = jpegData(compressionQuality: compression) } return imageData } }
1
0
553
Feb ’25
MainActor in Network Extension
I am adopting Swift Concurrency in my network extension app to use Swift 6 protections. In the UI app I ended up with most of the app marked as MainActor, so that pieces of my app can keep seamless access to each other and at the same time have thread safe access. When it comes to my network extension, does it make sense to also mark most of the code as MainActor for the purposes of thread safety and seamless access of most classes to each other? I have doubts, because MainActor sounds like it should be a UI think, but network extension has no UI Of course any long or blocking operations would not be MainActor
1
0
460
Feb ’25
Task cancellation behaviour
Hi everyone, I believe this should be a simple and expected default behavior in a real-world app, but I’m unable to make it work: 1. I have a View (a screen/page in this case) that calls an endpoint using async/await. 2. If the endpoint hasn’t finished, but I navigate forward to a DetailView, I want the endpoint to continue fetching data (i.e., inside the @StateObject ViewModel that the View owns). This way, when I go back, the View will have refreshed with the fetched data once it completes. 3. If the endpoint hasn’t finished and I navigate back to the previous screen, I want it to be canceled, and the @StateObject ViewModel should be deinitialized. I can achieve 1 and 3 using the .task modifier, since it automatically cancels the asynchronous task when the view disappears: view .task { await vm.getData() } I can achieve 1 and 2 using a structured Task in the View (or in the ViewModel, its the same behavior), for example: .onFirstAppearOnly { Task { away vm.getData() } } onFirstAppearOnly is a custom modifier that I have for calling onAppear only once in view lifecycle. Just to clarify, dont think that part is important for the purpose of the example But the question is: How can I achieve all three behaviors? Is this really such an unusual requirement? My minimum deployment target is iOS 15, and I’m using NavigationView + NavigationLink. However, I have also tried using NavigationStack + NavigationPath and still couldn’t get it to work. Any help would be much appreciated. Thank you, folks!
0
0
397
Feb ’25
Non-sendable type AVMediaSelectionGroup
Hi all, we try migrate project to Swift 6 Project use AVPlayer in MainActor Selection audio and subtitiles not work Task { @MainActor in let group = try await item.asset.loadMediaSelectionGroup(for: AVMediaCharacteristic.audible) get error: Non-sendable type 'AVMediaSelectionGroup?' returned by implicitly asynchronous call to nonisolated function cannot cross actor boundary and second example `if #available(iOS 15.0, *) { player?.currentItem?.asset.loadMediaSelectionGroup(for: AVMediaCharacteristic.audible, completionHandler: { group, error in if error != nil { return } if let groupWrp = group { DispatchQueue.main.async { self.setupAudio(groupWrp, audio: audioLang) } } }) }` get error: Sending 'groupWrp' risks causing data races
1
0
500
Feb ’25
Swift 6 concurrency. Apple Watch App target and -disable-dynamic-actor-isolation.
I've got a watch app, still with storyboard, WKInterfaceController and WatchConnectivity. After updating it for swift 6 concurrency I thought I'd keep it for a little while without swift 6 concurrency dynamic runtime check. So I added -disable-dynamic-actor-isolation in OTHER_SWIFT_FLAGS, but it doesn't seem to have an effect for the Apple Watch target. Without manually marking callbacks where needed with @Sendable in dynamic checks seem to be in place. swiftc invocation is as (includes -disable-dynamic-actor-isolation): swiftc -module-name GeoCameraWatchApp -Onone -enforce-exclusivity\=checked ... GeoCameraWatchApp.SwiftFileList -DDEBUG -enable-bridging-pch -disable-dynamic-actor-isolation -D DEBUG -enable-experimental-feature DebugDescriptionMacro -sdk /Applications/Xcode.app/Contents/Developer/Platforms/WatchOS.platform/Developer/SDKs/WatchOS11.2.sdk -target arm64_32-apple-watchos7.0 -g -module-cache-path /Users/stand/Library/Developer/Xcode/DerivedData/ModuleCache.noindex -Xfrontend -serialize-debugging-options -enable-testing -index-store-path /Users/stand/Library/Developer/Xcode/DerivedData/speedo-almhjmryctkitceaufvkvhkkfvdw/Index.noindex/DataStore -enable-experimental-feature OpaqueTypeErasure -Xcc -D_LIBCPP_HARDENING_MODE\=_LIBCPP_HARDENING_MODE_DEBUG -swift-version 6 ... -disable-dynamic-actor-isolation flag seems to be working for the iOS targets, I believe. The flag is described here Am I missing something? Should the flag work for both iOS and Apple Watch targets?
2
0
639
Jan ’25
Swift 6 and 5 - Strict concurrency: complete and WKNavigationDelegate decidePolicyFor not being called.
decidePolicyFor delegate method: import WebKit @objc extension DocumentationVC { func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) Being called just alright in swift 5 minimal concurrency. Raising concurrency to complete with swift 5 or swift 6. Changing the code to avoid warnings: @preconcurrency import WebKit @objc extension DocumentationVC { func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { The delegate method is not being called. Changing back to swift 5 concurrency minimal - it is called. Looking at WKNavigationDelegate: WK_SWIFT_UI_ACTOR @protocol WKNavigationDelegate <NSObject> - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(WK_SWIFT_UI_ACTOR void (^)(WKNavigationActionPolicy))decisionHandler WK_SWIFT_ASYNC(3); Changing the delegate method to: func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping @MainActor (WKNavigationActionPolicy) -> Void) { And it is called across swift 5 concurrency minimal to complete to swift 6. I thought, the meaning of @preconcurrency import WebKit was to keep the delegate without @MainActor before the (WKNavigationActionPolicy) still matching regardless the swift concurrency mode? My point is - this can introduce hidden breaking changes? I didn't see this documented anyhow at: https://www.swift.org/migration/documentation/migrationguide/. decidePolicyFor is an optional method - so if signature 'mismatches' - there will be no warning on not-implementing the delegate method. How do we catch or diagnose irregularities like this? Is it something @preconcurrency import WebKit should be ensuring and it is not? Is this delegate mismatch a bug on swift side or something we should be taking care of while migrating? If it is on us, how do we diagnose these potential mismatches?
1
0
628
Jan ’25