Concurrency

RSS for tag

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

Pinned Posts

Posts under Concurrency tag

89 Posts
Sort by:
Post marked as solved
1 Replies
944 Views
Code below from the video: Generalize APIs with parameter packs protocol RequestProtocol { associatedtype Input associatedtype Output func evaluate(_ input: Input) -> Output } struct Evaluator<each Request: RequestProtocol> { var item: (repeat each Request) func query( _ input: repeat (each Request).Input ) -> (repeat (each Request).Output) { return (repeat (each item).evaluate(each input)) } } The Evaluator declaration causes a compiler error, "Generic types with parameter packs are experimental". Is there a switch or flag to enable use of parameter packs?
Posted
by
Post not yet marked as solved
7 Replies
1.3k Views
When building our project in Xcode 14.3, numerous weird warnings do not seem to be related to our code: <unknown>:0: warning: cannot form key path that captures non-sendable type 'KeyPath<AttributeScopes.FoundationAttributes, AttributeScopes.FoundationAttributes.DateFieldAttribute>' Swift.KeyPath:1:14: note: generic class 'KeyPath' does not conform to the 'Sendable' protocol public class KeyPath<Root, Value> : PartialKeyPath<Root> { ^ <unknown>:0: warning: cannot form key path that captures non-sendable type 'KeyPath<AttributeScopes.UIKitAttributes, AttributeScopes.UIKitAttributes.FontAttribute>' Swift.KeyPath:1:14: note: generic class 'KeyPath' does not conform to the 'Sendable' protocol public class KeyPath<Root, Value> : PartialKeyPath<Root> { ^ <unknown>:0: warning: cannot form key path that captures non-sendable type 'KeyPath<AttributeScopes.UIKitAttributes, AttributeScopes.UIKitAttributes.FontAttribute>' Swift.KeyPath:1:14: note: generic class 'KeyPath' does not conform to the 'Sendable' protocol public class KeyPath<Root, Value> : PartialKeyPath<Root> { ^ It is just a tiny portion of warnings we wee in build results each time. Hundreds of them make it impossible to spot other warnings that these logs might obscure. We can't figure out where they come from, what triggers them, and, more importantly, how to eliminate or at least suppress them. Can anyone suggest any workaround or give a clue on how to fix these warnings?
Posted
by
Post not yet marked as solved
0 Replies
1.4k Views
In What's New In Swift, a new DispatchSerialQueue-backed actor was introduced. We're able to call MainActor-annotated functions using DispatchQueue.main.async without errors or warnings. For example: @MainActor func randomFunc() { print("Hello World") } DispatchQueue.main.async { randomFunc() } However calling a globalActor-annotated function or a regular actor-isolated function backed by a DispatchSerialQueue, we get the warning Actor-isolated instance method 'randomFunc()' can not be referenced from a non-isolated context; this is an error in Swift 6. Code here: actor MyActor { private let queue: DispatchSerialQueue nonisolated var unownedExecutor: UnownedSerialExecutor { queue.asUnownedSerialExecutor() } init(queue: DispatchSerialQueue) { self.queue = queue } func randomFunc() { print("Hello World!") } } let queue = DispatchSerialQueue(label: "actorQueue") let actor = MyActor(queue: queue) queue.async { actor.randomFunc() // Warning here } Although it has a warning, the code still runs successfully and prints Hello World, but it would also do so from another DispatchQueue not used by the actor (this was tested in Version 15.0 beta (15A5160n) Playground). My question: Can we remove the warning resulting from calling an actor isolated function backed by DispatchSerialQueue A using A.async { }, if that's safe behavior? If it's not safe, why not?
Posted
by
Post not yet marked as solved
9 Replies
3.6k Views
Previously, it was recommended to use the @MainActor annotation for ObservableObject implementation. @MainActor final class MyModel: ObservableObject { let session: URLSession @Published var someText = "" init(session: URLSession) { self.session = session } } We could use this as either a @StateObject or @ObservedObject: struct MyView: View { @StateObject let model = MyModel(session: .shared) } By moving to Observation, I need to the @Observable macro, remove the @Published property wrappers and Switch @StateObject to @State: @MainActor @Observable final class MyModel { let session: URLSession var someText = "" init(session: URLSession) { self.session = session } } But switching from @StateObject to @State triggers me an error due to a call to main-actor isolated initialiser in a synchronous nonisolated context. This was not the case with @StateObject of @ObservedObject. To suppress the warning I could : mark the initializer as nonisolated but it is not actually what I want Mark the View with @MainActor but this sounds odd Both solutions does not sound nice to my eye. Did I miss something here?
Posted
by
Post not yet marked as solved
1 Replies
921 Views
I'm working on an iOS project and I'm wondering if it's possible to mix Combine and the new async/await feature in Swift. I want to leverage the power of Combine's reactive programming paradigm along with the simplified asynchronous code flow provided by async/await. Has anyone tried mixing these two approaches in the same project? Are there any known issues or considerations to keep in mind? Are there any best practices or patterns to follow when combining Combine and async/await? I would appreciate any insights or experiences shared. Thank you in advance!
Posted
by
Post not yet marked as solved
1 Replies
730 Views
After changing our main method to become async, with a logic that looks like the following: static func main() async throws { // A special startup case that does something in the background and kills the app if someCondition { try await someAsyncCode() exit(0) } _ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv) } we started seeing several crashes that start like this: ... etc 30 AppKit _DPSNextEvent 31 AppKit -[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] 32 AppKit -[NSApplication run] 33 AppKit NSApplicationMain 34 MyApp (1) suspend resume partial function for static AppMain.main() (<compiler-generated>:0) 35 MyApp [crash.txt](https://developer.apple.com/forums/content/attachment/33f37f09-32be-465b-a2e2-7312fab10597) (1) await resume partial function for specialized thunk for @escaping @convention(thin) @async () -> () (<compiler-generated>:0) 36 libswift_Concurrency.dylib completeTaskAndRelease(swift::AsyncContext*, swift::SwiftError*) I realize there's not a lot of detail and I'm willing to expand on the code and the errors if needed, but I'm wondering if anyone has seen this type of issue after making their main method async.
Posted
by
Post not yet marked as solved
2 Replies
2k Views
SWIFT TASK CONTINUATION MISUSE: saveAndClose() leaked its continuation! Inside for loop, after executing few items, at one of the item, in save and close function, it is blocked, and says above error, and not moving to next item, so not able to return result. What could be wrong here, and is there any way to optimize any of these snippets? private func processTags(reqItems: [FTShelfItemProtocol], selectedTags: [String]) async throws -> FTShelfTagsResult { let items: [FTDocumentItemProtocol] = reqItems.filter({ ($0.URL.downloadStatus() == .downloaded) }).compactMap({ $0 as? FTDocumentItemProtocol }) var totalTagItems: [FTShelfTagsItem] = [FTShelfTagsItem]() for case let item in items where item.documentUUID != nil { guard let docUUID = item.documentUUID else { continue }//, item.URL.downloadStatus() == .downloaded else { continue } let destinationURL = FTDocumentCache.shared.cachedLocation(for: docUUID) print(destinationURL.path) // move to post processing phace do { let document = await FTNoteshelfDocument(fileURL: destinationURL) let isOpen = try await document.openDocument(purpose: FTDocumentOpenPurpose.read) if isOpen { let tags = await document.documentTags() let considerForResult = selectedTags.allSatisfy(tags.contains(_:)) if considerForResult && !tags.isEmpty { var tagsBook = FTShelfTagsItem(shelfItem: item, type: .book) tagsBook.tags = tags totalTagItems.append(tagsBook) } } let tagsPage = await document.fetchSearchTagsPages(shelfItem: item, selectedTags: selectedTags) totalTagItems.append(contentsOf: tagsPage) _ = await document.saveAndClose() } catch { cacheLog(.error, error, destinationURL.lastPathComponent) } } cacheLog(.success, totalTagItems.count) let result = FTShelfTagsResult(tagsItems: totalTagItems) return result } func saveAndClose() async -> Bool { return await withCheckedContinuation({ continuation in self.saveAndCloseWithCompletionHandler { isSuccess in continuation.resume(returning: isSuccess) } }) }
Posted
by
Post not yet marked as solved
1 Replies
1.5k Views
I am using a preview with my view that looks like this - #Preview { PaywallTwo(store: MyStore()) } In this scenario, MyStore is a MainActor, but has an initialiser doing some async work inside a Task from the initialiser. I am not sure if this is an implementation error, or perhaps something wrong with the new #Preview, as this was working fine with PreviewProvider I currently have complete strict concurrency, and the error I get is: call to main actor-isolated initializer 'init()' in a synchronous nonisolated context
Posted
by
Post marked as solved
8 Replies
3.2k Views
I see this warning when my app runs: Thread running at QOS_CLASS_USER_INTERACTIVE waiting on a lower QoS thread running at QOS_CLASS_DEFAULT. Investigate ways to avoid priority inversions This is true; I know what is going on. I'd like this other thread to have a higher priority. But how do I set the "QOS class" for a thread? Searching developer.apple.com for QOS_CLASS_USER_INTERACTIVE doesn't find much. It seems that dispatch queues have priorities, but in this case I have a thread, not a dispatch queue. Any ideas?
Posted
by
Post not yet marked as solved
1 Replies
786 Views
I had to update a simple piece of code, so it could be delayed by a few ms in some cases. This is how I tried to achieve that, by using a Task: class SomeClass { private var availableStuff : Set&lt;Stuff&gt; = [] func updateStuff(lookingFor prop: SomeProp, updatedThing: String, waiting: Duration = .zero) { Task { if waiting != .zero { try await Task.sleep(for: waiting) } if var stuff = availableStuff.first(where: { $0.prop == prop }) { stuff.thing = updatedThing // print(availableStuff) self.availableStuff.remove(stuff) self.availableStuff.insert(stuff) // BAD ACCESS here } } } } It did work before implementing the delay, but now it would crash at the insert statement (sometimes at remove). Naively, I put a print statement beforehand and to my surprise, this kept the crash from occurring at all! I switched on Address Sanitizer, now it doesn't crash, even w/o the print statement. Am I using the Task wrong, somehow? How can I further diagnose this, so it doesn't happen later in production? Or should I just leave the print in and hope for the best? Thanks!
Posted
by
Post not yet marked as solved
4 Replies
2.1k Views
In the WWDC 23 lounge, this exchange with @Dave N (Apple) indicated that using ModelActor is the right way to do background work using swift concurrency. https://developer.apple.com/forums/thread/731338 I've figured out how to use this approach to do background work using swift concurrency (inside a Task), and example code is below for those who find it useful, however I'm not seeing view updates when the background work is complete. There seems to be no way to trigger a view update based on a @Query when inserts happen in a ModelActor’s context. However, if a delete happens on the ModelActor context, this DOES trigger a view redraw. I believe this is a bug because I expect the behavior to be the same for inserts and deletes. I've submitted this as Feedback FB12689036. Below is a minimal project which is the default SwiftData template, where the Add and Delete buttons point to a ModelActor instead of the main view ModelContext. You will see that using the addItem function in the ModelActor does not trigger a UI update in ContentView. However if you relaunch the app the added Item will be present. In contrast, using the delete function on the ModelActor context does trigger an immediate view update in ContentView. If this is intended behavior, we need a way to merge changes from background contexts, similar to what is described in the Core Data document “Loading and Displaying a Large Data Feed”: https://developer.apple.com/documentation/swiftui/loading_and_displaying_a_large_data_feed In Core Data we have automaticallyMergesChangesFromParent and mergeChanges(fromContextDidSave:) to do this manually. There seems to be no equivalent for Swift Data. If anyone has solved this problem of merging changes from other contexts, or can confirm that this is a bug, please let me know. import SwiftUI import SwiftData struct ContentView: View { @Environment(\.modelContext) private var modelContext @Query private var items: [Item] @State private var simpleModelActor: SimpleModelActor! var body: some View { NavigationView { List { ForEach(items) { item in NavigationLink { Text("Item at \(item.timestamp, format: Date.FormatStyle(date: .numeric, time: .standard))") } label: { Text(item.timestamp, format: Date.FormatStyle(date: .numeric, time: .standard)) } } .onDelete(perform: deleteItems) } .toolbar { ToolbarItem(placement: .navigationBarTrailing) { EditButton() } ToolbarItem { Button(action: addItem) { Label("Add Item", systemImage: "plus") } } } Text("Select an item") } .onAppear { simpleModelActor = SimpleModelActor(modelContainer: modelContext.container) } } private func addItem() { Task { await simpleModelActor.addItem() } } private func deleteItems(offsets: IndexSet) { Task { for index in offsets { await simpleModelActor.delete(itemWithID: items[index].objectID) } } } } import Foundation import SwiftData final actor SimpleModelActor: ModelActor { let executor: any ModelExecutor init(modelContainer: ModelContainer) { let modelContext = ModelContext(modelContainer) executor = DefaultModelExecutor(context: modelContext) } func addItem() { let newItem = Item(timestamp: Date()) context.insert(newItem) try! context.save() // this does not impact a re-display by the @Query in ContentView. I would have expected it to cause a view redraw. } func delete(itemWithID itemID: Item.ID) { let item = context.object(with: itemID) context.delete(object: item) // this DOES cause a view redraw in ContentView. It triggers an update by @Query. // try! context.save() // this makes do difference to view redraw behavior. } }
Posted
by
Post marked as solved
1 Replies
835 Views
I'm having trouble understanding the use case for discardingTaskGroup. In my app, I want to submit 10 concurrent image upload requests; usually, I'd just fire off 10 unstructured Task {} instances (I'm assuming this is fine) for image in images { Task { do { try await uploadImage(item: item, image: image) } catch { // Handle any errors } } } But then I thought I'd actually like to have a max of ~3 uploads concurrently, where I would prioritize the images that appear to the user earlier first. I know using group.next() in a taskGroup we can await on previous results and add tasks as required. But my task does not return data, rather it performs an action. So, it seems like the new discardingTaskGroup could be a useful API. Task { do { try await withThrowingDiscardingTaskGroup { group in for image in images { group.addTask { try await uploadImage(item: item, image: image) } } } } catch { // Handle any errors } } How can I convert this discarding task group code to only include a max of n tasks running concurrently? And is this even a reasonable use of the new API to begin with? Best, T
Posted
by
Post not yet marked as solved
1 Replies
436 Views
Hi. In Apple's SwiftUI Tutorial Scrumdinger, there's a logic that read and save a file in asynchronous function to keep the App's UI responsive. The tutorial says, Reading from the file system can be slow. To keep the interface responsive, you’ll write an asynchronous function to load data from the file system. Making the function asynchronous lets the system efficiently prioritize updating the user interface instead of sitting idle and waiting while the file system reads data. And here's code. @MainActor class ScrumStore: ObservableObject { @Published var scrums: [DailyScrum] = [] func load() async throws { let task = Task<[DailyScrum], Error> { let fileURL = try Self.fileURL() guard let data = try? Data(contentsOf: fileURL) else { return [] } let dailyScrums = try JSONDecoder().decode([DailyScrum].self, from: data) return dailyScrums } let scrums = try await task.value } func save(scrums: [DailyScrum]) async throws { let task = Task { let data = try JSONEncoder().encode(scrums) let outfile = try Self.fileURL() try data.write(to: outfile) } _ = try await task.value } } But the problem i think here is that the ScrumStore is Mainactor isolated annotated, the 'load' and 'save' method execute their Tasks in the main thread, so I think there's not much benefit we can get comparing using a synchronous method. The proper way I think here is that use 'Task.detached' initializer instead of 'Task' initializer, so that the Task doesn't isolated in MainActor context. If there's anything I understand wrong or miss, please let me know. Apple's Scrumdinger Tutorial Link
Posted
by
Post marked as solved
2 Replies
868 Views
Hi all, There appears to be some changes to how ModelActors are implemented in Beta 7 of Xcode 15. In Beta 6, we had the DefaultModelExecutor object which we used to assign to the ModelActor's executor property. A simple example is shown below: final actor SimpleModelActor: ModelActor { let executor: any ModelExecutor init(modelContainer: ModelContainer) { let modelContext = ModelContext(modelContainer) executor = DefaultModelExecutor(context: modelContext) } func addItem() { let newItem = Item(timestamp: Date()) context.insert(newItem) try! context.save() } .... } However, it seems that the DefaultModelExecutor class has been removed for Beta 7, without any real replacement. Anyone know how to implement a ModelActor in the new beta? Thanks in advance.
Posted
by
Post not yet marked as solved
0 Replies
513 Views
Currently, PhotosPickerItem cannot be used from async/await code safely, for example in Tasks etc., because it does not conform to Sendable. The public api has only two properties, both of which are of a Sendable type: UTType and String?. Is this something that's going to be changed so I can mark it as @unchecked Sendable in my code, or is there an actual underlying reason why PhotosPickerItem is not Sendable? PHAsset, PHAssetCollection, PHFetchRequest, PHObject are all sendable, because - similarly to PhotosPickerItem - they contain no actual image data, they are just a preliminary representation of an image in the Photos app.
Posted
by
Post not yet marked as solved
5 Replies
686 Views
In a thread titled “Avoid Dispatch Global Concurrent Queues” [1], Quinn links to a video from WWDC 2015 Session 718, “Building Responsive and Efficient Apps with GCD”. However, this video is not available from the Apple Developer Videos site; only a half dozen or so videos from 2015 are available. This same issue of the missing video came up about five years ago, when Quinn stated that the video had been mistakenly removed but had been restored. Now it’s gone again. :sad_face: Could this video be restored again, or at least its transcript? While I understand that Apple is focused on Swift concurrency, I need to maintain some Objective-C code that uses GCD, and in tracking down some performance issues, I would like to better understand the tradeoffs in the existing code and make improvements where I can. I don’t have the resources to reimplement the code in Swift right now. (More generally, why can't Apple just leave all these videos online indefinitely, for historical purposes at least? Couldn't the ones deemed “old and misleading” just be tagged with a banner like the legacy documentation has?) [1] I like to think of these valuable threads as “Quinn Technical Notes”; I have a page in my Notes app that holds links to the ones I’ve found.
Posted
by
Post not yet marked as solved
9 Replies
1.1k Views
Hello, I am trying to debug Swift Concurrency codes by using Swift Concurrency Instruments. But in our project which has been maintained for a long time, Swift Concurrency Instruments seems not working. Task { print("async code") await someAsyncFunction() } When I run Swift Concurrency Instruments with the above codes in our project, I could check that the print works well by checking stdout/stderr Instruments, but there are no records on Swift Concurrency Instruments. But in the small simple sample project, I checked that Swift Concurrency Instruments works well. Are there any settings that I need to do for the legacy project to debug Swift Concurrency?
Posted
by
Post marked as solved
4 Replies
818 Views
My request call site: let headers = coordinator.requestHeaders(path: headersPath) var request = URLRequest(url: url, cachePolicy: .useProtocolCachePolicy, timeoutInterval: defaultTimeoutInterval) request.httpMethod = HTTPRequestMethod.get.rawValue request.allHTTPHeaderFields = headers let delegate = SessionDelegate(priority: priority, progressHandler: progressHandler) let session = URLSession(configuration: configuration, delegate: delegate, delegateQueue: .main) let (localUrl, response) = try await session.download(for: request) let data = try Data(contentsOf: localUrl) SessionDelegate: public class SessionDelegate: NSObject, URLSessionTaskDelegate, URLSessionDownloadDelegate { public typealias ProgressHandler = ((_ progress: Double) -&gt; Void) public var metrics: URLSessionTaskMetrics? private let priority: TaskPriority private let progressHandler: ProgressHandler? //MARK: - Lifecycle init(priority: TaskPriority, progressHandler: ProgressHandler? = nil) { self.priority = priority self.progressHandler = progressHandler } //MARK: - URLSessionTaskDelegate public func urlSession(_ session: URLSession, didCreateTask task: URLSessionTask) { task.priority = priority.rawValue } public func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) { self.metrics = metrics } public func urlSession(_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) { if let progressHandler { let progress = Progress(totalUnitCount: totalBytesExpectedToSend) progress.completedUnitCount = totalBytesSent progressHandler(progress.fractionCompleted) } } //MARK: - URLSessionDownloadDelegate public func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) { } public func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) { if let progressHandler { let progress = Progress(totalUnitCount: totalBytesExpectedToWrite) progress.completedUnitCount = totalBytesWritten progressHandler(progress.fractionCompleted) } } } For some reason I get delegate callbacks from URLSessionDelegate and URLSessionTaskDelegate but never from URLSessionDownloadDelegate. I've tried assigning the delegate to the task or the session (or both), but behavior remains the same. Not sure if related but when I try using background session configuration I crash on let (localUrl, response) = try await session.download(for: request) with the error Completion handler blocks are not supported in background sessions. Use a delegate instead. even though I use the async version of the download task, and assigning delegate to the session, it might point to the reason why none of the delegate functions is getting called (maybe?), but I have no clue why Xcode thinks I'm using completion handler.
Posted
by