hmlongco / Resolver

Swift Ultralight Dependency Injection / Service Locator framework
MIT License
2.14k stars 187 forks source link

Using Resolver to Register a View Model with "@Main Actor" Annotation #150

Closed pkrusek closed 2 years ago

pkrusek commented 2 years ago

When I register a view model with @MainActor I get an error:

Call to main actor-isolated initializer 'init()' in a synchronous non isolated context
hmlongco commented 2 years ago

The only answer I have here is don't do that. See the last answer in:

https://stackoverflow.com/questions/71396296/how-do-i-fix-expression-requiring-global-actor-mainactor-cannot-appear-in-def

pavelk commented 2 years ago

Thanks :)

As a workaround I used for now the "classic"

DispatchQueue.main.async
hmlongco commented 2 years ago

If possible I think you can simply mark the async function with @MainActor as opposed to the entire class.

final class ContentViewModel: ObservableObject {

    @Published private(set) var loading: Bool = false
    @Published private(set) var message: String?
    @Published private(set) var error: String?

    let messageService = SystemStatusService()
    let usersService = UserService()

    @MainActor
    func loadStatusAsyncAwait() async {
        defer { loading = false }
        do {
            loading = true
            let result = try await messageService.fetchAsync()
            message = result.message
        } catch {
            self.error = error.localizedDescription
        }
    }
}

That also gets around the StateObject property warning with 5.5.


struct ContentView: View {
    @StateObject var model = ContentViewModel()
    var body: some View {
        ...
    }
}
pkrusek commented 2 years ago

Thank you very much for your hint / help. I solved it as you write - at the function level.

StefanNestorov commented 2 years ago

With XCode 13.3 if you add @MainActor on the registration method it seems to work. Do you think this can lead to some problems ? It was not working with the older XCode versions.

`extension Resolver: ResolverRegistering {

@MainActor
public static func registerAllServices() {
    register { NetworkService() as NetworkServiceProtocol }.scope(.application)
    register { AuthService() as AuthServiceProtocol }.scope(.application)
    register { NavigationService() as NavigationService }.scope(.application)
}

}

@MainActor class NavigationService: ObservableObject {

@Published var marketViewToCoinDetailsActiveLink = false
@Published var portfolioViewToEditPortfolioActiveLink = false

}

`

hmlongco commented 2 years ago

I think I would advise against that. There's no guarantee that the escaping factory closures will execute on the main thread and that breaks the model.