0

I am having trouble moving data between my views and my viewModel. I am working on an app for cars and need to be able to toggle the same views between the 4 corners of the car.

I did this with an enum:

enum TestCorners: String {
    case LF
    case RF
    case LR
    case RR
}

Based on the users' selection, my next screen must load some userDefaults based on which corner was selected. So I am passing the selectedCorner to the view like this: (this subview creates a button to navigate; in the app it belongs to a view with 4 buttons based on the TestCorner enum for the user to selected the desired TestCorner)

import SwiftUI

struct CornerSelectorSubView: View {
    
    @State var showThing = false
    @State var selectedCorner: TestCorners
    
    var body: some View {
        
        NavigationLink(destination: CornerSetupScreen(selectedCorner: selectedCorner), label: {
            Text(selectedCorner.rawValue)
                .font(.largeTitle)
                .padding()
                .padding(.vertical)
                .overlay(RoundedRectangle(cornerRadius: 15)
                    .stroke(Color.blue, lineWidth: 2))
        })
}

Once the user selects the corner they want, I need to load a "Setup Screen" with some user editable parameters that can be saved to UserDefaults. I pass the selectedCorner to the view correctly, but I'm not sure how to pass the selectedCorner to the viewModel so that it automatically loads the UserDefaults when the "Setup Screen" appears.

Here is what the "Setup Screen" view looks like: (To simplify, I only left two setup parameters)

struct CornerSetupScreen: View {
    var corner: TestCorners
    
    @StateObject var setupScreenVM  =
    CornerSetupDataViewModel()
       
    
    var body: some View {
        
        VStack(){
                TestParameterSubView(parameterName: "Max Center-Center", parameterUnit: "in", parameterValue: $setupScreenVM.maxC2C)
                }
                
                TestParameterSubView(parameterName: "Max Nut Height", parameterUnit: "in", parameterValue: $setupScreenVM.maxNutHeight)
            
            Button("Save"){
                setupScreenVM.saveValues(corner: corner)
                
                showAlert = true
            }.alert(isPresented: $showAlert) {
                Alert(
                    title: Text("Success"),
                    message: Text("Shock data saved!")
                )
            }
        }
      }
    }

and the viewModel is this:

class CornerSetupDataViewModel: ObservableObject {
    
    @Published var corner: TestCorners?
    
    @Published var maxC2C: String = "50"
    @Published var maxNutHeight: String = ""
    
    var userDefaults = UserDefaults.standard
    
    func populateValues(corner: TestCorners) {
        maxC2C = userDefaults.string(forKey: SetupKeyValues.maxC2C.rawValue + corner.rawValue)!
        maxNutHeight = userDefaults.string(forKey: SetupKeyValues.maxNutHeight.rawValue + corner.rawValue)!
        sliderThickness = userDefaults.string(forKey: SetupKeyValues.sliderThickness.rawValue + corner.rawValue)!
        shaftDiameter = userDefaults.string(forKey: SetupKeyValues.shaftDiameter.rawValue + corner.rawValue)!
        gasShock = userDefaults.bool(forKey: SetupKeyValues.gasShock.rawValue + corner.rawValue)
        canister = userDefaults.bool(forKey: SetupKeyValues.canister.rawValue + corner.rawValue)
    }
    
    func saveValues(corner: TestCorners) {
        userDefaults.set(maxC2C, forKey: SetupKeyValues.maxC2C.rawValue + corner.rawValue)
        userDefaults.set(maxNutHeight, forKey: SetupKeyValues.maxNutHeight.rawValue + corner.rawValue)
        userDefaults.set(sliderThickness, forKey: SetupKeyValues.sliderThickness.rawValue + corner.rawValue)
        userDefaults.set(gasShock, forKey: SetupKeyValues.gasShock.rawValue + corner.rawValue)
        userDefaults.set(canister, forKey: SetupKeyValues.canister.rawValue + corner.rawValue)
        
        print(maxC2C, corner.rawValue)

    }
}

What is the best way to move the selectedCorner from the previous view into the viewModel so that the viewModel can then present the right data in the view?

bb4
  • 15
  • 3
  • Please consider making a minimal example! Lots of your code, such as imports, view modifiers (padding, font, etc), and print statements aren't needed and make the question harder to answer. Try recreating the issue with only one or two `userDefault` values :) – Stoic Nov 28 '22 at 04:05

1 Answers1

0

You can do this in a number of ways actually. Here are a few:

  1. You can use .onAppear() inside CornerSetupScreen and then call your function that fetch userDefaults based on the corner you pass into your view.

  2. You could pass the viewModel in which you init with the corner that was tapped. I think that would be the cleaner way.


struct ContentView: View {
    var body: some View {
        NavigationView {
            NavigationLink("Action") {
                DetailsView(viewModel: ViewModel(name: "Jack"))
            }
        }
    }
}

struct DetailsView: View {

    @ObservedObject var viewModel: ViewModel

    var body: some View {
        if let name = viewModel.name {
            Text(name)
        }
    }
}

class ViewModel: ObservableObject {
    let name: String?

    init(name: String) {
        // Fetch data from userDefaults and set it
        self.name = name
    }
}
bjorn.lau
  • 774
  • 5
  • 16