Prior to iOS 17, I was used to creating SwiftUI views with a generic approach where a view is constrainted to one view model, and that ViewModel can be a protocol. This was very nice for testing and wrapping all the UI-related things to the ViewModels interface.
@MainActor
protocol CityQueryViewModelInterface: ObservableObject {
var text: String { get set }
func fetchWeather() async throws -> WeatherItem
}
struct CityQueryView<ViewModel: CityQueryViewModelInterface>: View {
@ObservedObject var viewModel: ViewModel
}
However, when trying to achieve this in iOS 17 using the Observable
macro with @Bindable
and protocol, I am getting an error
protocol CityQueryViewModelInterface: Observable {
var text: String { get set }
func fetchWeather() async throws -> WeatherItem
}
@Observable
final class CityQueryViewModel: CityQueryViewModelInterface {
struct CityQueryView<ViewModel: CityQueryViewModelInterface>: View {
@Bindable var viewModel: ViewModel
}
'init(wrappedValue:)' is unavailable: The wrapped value must be an object that conforms to Observable
Next to the Bindable annotation
The error says
The wrapped value must be an object that conforms to Observable.
Notice the highlight on "object".
Bindable
requires Observable
and AnyObject
so just add AnyObject
to your protocol
.
protocol CityQueryViewModelInterface: Observable, AnyObject {
var text: String { get set }
func fetchWeather() async throws -> WeatherItem
}