Search code examples
iosswiftuiswiftui-environmentproperty-wrapper

Work with Property wrappers in nested views in SwiftUI


I'm fairly new to SwiftUI and I ran into this issue trying to figure out how to work with Environment object in many views. I'm trying to move to the next view after successful login which is happening, I'm then updating @Published Isloggedin to true, which all is fine up until now. I'm also wondering if my approach is good or if should I change to a completely different approach.

Here is my LoginView that contains nested structs in them and this is exactly my issue, I don't know how to pass the same Env object to child views.

[Edited] : I made some changes and the navigation works now but it lags a bit, it takes few seconds because it navigates to the next page so I'm wondering now if I'm doing anything wrong that's causing the navigation to lag.

struct LoginView: View {


var body: some View {
    
    
    VStack(alignment: .center){
        
        //
        
        cardView(image: "LoginImg", title: "Title here ", subtitle: "subtitle here")

        Spacer()
        
    }.background(Color("PrimaryColor"))
}
}

Here is the code with my CardView :

struct cardView: View
{
@State private var isShowingWebView: Bool = false

var body: some View {
    VStack(spacing: 30){
        

        ZStack{
   
            //
   
        VStack(spacing: 12){
            
            ContinueWithGoogleBtn(image: "GoogleLogo", text: "Continue with Google")
            
            
            ContinueWithDropboxBtn(image: "DropboxLogo", text: "Continue with Dropbox")
            ContinueWithMicrosoftBtn(image: "MicrosoftLogo", text: "Continue with Microsoft")
            

        }

       
          //
}

}

Then my GoogleButton

struct ContinueWithGoogleBtn: View{


var image: String
var text: String
@EnvironmentObject var vm: userAuthModelGoogle()

var body: some View{

    Button {
        
        //userAuth.SigninwithGoogle()
        vm.SigninwithGoogle()
        
        
    } label: {
     //
    }

}


}

ContentView :

struct ContentView: View {


@StateObject var userAuth = userAuthModelGoogle()

var body: some View {
    NavigationView{
        
        if userAuth.isLoggedin {
            
           HomeScreen(isshowing: false)
        }
        else {

            LoginView()
          
        }
  
        
    }.environmentObject(userAuth)
        .navigationBarTitle("")
        .navigationBarHidden(true)

}

and on my main file I'm just the contentstruct

@main
 struct testapp: App {
var body: some Scene {
    
    WindowGroup {
    ContentView()
        
    }
}

}

Thank you in advance


Solution

  • You don't need to/should not use nested structs. Otherwise your approach is good. A simplified version runs fine with me.

    The only mistake seems to be in ContinueWithGoogleBtn:

    @EnvironmentObject var vm: userAuthModelGoogle() should be:
    @EnvironmentObject var vm: userAuthModelGoogle
    Skip the () – you don't want to instantiate an object, but get the one from the environment that matches the type.

    For reference here is my full code that works well:

    struct LoginView: View {
    
        var body: some View {
            VStack(alignment: .center) {
                //
                cardView()
                Spacer()
            }
        }
    }
    
    
    struct cardView: View {
        @State private var isShowingWebView: Bool = false
        
        var body: some View {
            VStack(spacing: 30){
                ZStack{
                    //
                    VStack(spacing: 12){
                        ContinueWithGoogleBtn(image: "GoogleLogo", text: "Continue with Google")
    //                    ContinueWithDropboxBtn(image: "DropboxLogo", text: "Continue with Dropbox")
    //                    ContinueWithMicrosoftBtn(image: "MicrosoftLogo", text: "Continue with Microsoft")
                    }
                    //
                }
            }
        }
    }
    
    struct ContinueWithGoogleBtn: View{
        
        var image: String
        var text: String
        
        @EnvironmentObject var vm: userAuthModelGoogle
        
        var body: some View{
            Button {
                //userAuth.SigninwithGoogle()
                vm.SigninwithGoogle()
            } label: {
                //
                Text(text)
            }
        }
    }
    
    
    // dummy
    class userAuthModelGoogle: ObservableObject {
        
        @Published var isLoggedin = false
        
        func SigninwithGoogle() {
            isLoggedin = true
        }
    }
    
    
    struct ContentView: View {
        
        @StateObject var userAuth = userAuthModelGoogle()
        
        var body: some View {
            NavigationView {
                if userAuth.isLoggedin {
                    Text("HomeScreen")
                }
                else {
                    LoginView()
                }
            }
            .environmentObject(userAuth)
            .navigationBarTitle("")
            .navigationBarHidden(true)
        }
    }