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

I am not sure, but I would assume that you cannot have two asynchronous tasks running on the same thread. Async tasks execute in the order they appear in the code. I would search how to open a new thread for the separate tasks, or find a method to periodically run watch_for_new_taks() so fetch_tasks() has a chance to operate.