Skip to main content
added 209 characters in body
Source Link
emehex
  • 10.7k
  • 11
  • 61
  • 111

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:

  1. Define the Environment Object:
import Combine

final class MyAuthService: ObservableObject {
    @Published private(set) var isSignedIn = false
    
    func signIn() {
        isSignedIn = true
    }
}
  1. 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()
        }
    }
}
  1. 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()
        }
    }
}
  1. 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")
        }
    }
}
  1. Preview it for completeness:
struct MyEntryPointView_Previews: PreviewProvider {
    static var previews: some View {
        MyEntryPointView()
    }
}

Here's how you might interact with an Environment Object from a View Model, without having to inject it on instantiation:

  1. Define the Environment Object:
import Combine

final class MyAuthService: ObservableObject {
    @Published private(set) var isSignedIn = false
    
    func signIn() {
        isSignedIn = true
    }
}
  1. 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()
        }
    }
}
  1. 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()
        }
    }
}
  1. 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")
        }
    }
}

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:

  1. Define the Environment Object:
import Combine

final class MyAuthService: ObservableObject {
    @Published private(set) var isSignedIn = false
    
    func signIn() {
        isSignedIn = true
    }
}
  1. 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()
        }
    }
}
  1. 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()
        }
    }
}
  1. 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")
        }
    }
}
  1. Preview it for completeness:
struct MyEntryPointView_Previews: PreviewProvider {
    static var previews: some View {
        MyEntryPointView()
    }
}
Stack Overflow is like an encyclopedia, so we prefer to omit these types of phrases. It is assumed that everyone here is trying to be helpful.
Source Link
Dharman
  • 33.9k
  • 27
  • 103
  • 156

Here's how you might interact with an Environment Object from a View Model, without having to inject it on instantiation:

  1. Define the Environment Object:
import Combine

final class MyAuthService: ObservableObject {
    @Published private(set) var isSignedIn = false
    
    func signIn() {
        isSignedIn = true
    }
}
  1. 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()
        }
    }
}
  1. 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()
        }
    }
}
  1. 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")
        }
    }
}

Hope this helps!

Here's how you might interact with an Environment Object from a View Model, without having to inject it on instantiation:

  1. Define the Environment Object:
import Combine

final class MyAuthService: ObservableObject {
    @Published private(set) var isSignedIn = false
    
    func signIn() {
        isSignedIn = true
    }
}
  1. 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()
        }
    }
}
  1. 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()
        }
    }
}
  1. 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")
        }
    }
}

Hope this helps!

Here's how you might interact with an Environment Object from a View Model, without having to inject it on instantiation:

  1. Define the Environment Object:
import Combine

final class MyAuthService: ObservableObject {
    @Published private(set) var isSignedIn = false
    
    func signIn() {
        isSignedIn = true
    }
}
  1. 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()
        }
    }
}
  1. 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()
        }
    }
}
  1. 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")
        }
    }
}
Source Link
emehex
  • 10.7k
  • 11
  • 61
  • 111

Here's how you might interact with an Environment Object from a View Model, without having to inject it on instantiation:

  1. Define the Environment Object:
import Combine

final class MyAuthService: ObservableObject {
    @Published private(set) var isSignedIn = false
    
    func signIn() {
        isSignedIn = true
    }
}
  1. 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()
        }
    }
}
  1. 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()
        }
    }
}
  1. 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")
        }
    }
}

Hope this helps!