Meet Swift Async Algorithms

RSS for tag

Discuss the WWDC22 Session Meet Swift Async Algorithms

Posts under wwdc2022-110355 tag

6 Posts

Post

Replies

Boosts

Views

Activity

Problem Running multiple Async tasks in View
Hey there, I have a problem running multiply tasks in parallel in a SwiftUI view. struct ModelsView: View { @StateObject var tasks = TasksViewModel() var body: some View { NavigationView{ ScrollView { ForEach(Array(zip(tasks.tasks.indices, tasks.tasks)), id: \.0) { task in NavigationLink(destination: ModelView()) { ModelPreviewView(model_name: "3dobject.usdz") .onAppear { if task.0 == tasks.tasks.count - 2 { Task { print(tasks.tasks.count) await tasks.fetch_tasks(count: 4) } } } } } }.navigationTitle("3D modelle") }.onAppear{ Task { await tasks.fetch_tasks(count: 5) await tasks.watch_for_new_tasks() } } } } In my view, I spawn a task as soon as the View Appears which, first, fetches 5 tasks from the database (this works fine), and then it starts watching for new tasks. In the Scroll View, right before the bottom is reached, I start loading new tasks. The problem is, the asynchronous function fetch_tasks(count: 4) only gets continued if the asynchronous function watch_for_new_tasks() stops blocking. actor TasksViewModel: ObservableObject { @MainActor @Published private(set) var tasks : [Tasks.Task] = [] private var last_fetched_id : String? = nil func fetch_tasks(count: UInt32) async { do { let tasks_data = try await RedisClient.shared.xrevrange(streamName: "tasks", end: last_fetched_id ?? "+" , start: "-", count: count) last_fetched_id = tasks_data.last?.id let fetched_tasks = tasks_data.compactMap { Tasks.Task(from: $0.data) } await MainActor.run { withAnimation(.easeInOut) { self.tasks.append(contentsOf: fetched_tasks) } } } catch { print("Error fetching taskss \(error)") } } func watch_for_new_tasks() async { while !Task.isCancelled { do { let tasks_data = try await RedisClient.shared.xread(streams: "tasks", ids: "$") let new_tasks = tasks_data.compactMap { Tasks.Task(from: $0.data) } await MainActor.run { for new_task in new_tasks.reversed() { withAnimation { self.tasks.insert(new_task, at: 0) } } } } catch { print(error) } } } ... } The asynchronous function watch_for_new_tasks() uses RedisClient.shared.xread(streams: "tasks", ids: "$") which blocks until at least one tasks is added to the Redis Stream. I tried running the watch_for_new_tasks() on a Task.detached tasks, but that also blocks. To be honest, I have no idea why this blocks, and I could use your guy's help if you could. Thank you in Advance, Michael
1
0
1.7k
Oct ’23
Asynchronous Video Encoding using Tencent SDK
Hello, developers and expert, trying to implement encoding video simply using tencent sdk but I got error, and continuation doesn't return any progress or result. anyone help me please... what i am missing? SWIFT TASK CONTINUATION MISUSE: generateVideo(_:) leaked its continuation! 2023-03-30 12:07:15.694914+0900 Metacellar (dev)[87044:19556775] SWIFT TASK CONTINUATION MISUSE: generateVideo(_:) leaked its continuation! func encodeVideo() async throws { if let video { let encoded = try await TencentVideoEncoder.generateVideo(video) } } import TXLiteAVSDK_Professional class TencentVideoEncoder: NSObject { static func generateVideo(_ original: AVAsset) async throws -> AVAsset { let inputPath = try await Cache.saveToLocal( asset: original, cache: Cache.temporary, cacheKey: UUID().uuidString) let outputPath = MetaCellarCache.temporary.cachePath(forKey: UUID().uuidString)! let editor = TXVideoEditer() editor.setVideoBitrate(2300) editor.setVideoPath(inputPath) editor.setCutFromTime(0, toTime: 10) return try await withCheckedThrowingContinuation { continuation in let listener = Listener(onComplete: { result in switch result.retCode { case .GENERATE_RESULT_OK: continuation.resume(returning: AVAsset(url: URL(fileURLWithPath: outputPath))) default: continuation.resume(throwing: NSError(domain: "Encoding Failed", code: result.retCode.rawValue)) } }, onProgress: { progress in print(progress) }) editor.generateDelegate = listener editor.generateVideo(withTwoPass: .VIDEO_COMPRESSED_720P, videoOutputPath: outputPath) } } class Listener: NSObject, TXVideoGenerateListener { private var onComplete: (TXGenerateResult) -> () private var onProgress: (Float) -> () init(onComplete: @escaping (TXGenerateResult) -> (), onProgress: @escaping (Float) -> ()) { self.onComplete = onComplete self.onProgress = onProgress } func onGenerateComplete(_ result: TXGenerateResult) { onComplete(result) } func onGenerateProgress(_ progress: Float) { onProgress(progress) } } }
0
0
789
Mar ’23
AsyncStream(unfolding:) cancels early
So I have been trying to figure out how to solve an issue all day today. Basically, I have two async streams and I am using async-algorithms combineLatest to merge them, and then .map to select a value from one of the streams. Unfortunately, there is no "AnyAsyncSequence" so I can't type erase. As such, I have wrapped this sequence in an AsyncStream. I noticed that by doing this, the observer of this stream seems to cancel early. When in reality it should only be ending when my class deinitializes. Here is the code that does not work: var values: AsyncStream<MyValue> { let stream = combineLatest(self.myFirstStream, self.mySecondStream) .map(self.selectValueToReturn) return AsyncStream { for await value in stream { // For some reason, only a few values get returned here and then the stream gets cancelled. return value } return nil } } Strangely enough, if I switch to a continuation based AsyncStream. It works fine: var values: AsyncStream<FeatureConfig> { return AsyncStream { continuation in Task { [weak self] in guard let self else { return } let stream = combineLatest(self.myFirstStream, self.mySecondStream) .map(self.selectValueToReturn) for await value in stream { continuation.yield(value) } continuation.finish() } } }
1
0
938
Mar ’23
A non-destructive URL.lines?
I am working on a Server-Sent-Event parser which has newlines as part of the protocol: https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation If the line is empty (a blank line) Dispatch the event, as defined below. Unfortunately, when using: let (asyncBytes, _) = try await self.session.bytes(from: sseStreamURL) for try await line in asyncBytes.lines { //parse line } The iterator actually skips empty lines entirely, which seems destructive and therefore can't be the only way? Can it? With split there is a omittingEmptySubsequences: false, but I didn't find any trace of anything like that for .lines. I couldn't find the var definition in the Swift repo either, if anyone could point me at it. I did write something that works for now, but I'd love if anyone had any comments on how to improve it / point me at any exiting language features that I missed. extension AsyncSequence where Element == UInt8 {     var allLines:AsyncThrowingStream<String, Error> {         AsyncThrowingStream { continuation in             Task {                 var buffer:[UInt8] = []                 var iterator = self.makeAsyncIterator()                 while let byte = try await iterator.next() {                     // b/c 10 == \n                     if byte != 10 { buffer.append(byte) } else {                         if buffer.isEmpty { continuation.yield("") } else {                             if let line = String(data: Data(buffer), encoding: .utf8) { continuation.yield(line) } else { //Is there a different AsyncSequence error I could use?                                 throw SSEError("allLines: Couldn't make string from [UInt8] chunk")                             }                             buffer = []                         } }                 }             }         }     } } usage: for try await line in asyncBytes.allLines { //parse line } or of course also var iterator = asyncBytes.allLines.makeAsyncIterator() while let line = try await iterator.next() { //parse line }
2
0
952
Feb ’23
Invalid Bundle. The bundle at 'Foo.framework' contains disallowed file 'Frameworks'
In our project setup we have Main App and our code base is divided into Module using Framework, Recently in one of our framework we write some code using swift async-await API. After that We got this error while uploading to TestFlight Invalid Bundle. The bundle at 'Foo.framework' contains disallowed file 'Frameworks'. With error code STATE_ERROR.VALIDATION_ERROR.90206 for id 6822222-72fc-4820-0000-b1f8411d4f04 From stackoverflow we resolved issue by doing following things previously it was YES and working fine ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; Now our build Successfully uploaded to TestFlight and in apple review we got this comment from apple team and they rejected app
3
0
2.4k
Jan ’23
ContinuousClock not working for sleeping
// continuous version let continuousClock = ContinuousClock() let continuousElapsed = try await continuousClock.measure { try await Task.sleep(until: .now + .seconds(5), clock: .continuous) } print(continuousElapsed) // suspending version let suspendingClock = SuspendingClock() let suspendingElapsed = try await suspendingClock.measure { try await Task.sleep(until: .now + .seconds(5), clock: .suspending) } print(suspendingElapsed) result: 0.000126 seconds 5.324980708 seconds Swift version: Apple Swift version 5.7 (swiftlang-5.7.0.113.202 clang-1400.0.16.2)
1
0
1.1k
Jun ’22
Problem Running multiple Async tasks in View
Hey there, I have a problem running multiply tasks in parallel in a SwiftUI view. struct ModelsView: View { @StateObject var tasks = TasksViewModel() var body: some View { NavigationView{ ScrollView { ForEach(Array(zip(tasks.tasks.indices, tasks.tasks)), id: \.0) { task in NavigationLink(destination: ModelView()) { ModelPreviewView(model_name: "3dobject.usdz") .onAppear { if task.0 == tasks.tasks.count - 2 { Task { print(tasks.tasks.count) await tasks.fetch_tasks(count: 4) } } } } } }.navigationTitle("3D modelle") }.onAppear{ Task { await tasks.fetch_tasks(count: 5) await tasks.watch_for_new_tasks() } } } } In my view, I spawn a task as soon as the View Appears which, first, fetches 5 tasks from the database (this works fine), and then it starts watching for new tasks. In the Scroll View, right before the bottom is reached, I start loading new tasks. The problem is, the asynchronous function fetch_tasks(count: 4) only gets continued if the asynchronous function watch_for_new_tasks() stops blocking. actor TasksViewModel: ObservableObject { @MainActor @Published private(set) var tasks : [Tasks.Task] = [] private var last_fetched_id : String? = nil func fetch_tasks(count: UInt32) async { do { let tasks_data = try await RedisClient.shared.xrevrange(streamName: "tasks", end: last_fetched_id ?? "+" , start: "-", count: count) last_fetched_id = tasks_data.last?.id let fetched_tasks = tasks_data.compactMap { Tasks.Task(from: $0.data) } await MainActor.run { withAnimation(.easeInOut) { self.tasks.append(contentsOf: fetched_tasks) } } } catch { print("Error fetching taskss \(error)") } } func watch_for_new_tasks() async { while !Task.isCancelled { do { let tasks_data = try await RedisClient.shared.xread(streams: "tasks", ids: "$") let new_tasks = tasks_data.compactMap { Tasks.Task(from: $0.data) } await MainActor.run { for new_task in new_tasks.reversed() { withAnimation { self.tasks.insert(new_task, at: 0) } } } } catch { print(error) } } } ... } The asynchronous function watch_for_new_tasks() uses RedisClient.shared.xread(streams: "tasks", ids: "$") which blocks until at least one tasks is added to the Redis Stream. I tried running the watch_for_new_tasks() on a Task.detached tasks, but that also blocks. To be honest, I have no idea why this blocks, and I could use your guy's help if you could. Thank you in Advance, Michael
Replies
1
Boosts
0
Views
1.7k
Activity
Oct ’23
Asynchronous Video Encoding using Tencent SDK
Hello, developers and expert, trying to implement encoding video simply using tencent sdk but I got error, and continuation doesn't return any progress or result. anyone help me please... what i am missing? SWIFT TASK CONTINUATION MISUSE: generateVideo(_:) leaked its continuation! 2023-03-30 12:07:15.694914+0900 Metacellar (dev)[87044:19556775] SWIFT TASK CONTINUATION MISUSE: generateVideo(_:) leaked its continuation! func encodeVideo() async throws { if let video { let encoded = try await TencentVideoEncoder.generateVideo(video) } } import TXLiteAVSDK_Professional class TencentVideoEncoder: NSObject { static func generateVideo(_ original: AVAsset) async throws -> AVAsset { let inputPath = try await Cache.saveToLocal( asset: original, cache: Cache.temporary, cacheKey: UUID().uuidString) let outputPath = MetaCellarCache.temporary.cachePath(forKey: UUID().uuidString)! let editor = TXVideoEditer() editor.setVideoBitrate(2300) editor.setVideoPath(inputPath) editor.setCutFromTime(0, toTime: 10) return try await withCheckedThrowingContinuation { continuation in let listener = Listener(onComplete: { result in switch result.retCode { case .GENERATE_RESULT_OK: continuation.resume(returning: AVAsset(url: URL(fileURLWithPath: outputPath))) default: continuation.resume(throwing: NSError(domain: "Encoding Failed", code: result.retCode.rawValue)) } }, onProgress: { progress in print(progress) }) editor.generateDelegate = listener editor.generateVideo(withTwoPass: .VIDEO_COMPRESSED_720P, videoOutputPath: outputPath) } } class Listener: NSObject, TXVideoGenerateListener { private var onComplete: (TXGenerateResult) -> () private var onProgress: (Float) -> () init(onComplete: @escaping (TXGenerateResult) -> (), onProgress: @escaping (Float) -> ()) { self.onComplete = onComplete self.onProgress = onProgress } func onGenerateComplete(_ result: TXGenerateResult) { onComplete(result) } func onGenerateProgress(_ progress: Float) { onProgress(progress) } } }
Replies
0
Boosts
0
Views
789
Activity
Mar ’23
AsyncStream(unfolding:) cancels early
So I have been trying to figure out how to solve an issue all day today. Basically, I have two async streams and I am using async-algorithms combineLatest to merge them, and then .map to select a value from one of the streams. Unfortunately, there is no "AnyAsyncSequence" so I can't type erase. As such, I have wrapped this sequence in an AsyncStream. I noticed that by doing this, the observer of this stream seems to cancel early. When in reality it should only be ending when my class deinitializes. Here is the code that does not work: var values: AsyncStream<MyValue> { let stream = combineLatest(self.myFirstStream, self.mySecondStream) .map(self.selectValueToReturn) return AsyncStream { for await value in stream { // For some reason, only a few values get returned here and then the stream gets cancelled. return value } return nil } } Strangely enough, if I switch to a continuation based AsyncStream. It works fine: var values: AsyncStream<FeatureConfig> { return AsyncStream { continuation in Task { [weak self] in guard let self else { return } let stream = combineLatest(self.myFirstStream, self.mySecondStream) .map(self.selectValueToReturn) for await value in stream { continuation.yield(value) } continuation.finish() } } }
Replies
1
Boosts
0
Views
938
Activity
Mar ’23
A non-destructive URL.lines?
I am working on a Server-Sent-Event parser which has newlines as part of the protocol: https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation If the line is empty (a blank line) Dispatch the event, as defined below. Unfortunately, when using: let (asyncBytes, _) = try await self.session.bytes(from: sseStreamURL) for try await line in asyncBytes.lines { //parse line } The iterator actually skips empty lines entirely, which seems destructive and therefore can't be the only way? Can it? With split there is a omittingEmptySubsequences: false, but I didn't find any trace of anything like that for .lines. I couldn't find the var definition in the Swift repo either, if anyone could point me at it. I did write something that works for now, but I'd love if anyone had any comments on how to improve it / point me at any exiting language features that I missed. extension AsyncSequence where Element == UInt8 {     var allLines:AsyncThrowingStream<String, Error> {         AsyncThrowingStream { continuation in             Task {                 var buffer:[UInt8] = []                 var iterator = self.makeAsyncIterator()                 while let byte = try await iterator.next() {                     // b/c 10 == \n                     if byte != 10 { buffer.append(byte) } else {                         if buffer.isEmpty { continuation.yield("") } else {                             if let line = String(data: Data(buffer), encoding: .utf8) { continuation.yield(line) } else { //Is there a different AsyncSequence error I could use?                                 throw SSEError("allLines: Couldn't make string from [UInt8] chunk")                             }                             buffer = []                         } }                 }             }         }     } } usage: for try await line in asyncBytes.allLines { //parse line } or of course also var iterator = asyncBytes.allLines.makeAsyncIterator() while let line = try await iterator.next() { //parse line }
Replies
2
Boosts
0
Views
952
Activity
Feb ’23
Invalid Bundle. The bundle at 'Foo.framework' contains disallowed file 'Frameworks'
In our project setup we have Main App and our code base is divided into Module using Framework, Recently in one of our framework we write some code using swift async-await API. After that We got this error while uploading to TestFlight Invalid Bundle. The bundle at 'Foo.framework' contains disallowed file 'Frameworks'. With error code STATE_ERROR.VALIDATION_ERROR.90206 for id 6822222-72fc-4820-0000-b1f8411d4f04 From stackoverflow we resolved issue by doing following things previously it was YES and working fine ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; Now our build Successfully uploaded to TestFlight and in apple review we got this comment from apple team and they rejected app
Replies
3
Boosts
0
Views
2.4k
Activity
Jan ’23
ContinuousClock not working for sleeping
// continuous version let continuousClock = ContinuousClock() let continuousElapsed = try await continuousClock.measure { try await Task.sleep(until: .now + .seconds(5), clock: .continuous) } print(continuousElapsed) // suspending version let suspendingClock = SuspendingClock() let suspendingElapsed = try await suspendingClock.measure { try await Task.sleep(until: .now + .seconds(5), clock: .suspending) } print(suspendingElapsed) result: 0.000126 seconds 5.324980708 seconds Swift version: Apple Swift version 5.7 (swiftlang-5.7.0.113.202 clang-1400.0.16.2)
Replies
1
Boosts
0
Views
1.1k
Activity
Jun ’22