.onScrollVisibilityChange { isVisible in
if isVisible {
Task {
saveIdsWatchedTime(postTime: meme.time)
}
}
}
The onScrollVisibilityChange
is attached to a LazyVStack item and every time saveIdsWatchedTime is called, the UI get's blocked and the scrolling is lagging.
Why is that when I'm using Task, which is basically async?
The function saveIdsWatchedTime
isn't even a big deal and is finished in couple milliseconds.
This is the function:
func saveIdsWatchedTime(postTime: Int) {
if(watchedTimeChecked){
return
}
watchedTimeChecked = true
if((gal == "latest" || gal == "top") && !endOfMemesReached && postTime < lastTimeChecked){
var endString = ""
var endArray = [String]()
let newestMemeViewed = Int(memesWatchedTimeRanges[0]ponents(separatedBy: ",")[0])
var oldestMemeViewed = Int(memesWatchedTimeRanges[0]ponents(separatedBy: ",")[1])
if(oldestMemeViewed! > postTime){
memesWatchedTimeRanges[0] = String(newestMemeViewed!) + "," + String(postTime)
oldestMemeViewed = postTime
}
var firstPassed = false
for bla in memesWatchedTimeRanges{
let newestTimePastScroll = Int(blaponents(separatedBy: ",")[0])
let oldestTimePastScroll = Int(blaponents(separatedBy: ",")[1])
if(!firstPassed){
firstPassed = true
endArray.append(bla)
endString += String(newestTimePastScroll!) + "," + String(oldestTimePastScroll!) + "|"
}else{
if(newestMemeViewed! > newestTimePastScroll! && oldestMemeViewed! < oldestTimePastScroll!){
}else{
endArray.append(bla)
endString += String(newestTimePastScroll!) + "," + String(oldestTimePastScroll!) + "|"
}
}
}
if(endString.onlyLastCharacters(1) == "|"){
endString = String(endString.dropLast(1))
}
memesWatchedTimeRanges = endArray
if(gal == "latest"){
memesWatchedTimeRangesString = endString // AppStorage, between 25 and 100 characters long
}else{
memesWatchedTimeRangesTopString = endString // AppStorage, between 25 and 100 characters long
}
}
}
.onScrollVisibilityChange { isVisible in
if isVisible {
Task {
saveIdsWatchedTime(postTime: meme.time)
}
}
}
The onScrollVisibilityChange
is attached to a LazyVStack item and every time saveIdsWatchedTime is called, the UI get's blocked and the scrolling is lagging.
Why is that when I'm using Task, which is basically async?
The function saveIdsWatchedTime
isn't even a big deal and is finished in couple milliseconds.
This is the function:
func saveIdsWatchedTime(postTime: Int) {
if(watchedTimeChecked){
return
}
watchedTimeChecked = true
if((gal == "latest" || gal == "top") && !endOfMemesReached && postTime < lastTimeChecked){
var endString = ""
var endArray = [String]()
let newestMemeViewed = Int(memesWatchedTimeRanges[0].components(separatedBy: ",")[0])
var oldestMemeViewed = Int(memesWatchedTimeRanges[0].components(separatedBy: ",")[1])
if(oldestMemeViewed! > postTime){
memesWatchedTimeRanges[0] = String(newestMemeViewed!) + "," + String(postTime)
oldestMemeViewed = postTime
}
var firstPassed = false
for bla in memesWatchedTimeRanges{
let newestTimePastScroll = Int(bla.components(separatedBy: ",")[0])
let oldestTimePastScroll = Int(bla.components(separatedBy: ",")[1])
if(!firstPassed){
firstPassed = true
endArray.append(bla)
endString += String(newestTimePastScroll!) + "," + String(oldestTimePastScroll!) + "|"
}else{
if(newestMemeViewed! > newestTimePastScroll! && oldestMemeViewed! < oldestTimePastScroll!){
}else{
endArray.append(bla)
endString += String(newestTimePastScroll!) + "," + String(oldestTimePastScroll!) + "|"
}
}
}
if(endString.onlyLastCharacters(1) == "|"){
endString = String(endString.dropLast(1))
}
memesWatchedTimeRanges = endArray
if(gal == "latest"){
memesWatchedTimeRangesString = endString // AppStorage, between 25 and 100 characters long
}else{
memesWatchedTimeRangesTopString = endString // AppStorage, between 25 and 100 characters long
}
}
}
Share
Improve this question
edited Feb 8 at 12:23
TheGreatCornholio
asked Feb 7 at 23:59
TheGreatCornholioTheGreatCornholio
1,4951 gold badge22 silver badges48 bronze badges
14
|
Show 9 more comments
3 Answers
Reset to default 0Ensure that your func saveIdsWatchedTime(postTime: Int) is thread-safe. Marking a function as async actually runs it on a subthread.
.onScrollVisibilityChange { isVisible in
if isVisible {
Task {
await saveIdsWatchedTime(postTime: meme.time)
}
}
}
func saveIdsWatchedTime(postTime: Int) async {
...
}
I assume that saveIdsWatchedTime
is a method of View
so by default it will be bounds to MainActor
because View
is bounded to it.
So using Task
or even Task.detach
does not work because saveIdsWatchedTime
still run in main thread.
To not run saveIdsWatchedTime
on main thread which cause block UI, you could consider create a global actor
.
struct ContentView: View {
...
@Global func saveIdsWatchedTime() { // <- bound to global actor
// your code goes here
}
}
@globalActor actor Global {
static var shared: Global = .init()
}
You can also implement custom executor to specify which thread you want to run your code too.
View functions by default now run on @MainActor
. That means that by default your function looks like this @MainActor
func saveIdsWatchedTime()
. It doesn't matter if you call it from a Task since the function is has a @MainActor
signature it will block the UI. You can move the Task inside the function instead of calling it inside onScrollVisibilityChange. Any assignment you make to a @State
variable will still be made in the main actor therefore blocking the UI. Ideally you want to leave any assignments until the end of the function. Progress flags are usually an exception. The other solution is to declare saveIdsWatchedTime inside an @Observable
object and mark the function as nonisolated and async. Note that in this case you'd have to move any @State
variables to the @Observable
object and make sure any UI exposed variables are called on the main actor since the function is now nonisolated.
async
, but you call itawait
, which says "block here until the async function finishes," thereby making it sync. – Tim Roberts Commented Feb 8 at 0:19Task
replaces the old function ofasync
. You don't use both. stackoverflow.com/questions/70979038/… – Tim Roberts Commented Feb 8 at 0:25saveIdsWatchedTime
do? Is it CPU-bound? – Sweeper Commented Feb 8 at 8:07onScrollVisibilityChange
and there it doesn’t block the UI, out of curiosity, tryTask.detached(priority:.utility) {…}
– Luca Iaco Commented Feb 8 at 10:26