Push View programmatically in callback, SwiftUI
I've found the answer. If you want to show another view on callback you should
Create state
@State var pushActive = false
When ViewModel notifies that login is successful set
pushActive
totrue
func handleSuccessfullLogin() { self.pushActive = true print("handleSuccessfullLogin")}
Create hidden
NavigationLink
and bind to that stateNavigationLink(destination: ProfileView(viewModel: ProfileViewModelImpl()), isActive: self.$pushActive) { EmptyView()}.hidden()
I'm adding some snippets here because I think it simplifies some things and makes reusing navigation links easier:
1. Add View Navigation Extensions
extension View { func navigatePush(whenTrue toggle: Binding<Bool>) -> some View { NavigationLink( destination: self, isActive: toggle ) { EmptyView() } } func navigatePush<H: Hashable>(when binding: Binding<H>, matches: H) -> some View { NavigationLink( destination: self, tag: matches, selection: Binding<H?>(binding) ) { EmptyView() } } func navigatePush<H: Hashable>(when binding: Binding<H?>, matches: H) -> some View { NavigationLink( destination: self, tag: matches, selection: binding ) { EmptyView() } }}
Now, you can call on any view (make sure they (or a parent) are in a navigation view)
2. Use at leisure
struct Example: View { @State var toggle = false @State var tag = 0 var body: some View { NavigationView { VStack(alignment: .center, spacing: 24) { Text("toggle pushed me") .navigatePush(whenTrue: $toggle) Text("tag pushed me (2)") .navigatePush(when: $tag, matches: 2) Text("tag pushed me (4)") .navigatePush(when: $tag, matches: 4) Button("toggle") { self.toggle = true } Button("set tag 2") { self.tag = 2 } Button("set tag 4") { self.tag = 4 } } } }}
as @Bhodan mentioned you can do it by changing state
Using EnvironmentObject with SwiftUI
- Add UserData ObservableObject :
class UserData: ObservableObject, Identifiable { let id = UUID() @Published var firebase_uid: String = "" @Published var name: String = "" @Published var email: String = "" @Published var loggedIn: Bool = false}
the loggedIn
property will be used to monitor when a change in user logs in or out
- Now add it as an
@EnvironmentObject
in yourSceneDelegate.swift
file in Xcodethis just makes it so its accessible everywhere in your app
class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). // Create the SwiftUI view that provides the window contents. let userData = UserData() let contentView = ContentView().environmentObject(userData) // Use a UIHostingController as window root view controller. if let windowScene = scene as? UIWindowScene { let window = UIWindow(windowScene: windowScene) window.rootViewController = UIHostingController(rootView: contentView) self.window = window window.makeKeyAndVisible() } }
Once you make any change to the loggedIn
property any UI that is Binded to it will respond to the true/false value change
the as @Bhodan mentioned just add this to your view and it will respond to that change
struct LoginView: View {@EnvironmentObject var userData: UserDatavar body: some View {NavigationView {VStack {NavigationLink(destination: ProfileView(), isActive: self.$userData.loggedin) { EmptyView() }.hidden() } } }}