Meet async/await in Swift

RSS for tag

Discuss the WWDC21 session Meet async/await in Swift.

View Session

Posts under wwdc21-10132 tag

19 Posts
Sort by:
Post not yet marked as solved
2 Replies
224 Views
Good day! It seems to be a bug in Xcode that preview can be showed correctly with async code wrapped in Task, for ex: struct MyView: View { var onTapped: (() async -> Bool)? var body: some View { Button {             Task {                 guard await onTapped?() ?? true else { return }             } .....         } label: { Text("Tap me")         } } }      Crash log
Posted
by
Post not yet marked as solved
0 Replies
251 Views
I'm writing an app that performs actions in a web browser automatically on user's behalf. Currently my app uses OperationQueue to perform asynchronous work. I'm trying to rewrite the app to use async/await instead. Note, that it is necessary for my app to limit the number of concurrently executing work items. Otherwise the app may run into server throttling issues. However, I can't find an equivalent to OperationQueue's maxConcurrentOperationCount parameter in the new concurrency model. Is there a way to limit the number of concurrently running "lanes" of execution without resorting to using OperationQueue / DispatchQueue / NSLock ?
Posted
by
Post marked as solved
1 Replies
378 Views
Hi, when working with SwiftUI @Published variables need to be set from the main thread, but at the same time, cannot be defined as @MainActor. The easy solution is a simple forced sync or async execution on the main thread, but it seems like this does not exist in the world of async/await and Tasks - at least I did not find it. Instead I found that all demo's from Apple is a quite sad solution. The full method is marked as @MainActor even though this is often only relevant for the last line. The question is either that I can execute a setter on main only OR that I can mark my SwiftUI code as being on main and thus being able to accept @MainActor @Published Is there really no solution? If so where do I have to propose/request a solution for this. I feel like every developer had written his own version of Thread.performOnMain, but here I want something using Task and that the compiler can verify. All the best Christoph
Posted
by
Post not yet marked as solved
0 Replies
311 Views
For the following example I'm expecting if one of the concurrent tasks has thrown an error, the rest unfinished tasks must be cancelled. But it isn't what I see. If task2 has thrown first, the task1 will continue execution till completion. But it works as expecting in vice-verse, failing task1 first, results task2 cancellation. The log in case if task2 has thrown first: task1 BEGIN task2 BEGIN task2 completed task2 catch CancellationError() task2 END task1 completed // It's not expecting to see this log task1 catch CancellationError() task1 END Failed: CancellationError() The log in case if task1 has thrown first (works as expected, see there is no 'task2 completed' log. As soon as task1 has thrown, the entire try await(,) throws as well.): task1 BEGIN task2 BEGIN task1 completed task1 catch CancellationError() task1 END Failed: CancellationError() task2 catch CancellationError() task2 END Code is below: async let t1 = task1() async let t2 = task2() do { _ = try await (t1, t2) } catch { print("Failed: \(error)") } func task1() async throws -> Int {     print("task1 BEGIN")     defer { print("task1 END") }     do {       try await Task.sleep(nanoseconds: UInt64(3 * 1_000_000_000))       print("task1 completed")       throw CancellationError()     } catch {       print("task1 catch \(error)")       throw error     } } func task2() async throws -> Int {     print("task2 BEGIN")     defer { print("task2 END") }     do {       try await Task.sleep(nanoseconds: UInt64(1 * 1_000_000_000))       print("task2 completed")       throw CancellationError()     } catch {       print("task2 catch \(error)")       throw error     } }
Posted
by
Post marked as solved
1 Replies
248 Views
I have a table to which I've added a refreshControl and when I pull down the table to refresh the data, I reset the array that feeds the table with data and then immediately request new data through an API call. Until now, I have used completion handlers and protocols to get the data into the table view but I want to move the logic to async/await because of the complexity needed by the network calls and the pyramid of nested closures. Populating the view in viewDidLoad works fine but with pullToRefresh selector I get an error: Thread 1: EXC_BAD_ACCESS (code=1, address=0xbcf917df8160) override func viewDidLoad() {     super.viewDidLoad()     setupView()     setupTableView()     setupTableRefreshControl()     Task {       await getBalances() //async network call       myTable.reloadData()     }   }    func setupTableRefreshControl() {     myTable.refreshControl = UIRefreshControl()     myTable.refreshControl?.addTarget(self, action: #selector(didPullToRefresh), for: .valueChanged)   } Code that crashes app:    @objc func didPullToRefresh() async {     balance.reset() // reset array to []     Task {       await getBalances() //async network call       myTable.reloadData()     }   }
Posted
by
Post not yet marked as solved
5 Replies
588 Views
My app crashed when launch. The crash report: OS Version: iPhone OS 15.4 (19E5209h) Release Type: Beta Report Version: 104 Exception Type: EXC_CRASH (SIGABRT) Exception Codes: 0x0000000000000000, 0x0000000000000000 Exception Note: EXC_CORPSE_NOTIFY Termination Reason: DYLD 4 Symbol missing Symbol not found: _$sSo25NSURLSessionWebSocketTaskC10FoundationE4sendyyAbCE7MessageOYaKF Referenced from: /private/var/containers/Bundle/Application/43FB5827-DE90-4FCF-A716-11A7FD5BE11E/Kingbox.app/Kingbox Expected in: /usr/lib/swift/libswiftFoundation.dylib (terminated at launch; ignore backtrace) Triggered by Thread: 0 Thread 0 Crashed: 0 dyld 0x00000001056506d0 __abort_with_payload + 8 1 dyld 0x00000001056562c8 abort_with_payload_wrapper_internal + 104 (terminate_with_reason.c:102) 2 dyld 0x00000001056562fc abort_with_payload + 16 (terminate_with_reason.c:124) 3 dyld 0x00000001056267e8 dyld4::halt(char const*) + 328 (DyldProcessConfig.cpp:2067) 4 dyld 0x0000000105623920 dyld4::prepare(dyld4::APIs&, dyld3::MachOAnalyzer const*) + 3560 (dyldMain.cpp:0) 5 dyld 0x0000000105621c84 start + 488 (dyldMain.cpp:864) Demangle the symbol: _$sSo25NSURLSessionWebSocketTaskC10FoundationE4sendyyAbCE7MessageOYaKF (extension in Foundation):__C.NSURLSessionWebSocketTask.send((extension in Foundation):__C.NSURLSessionWebSocketTask.Message) async throws -> ()
Posted
by
Post marked as solved
3 Replies
911 Views
After I updated xcode to 13.3 version, almost all mt spm project warns. SourcePackages/checkouts/SwiftUI-MediaPicker/SwiftUI.UIViewControllerRepresentable:7:24: Static method '_makeView(view:inputs:)' isolated to global actor 'MainActor' can not satisfy corresponding requirement from protocol 'View' It's pretty bad, because I don' wanna any warnings.
Posted
by
Post not yet marked as solved
0 Replies
314 Views
I am trying to understand as to why following piece of code throws an assertion. What I am trying to do is to call asyncFunc() on main thread/main actor from call site. I don't want to decorate asyncFunc with @MainActor as I want the function to be thread agnostic. func asyncFunc() async -> String? { dispatchPrecondition(condition: .onQueue(.main)) return "abc" } func callSite() { Task { @MainActor in await asyncFunc() } } My understanding was that Task { @MainActor ...} would execute all the following code on MainActor/main thread.
Posted
by
Post marked as solved
2 Replies
591 Views
I am running Mac OS 12.1 and Xcode 13.2. My code is the following: import Foundation import Cocoa func trapezoid(_ begin: Double, _ end: Double,_ n: Int) async -> Double { let hc = (end-begin)/Double(n) var h = begin var result = 0.0 for _ in 0...n{ result += h*h + (h+hc)*(h+hc) h = h+hc } return result * (hc/2) } Task{ let answer1 = await trapezoid(0, 1, 10000) let answer2 = await trapezoid(1, 2, 10000) print(answer1) print(answer2) } With playgrounds I get the following correct result: 0.33343334500044286 2.333733355000306 However, when I use the exact same code in an Xcode project using Command Line Tools, the program just returns with exit code 0 and no output. I am not sure what my error is or if it is an Xcode bug.
Posted
by
Post marked as solved
1 Replies
480 Views
The function in question is the following. func records(for ids: [CKRecord.ID], desiredKeys: [CKRecord.FieldKey]? = nil) async throws -> [CKRecord.ID : Result<CKRecord, Error>] Note that the returned value looks like a dictionary of the form [CKRecord.ID : Result<CKRecord, Error>], but the input is an array of CKRecord.IDs. There are similar functions, but they return a tuple. Like the below example. func records(matching query: CKQuery, inZoneWith zoneID: CKRecordZone.ID? = nil, desiredKeys: [CKRecord.FieldKey]? = nil, resultsLimit: Int = CKQueryOperation.maximumResults) async throws -> (matchResults: [(CKRecord.ID, Result<CKRecord, Error>)], queryCursor: CKQueryOperation.Cursor?) Note that matchedResults is an array of tuples consisting of [(CKRecord.ID, Result<CKRecord, Error>)]. I would have thought that the return type in the first function would also be of the form [(CKRecord.ID, Result<CKRecord, Error>)]. What am I missing?
Posted
by
Post not yet marked as solved
0 Replies
587 Views
Anybody converting from CKQueryOperation to the new CloudKit async await methods? If so, what do you think? I'm currently playing with func records(matching query: CKQuery, inZoneWith zoneID: CKRecordZone.ID? = nil, desiredKeys: [CKRecord.FieldKey]? = nil, resultsLimit: Int = CKQueryOperation.maximumResults) async throws -> (matchResults: [(CKRecord.ID, Result<CKRecord, Error>)], queryCursor: CKQueryOperation.Cursor?) and func records(continuingMatchFrom queryCursor: CKQueryOperation.Cursor, desiredKeys: [CKRecord.FieldKey]? = nil, resultsLimit: Int = CKQueryOperation.maximumResults) async throws -> (matchResults: [(CKRecord.ID, Result<CKRecord, Error>)], queryCursor: CKQueryOperation.Cursor?) They seem to work fine and eliminate completion handlers.
Posted
by
Post not yet marked as solved
1 Replies
1.3k Views
When using Task { ... } in Swift 5.5, I'm unclear if retain cycles might be an issue or not. For example: class ViewController: UIViewController { private var rows = [String]() private var tableView: UITableView? func populateRows() { Task { rows = (try? await getRowsFromNetwork()) ?? [] tableView?.reloadData() } } } Will populateRows retain the view controller for the life of the task because it references rows and tableView? If the view controller goes out of scope, I'd like it to have the freedom to deallocate without populateRows retaining it. This syntax compiles, but I don't know if it makes sense: class ViewController: UIViewController { private var rows = [String]() private var tableView: UITableView? func populateRows() { Task { [weak self] in self?.rows = (try? await self?.getRowsFromNetwork()) ?? [] self?.tableView?.reloadData() } } } I don't mind if the task continues to run. (I'm not worried about canceling it.) I just want to understand if there is a way to avoid retaining the view controller in the task, and if that happens automatically or if something like [weak self] is needed.
Posted
by
Post not yet marked as solved
0 Replies
467 Views
I'm new to async/await, and am currently migrating my completion handler code to Swift 5.5's concurrency features. After generating an sync alternative in Xcode to my function func fetchMatchRecords(completion: @escaping ([Match]) -> Void), it becomes func fetchMatchRecords() async -> [Match]. I'm not sure how it would be used in the context of UIKit and diffable data sources. In a viewDidLoad, previously it would be MatchHistoryController.shared.fetchMatchRecords() { matches in DispatchQueue.main.async { self.dataSource.apply(self.initialSnapshot(), animatingDifferences: false) } } But I'm not sure how it would be used now Task { await MatchHistoryController.shared.fetchMatchRecords() } self.dataSource.apply(self.initialSnapshot(), animatingDifferences: false) How would I make sure that the snapshot is applied only after awaiting a successful fetch result? Here's the definition of initialSnapshot() that I used: func initialSnapshot() -> NSDiffableDataSourceSnapshot<Section, Match> { var snapshot = NSDiffableDataSourceSnapshot<Section, Match>() snapshot.appendSections([.main]) snapshot.appendItems(MatchHistoryController.shared.matches) return snapshot }
Posted
by
Post not yet marked as solved
10 Replies
2.2k Views
I'm seeing an error trying to test out async let. It seems like this should work, based on the async/let and structured concurrency session videos. Here's the code: func doIt() async -> String {     let t = TimeInterval.random(in: 0.25 ... 2.0)     Thread.sleep(forTimeInterval: t)     return String("\(Double.random(in: 0...1000))") } async {     async let a = doIt()     async let b = doIt()     async let c = doIt()     async let d = doIt()     let results = await [a, b, c, d]     for result in results {         print("  \(result)")     } } But, I get this error for every "async let" statement: error: AsyncLetSwift55WWDC21.playground:12:15: error: expression is 'async' but is not marked with 'await' async let a = doIt() ^ await Am I missing something key, or is this a bug? I'm running this in the Xcode 13.0 beta, in a Playground. Thanks!!
Posted
by
Post not yet marked as solved
2 Replies
928 Views
Hi, We have an API that I'm trying to prototype some async await extensions for. Most of our asynchronous methods are cancelable by returning an object that implements our AGSCancelable protocol that includes a cancel() method. Here's the code I have to try to extend a geocoding method on our AGSLocatorTask class: extension AGSLocatorTask { func geocode(withSearchText text: String) async throws -> [AGSGeocodeResult] { typealias GeocodeContinuation = CheckedContinuation<[AGSGeocodeResult], Error> var cancelable: AGSCancelable? return try await withTaskCancellationHandler(handler: { cancelable?.cancel() }, operation: { return try await withCheckedThrowingContinuation({ (continuation: GeocodeContinuation) in cancelable = self.geocode(withSearchText: text) { results, error in if let error = error { continuation.resume(throwing: error) } else { continuation.resume(returning: results!) } } }) }) } } This works great, but I have to comment out the cancelable?.cancel() call or else I get a Reference to captured var 'cancelable' in concurrently-executing code static analysis error (see attached screenshot). Could someone explain how to fix this, or is this a bug in the Xcode beta? Many thanks, and thanks for a great WWDC!!
Posted
by
Post not yet marked as solved
2 Replies
898 Views
How can we call async functions in did set blocks? This code will trigger a compilation error: 'didSet' accessor cannot have specifier 'async' @Published var lastLocation: CLLocation? { didSet async { await pinPosition() } }
Posted
by