'How to create Generic if @EnvironmentObject?
I've recently come across the need to write a Mock of a Class
, as it causes the SwiftUI preview
from working. Unfortunately, I get the error:
Property type 'T' does not match that of the 'wrappedValue' property of its wrapper type 'EnvironmentObject'
In the View struct:
struct ContentView<T>: View {
@EnvironmentObject var mockFoobar: T
...
}
And also the error:
Type of expression is ambiguous without more context
For the Preview struct:
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
let mockFoobar: MockFoobar = MockFoobar()
return ContentView<MockFoobar>()
.environmentObject(mockFoobar)
}
}
The MockFoobar class is:
class MockFoobar: ObservableObject {
...
}
As the user @Asperi kindly provided, tested the following as suggested:
class Foobar: ObservableObject {
@Published var param: Bool = false
func start() {
self.param = true
}
}
struct MyFoobarView<T: ObservableObject>: View {
@EnvironmentObject var foobar: T
var body: some View {
VStack {
Text("Hello Foobar")
}
.onAppear {
self.foobar.start()
}
}
}
struct MyFoobarView_Previews: PreviewProvider {
static var previews: some View {
let foobar: Foobar = Foobar()
return MyFoobarView()
.environmentObject(foobar)
}
}
But I get the following errors (the first in the .onAppear
and the second in the PreviewProvider):
Cannot call value of non-function type 'Binding<Subject>'
Generic parameter 'T' could not be inferred
Solution 1:[1]
The EnvironmentObject must be ObservableObject, so here is fix
struct ContentView<T: ObservableObject>: View {
@EnvironmentObject var mockFoobar: T
// .. other code here
Update: added demo with introduced model protocol
protocol Foobaring {
var param: Bool { get set }
func start()
}
class Foobar: ObservableObject, Foobaring {
@Published var param: Bool = false
func start() {
self.param = true
}
}
struct MyFoobarView<T: ObservableObject & Foobaring>: View {
@EnvironmentObject var foobar: T
var body: some View {
VStack {
Text("Hello Foobar")
}
.onAppear {
self.foobar.start()
}
}
}
struct MyFoobarView_Previews: PreviewProvider {
static var previews: some View {
let foobar: Foobar = Foobar()
return MyFoobarView<Foobar>()
.environmentObject(foobar)
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 |