how do I construct a swiftui class annotated mainactor in preview

Viewed 346

So - I have a class called settings, that I use all over the place - and particularly want other views to react when stuff is changed. I've annotated it with @MainActor - and when running the app, it all works fine.

however, in a preview I just try and create an instance:

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {        
        Group {
            ContentView( settings: Settings())
        }
    }
}

And suddenly, while the main program compiles and runs fine - the preview doesn't -

call to main actor-isolated initialiser 'init()' in a synchronous non isolated context.

Which I can get around with code like the following:

struct ContentView_Previews: PreviewProvider {
    
    var settings : Settings
    
    static func get_settings() -> Settings
    {
        var the_array : [Settings] =  []
        DispatchQueue.main.asyncAndWait( execute: DispatchWorkItem {
            the_array.append( Settings())
        })
        return the_array[0]
    }
    
    init()
    {
        self.settings = ContentView_Previews.get_settings()
    }
    
    static var previews: some View {        
        Group {
            ContentView( settings: Settings())
        }
    }
}

which is obviously ridiculous. I'm sure there's a better way - but don't know what it is - how can I do this in one or two lines, without having to make a helper function?

btw - THIS code crashes the preview provider every time:



struct ContentView_Previews: PreviewProvider {
        
    static func get_settings() -> Settings
    {
        var the_array : [Settings] =  []
        DispatchQueue.main.asyncAndWait( execute: DispatchWorkItem {
            the_array.append( Settings())
        })
        return the_array[0]
    }
  
    static var previews: some View {        
        Group {
            ContentView( settings: get_settings())
        }
    }
}
2 Answers

I was able to fix this issue by annotating the preview struct with @MainActor

@MainActor
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {        
        Group {
            ContentView( settings: Settings())
        }
    }
}

From my perspective instead of marking the entire class as MainActor you're better off isolating the actual functions that we know have asynchronous behavior.

class MainActorFuncTest {
    @MainActor
    func load() async -> String {
        return "Acting"
    }
}

That said, you should also be able to get around the problem by creating an init function and marking it nonisolated.

@MainActor
class MainActorTest {
    nonisolated init() {}
    func load() async -> String {
        return "Acting"
    }
}
Related