https://kotlinlang.org logo
Title
s

Stefan Oltmann

02/03/2022, 2:04 PM
Forgive me this iOS only question, but I really don't find the correct solution. 🙈 How can I make that my SwiftUI task runs on the background?
task
is always on the main thread.
Task.detached
starts a new task on another thread. The problem is
Task.detached()
runs to completion even is task is cancelled because the cancellation is not propagated. Can some expert please enlight me how to correctly do that?
.task {

      Task.detached {

        guard image == nil else { return }

        image = await imageLoader.loadThumbnailImage(photoUri: photo.uri)
      }
    }
1
h

hfhbd

02/03/2022, 2:07 PM
By reading the docs only: What about
.task(priority: .background) {
    Task {
    }
}
s

Stefan Oltmann

02/03/2022, 2:09 PM
That still runs on the main actor unfortunately.
It would be so nice if these Tasks would do it like coroutines and automaticly propagate cancellation
h

hfhbd

02/03/2022, 2:16 PM
I thought, they do. But
Task.detached
is
GlobalScope
, which doesn't inherit cancelation too:
suspend fun a() {
     while (true) {
        delay(1.seconds)
        println("Still running")
    }
}
    
@OptIn(DelicateCoroutinesApi::class)
@Test
fun b(): Unit = runBlocking {
    withTimeout(5.seconds) {
        GlobalScope.launch {
            a()
        }
    }
    delay(10.seconds)
}
s

Stefan Oltmann

02/03/2022, 2:24 PM
Yeah, I'm so confused. Apple changes API all the time and a lot of solutions for that are outdated. 🤷‍♂️
If I could just set the opposite of the MainActor annotation to my imageLoader call. That would be great.
Is there any
withContext()
equivalent in Swift?
h

hfhbd

02/03/2022, 2:37 PM
not similar, but this works: `actor`:
import SwiftUI

struct ContentView: View {
    let a = A()
    
    var body: some View {
        Text("Hello, world!")
            .padding()
            .task(priority: .background) {
                print("Start Thread")
                await a.a()
            }
    }
}


actor A {
    func a() async {
        Task {
            print("Start Sleeping")
            try await Task.sleep(nanoseconds: 50_000_000_000)
        }
    }
}
s

Stefan Oltmann

02/03/2022, 2:47 PM
Thank you. 🙏 I guess I need to look up again the whole "actor" mechanics. 😅 Changing my ImageLoaderImpl from "class" to "actor" according to your sample does the trick.
h

hfhbd

02/03/2022, 2:50 PM
Keep in mind, `actor`s handles
async
calls sequentiell!
s

Stefan Oltmann

02/03/2022, 2:50 PM
So no concurrent image loadings at the same time?
I improved performance now because if you scroll very fast trough my photo gallery tasks for images that are no more visible do not needed to be loaded. That's why task cancellation is very important here.
Yes, ok. Bringing everything to the same actor is indeed another problem. 😅
Wow, Apple really made this hard
Ok, I hacked this... If you call a kotlin suspend function (you must be on the main thread for that because the others are not supported) when this comes back from "await" you are on an random background thread... then you can do stuff in parallel... until you call a method marked as MainActor to bring you back.
🙈
Let's talk no further about this and let me thank you again, Phillip ❤️
h

hfhbd

02/03/2022, 3:47 PM
Nice 😄