Migrating @MainActor ViewModel to @Observable causing error

I get this error while migrating from ObservableObject to @Observable.

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

My original code:

struct SomeView: View {
	@StateObject private var viewModel = ViewModel()
}

After migration:

@MainActor @Observable class BaseViewModel {
}
@MainActor class ViewModel: BaseViewModel {
}
struct SomeView: View {
	@State private var viewModel = ViewModel()
}

As discussed here. It seems like @StateObject is adding @MainActor compliance to my View under the hood because it's wrappedValue and projectedValue properties are marked as @MainActor, while on @State they are not.

@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
@frozen @propertyWrapper public struct StateObject<ObjectType> : DynamicProperty where ObjectType : ObservableObject {
    ...
    @MainActor public var wrappedValue: ObjectType { get }
    ....
    @MainActor public var projectedValue: ObservedObject<ObjectType>.Wrapper { get }
}

One solution for this is to mark my View explicitly as @MainActor struct ViewModel: View but it have it side effects, for example code like:

Button(action: resendButtonAction) {
    Text(resendButtonAttributedTitle())
}

Will result a warning

Converting function value of type '@MainActor () -> ()' to '() -> Void' loses global actor 'MainActor'

While could be easily solved by using instead

Button(action: { resendButtonAction() } ) {
    Text(resendButtonAttributedTitle())
}

I still feel like marking the whole View explicitly as @MainActor is not a good practice.
Adding fake @StateObject property to my view also do the trick, but it's a hack (the same for @FetchRequest).
Can anyone think of a more robust solution for this?

Post not yet marked as solved Up vote post of itayAm Down vote post of itayAm
809 views