I'm using the latest Observation framework with SwiftUI introduced at WWDC 2023. It seems that the new @Environment property wrapper does not allow to use protocols. Here is what my service implementations looks like:
protocol UserServiceRepresentation {
var user: User { get }
}
// Service implementation
@Observable
final class UserService: UserServiceRepresentable {
private(set) var user: User
func fetchUser() async { ... }
}
// Service mock
@Observable
final class UserServiceMock: UserServiceRepresentable {
let user: User = .mock
}
Previously, it was possible using @EnvironmentObject to declare a protocol so you can pass your mocked service as your environment object to the view.
struct UserView: View {
// This is possible using EnvironmentObject.
// But it require UserService to conforms to ObservableObject.
@EnvironmentObject private var userService: UserService
var body: some View { ... }
}
#Preview {
UserView()
.environmentObject(UserServiceMock()) // this allows your previews not to rely on production data.
}
Now, with @Environment, I can't do something like this:
struct UserView: View {
// This is not possible
// Throw compiler error: No exact matches in call to initializer
@Environment(UserService.self) private var userService
var body: some View { ... }
}
I would have been nice to inject my mocked user service like so:
#Preview {
UserView()
.environment(UserServiceMock())
}
Am I missing something or doing it the wrong way? It seems that the new @Environment property wrapper was not built for this kind of usage. I can't find anything about it on the web. Thanks in advance for your help.
@Environmentshould take anObservable-conforming type, and no, protocols cannot conform to other protocols, so it can't be used this way. What's wrong with how you previously did this anyway?@EnvironmentObjectway allowed me not to make singleton for shared objects in the app to better deal with dependencies injection. The new@Environmentnow forces me to create singletons. It works that way, but I feel like a regression.@EnvironmentObjectandObservableObject. They are not deprecated or anything. Do you mean you don't want to useObservableObjectanymore for some other reason, but still wants a convenient dependency injection?#if DEBUG ... #endifif you want to be sure it isn't used anywhere in the app code.