@mattiem Hey! Do you have any guidance, documentation, resources you find useful on when to (and when not to) do [weak self]
inside a Task
?
I know that it does retain a strong reference to self, but then I see recommendations that just retaining a reference to the Task
and calling task.cancel()
gets around that. I see some suggesting adding it in your model/view model/whatever's deinit, but that deinit would never be called if a Task has a strong reference, …right? 🤯
=> More informations about this toot | More toots from rhysmorgan@mastodon.social
@rhysmorgan @mattiem Also if self is a struct, like most things in SwiftUI, there is no retaining, and thus no retain cycles.
=> More informations about this toot | More toots from gernot@mas.to
@gernot @mattiem Yep, that stuff makes a lot of sense to me. I think the compiler stops you even weakifying self in that use case anyway, right?
I'm trying to figure out if there's a "better" solution to something like this, using Task cancellation, or just to stop trying to be so "smart" 😄
(quotes, because I realise that this code probably works just fine and just let it be, and messing with it might not actually be so smart)
=> More informations about this toot | More toots from rhysmorgan@mastodon.social
@gernot @mattiem I've come up with this as an alternative, but also aware that it now needs cancelling from the outside to avoid any chance of a "retain cycle”, or pushing the Task
creation out to the View Controller layer, which makes the “always use currently active Task" thing not really testable 😅
=> More informations about this toot | More toots from rhysmorgan@mastodon.social
@rhysmorgan @mattiem Anybody correct me if I’m wrong, but the former version had the same functionality. You have to call it within a Task anyways, and if that is canceled, the Childs are canceled. A Task{} that is not in a parent taks has to be canceled manually, if it is in a parent task, it will be canceled like any other child. Task.detached{} has no parent. (1/2)
=> More informations about this toot | More toots from gernot@mas.to
And a .task modifier is canceled when the view disappears, so if the request is only relevant to the view, use that. (2/2)
=> More informations about this toot | More toots from gernot@mas.to
@gernot @rhysmorgan yeah I don't see yet why you'd do this. Are you looking to hold onto the Task as a way to cache the result? That's quite a common pattern.
=> More informations about this toot | More toots from mattiem@mastodon.social
@mattiem @gernot No, not looking to cache the result in the Task itself.
Basically, encountered a bug in a feature where a user could pull to refresh while the initial screen load was happening, and that would end up performing the same paginated API request twice. Because the request is paginated, we append the result to the array that drives the UITableView. If both initial requests happen, the user sees duplicate initial data.
So we needed a solution to prevent a second API call being made…
=> More informations about this toot | More toots from rhysmorgan@mastodon.social
@mattiem @gernot …and that's what led to the isLoading
Bool being added, checked, and guarded against – to make sure that only one request is happening at a time, but it's always the oldest request. Like the opposite of a debounce.
I was wondering if retaining the call in an Task?
was a decent solution, to encapsulate the concept that "A Task exists, don't try again". I think I missed a self.task = nil
at the end of the Task body 😅
=> More informations about this toot | More toots from rhysmorgan@mastodon.social
@mattiem @gernot Still – keen to avoid overcomplicating things, and the Bool solution is probably just fine, and avoids any questions about whether or not you need to weakify self inside the Task
and whose responsibility it is to call cancel() on the Task in the view model.
=> More informations about this toot | More toots from rhysmorgan@mastodon.social
@rhysmorgan @gernot Ahh right. Ok in this case, I think Task may be the better option because it will allow you to keep the latest request. That's another important difference we missed.
But you might want to investigate if you can shift this responsibility into a .task
modifier instead. Those handle cancellation for you, including when view are dismissed.
=> More informations about this toot | More toots from mattiem@mastodon.social
@mattiem @gernot Alas, it's in a UIViewController, so ultimately still need to perform cancellation somewhere.
I may have misspoke – I always want the oldest of the API requests to continue running. So if the user hammers the refresh, I only want it to load the first paginated call the one time, as that's the longest running one. I need to prevent the second, third, fourth time it's called (at least, until that first request has succeeded).
=> More informations about this toot | More toots from rhysmorgan@mastodon.social
@rhysmorgan @gernot Ahh ok. In that case, the flag + plain old async stuff is definitely the way to go!
=> More informations about this toot | More toots from mattiem@mastodon.social
@mattiem @rhysmorgan @gernot would this be a good place to use Semaphore? Async loading could happen in a function that uses a semaphore to prevent multiple executions. https://github.com/groue/Semaphore
=> More informations about this toot | More toots from jaanus@iosdev.space
@jaanus @rhysmorgan @gernot Actually no, because this is on the MainActor.
But in other situations that could potentially be an option! I like to try to use locks as a last resort though.
=> More informations about this toot | More toots from mattiem@mastodon.social
@gernot @rhysmorgan But also, cancallation is not propagated in either case. Unstructured Tasks always require manual cancellation. So that is a difference between these two versions.
=> More informations about this toot | More toots from mattiem@mastodon.social
@mattiem @rhysmorgan Ah, good point! The Task{} gets their actor, priority etc from the parent task, but not the cancellation… I knew I was wrong about something ;-)
=> More informations about this toot | More toots from gernot@mas.to This content has been proxied by September (3851b).Proxy Information
text/gemini