Solution for: iOS 14/15+
Here's how you might interact with an Environment Object from a View Model, without having to inject it on instantiation:
- Define the Environment Object:
import Combine
final class MyAuthService: ObservableObject {
@Published private(set) var isSignedIn = false
func signIn() {
isSignedIn = true
}
}
- Create a View to own and pass around the Environment Object:
import SwiftUI
struct MyEntryPointView: View {
@StateObject var auth = MyAuthService()
var body: some View {
content
.environmentObject(auth)
}
@ViewBuilder private var content: some View {
if auth.isSignedIn {
Text("Yay, you're all signed in now!")
} else {
MyAuthView()
}
}
}
- Define the View Model with methods that take the Environment Object as an argument:
extension MyAuthView {
@MainActor final class ViewModel: ObservableObject {
func signIn(with auth: MyAuthService) {
auth.signIn()
}
}
}
- Create a View that owns the View Model, receives the Environment Object, and calls the appropriate method:
struct MyAuthView: View {
@EnvironmentObject var auth: MyAuthService
@StateObject var viewModel = ViewModel()
var body: some View {
Button {
viewModel.signIn(with: auth)
} label: {
Text("Sign In")
}
}
}
- Preview it for completeness:
struct MyEntryPointView_Previews: PreviewProvider {
static var previews: some View {
MyEntryPointView()
}
}