Home > Net >  SwiftUI, How to publish data from view to a viewModel then to a second view?
SwiftUI, How to publish data from view to a viewModel then to a second view?

Time:10-08

I have one view (with a Form), a viewModel, and a second view that I hope to display inputs in the Form of the first view. I thought property wrapping birthdate with @Published in the viewModel would pull the Form input, but so far I can't get the second view to read the birthdate user selects in the Form of the first view.

Here is my code for my first view:

struct ProfileFormView: View {
@EnvironmentObject var appViewModel: AppViewModel
@State var birthdate = Date()

var body: some View {
    NavigationView {
        Form {
            Section(header: Text("Personal Information")) {
                DatePicker("Birthdate", selection: $birthdate, displayedComponents: .date)
            }
        }
    }

Here is my viewModel code:

class AppViewModel: ObservableObject {
@Published var birthdate = Date()

func calcAge(birthdate: String) -> Int {
    let dateFormater = DateFormatter()
    dateFormater.dateFormat = "MM/dd/yyyy"
    let birthdayDate = dateFormater.date(from: birthdate)
    let calendar: NSCalendar! = NSCalendar(calendarIdentifier: .gregorian)
    let now = Date()
    let calcAge = calendar.components(.year, from: birthdayDate!, to: now, options: [])
    let age = calcAge.year
    return age!

and here is my second view code:

struct UserDataView: View {
@EnvironmentObject var viewModel: AppViewModel
@StateObject var vm = AppViewModel()
var body: some View {
    VStack {
        Text("\(vm.birthdate)")

        Text("You are signed in")
        
        Button(action: {
            viewModel.signOut()
        }, label: {
            Text("Sign Out")
                .frame(width: 200, height: 50)
                .foregroundColor(Color.blue)
        })
    }
}

And it may not matter, but here is my contentView where I can tab between the two views:

struct ContentView: View {
@EnvironmentObject var viewModel: AppViewModel

var body: some View {
    NavigationView {
        ZStack {
            if viewModel.signedIn {
                ZStack {
                    Color.blue.ignoresSafeArea()
                        .navigationBarHidden(true)
                    TabView {
                        ProfileFormView()
                            .tabItem {
                                Image(systemName: "square.and.pencil")
                                Text("Profile")
                            }
                        UserDataView()
                            .tabItem {
                                Image(systemName: "house")
                                Text("Home")
                            }
                    }
                }
            }
            else
            {
                SignInView()
            }
        }
    }
    .onAppear {
        viewModel.signedIn = viewModel.isSignedIn
    }
}

One last note, I've got a second project that requires this functionality (view to viewmodel to view) so skipping the viewmodel and going direct from view to view will not help.

Thank you so much!!

CodePudding user response:

Using a class AppViewModel: ObservableObject like you do is the appropriate way to "pass" the data around your app views. However, there are a few glitches in your code.

In your first view (ProfileFormView), remove @State var birthdate = Date() and use DatePicker("Birthdate", selection: $appViewModel.birthdate, ....

Also remove @StateObject var vm = AppViewModel() in your second view (UserDataView), you already have a @EnvironmentObject var viewModel: AppViewModel, no need for 2 of them.

Put @StateObject var vm = AppViewModel() up in your hierarchy of views, and pass it down (as you do) using the @EnvironmentObject with .environmentObject(vm)

Read this info to understand how to manage your data: https://developer.apple.com/documentation/swiftui/managing-model-data-in-your-app

  • Related