hmlongco / Factory

A new approach to Container-Based Dependency Injection for Swift and SwiftUI.
MIT License
1.83k stars 115 forks source link

MainActor issue with ObservableObject's #135

Closed cabyambo closed 1 year ago

cabyambo commented 1 year ago

I am getting an error when trying to register an ObservableObject marked with @MainActor

Call to main actor-isolated initializer 'init()' in a synchronous nonisolated context

Here's how I am registering the object:

extension Container {
    var homeViewModel: Factory<HomeViewModel> {
        self { HomeViewModel() }
    }
}

And here is the object itself:

import Foundation
import Factory
import Combine

@MainActor // Removing this fixes the error, but I need this
class HomeViewModel: ObservableObject {
    @Injected(\.cardImageUrlRepository) private var cardImageUrlRepository

    @Published private(set) var cardImageUrls: [String] = []

    init() {
        fetchCreditCardImageUrls()
    }

    private func fetchCreditCardImageUrls() {
        Task {
            // Fetch data using repository
            // Set cardImageUrls to fetched data
        }
    }
}

Is it possible to keep the @MainActor tag?

hmlongco commented 1 year ago

If you mark the entire class that way then you need to mark the initializer as nonisolated in order to get around that particular problem. Marking the entire class with MainActor marks every function and method within the class as MainActor, including the initializer.

I tend to prefer marking async functions as MainActor as needed, and not the entire class. I wrote a lot about that on Medium: https://betterprogramming.pub/async-await-and-mainactor-strategies-cc35b6c58b52