4

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())
        }
    }
}
Darren Oakey
  • 2,894
  • 3
  • 29
  • 55

3 Answers3

8

For those using the new Xcode 15 beta. Currently there is a known issue with using @MainActor and the new #Preview.

An example of the proposed workaround, until this is resolved, is as follows:

#Preview {
    MainActor.assumeIsolated {
        AppTabView(viewModel: MockAppTabViewModel())
    }
}
George Barlow
  • 155
  • 1
  • 5
7

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())
        }
    }
}
0

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"
    }
}
Michael Long
  • 1,046
  • 8
  • 15