skip to Main Content

I have the following demo code:

import SwiftUI
import Observation



class Car1: ObservableObject {
    @Published var rentDate:Date = .now
}

class Car2: ObservableObject {
    @Published var rentDate:Date = .now
}


@Observable class CarViewModel {
    var rentDate:Date = .now
}


struct CarView: View {
    @State var model = CarViewModel()
    var body: some View {
        Text("DATE: (model.rentDate)")
    }
}



struct ContentView: View {
    
    @StateObject var car1 = Car1()
    @StateObject var car2 = Car2()
    @State var model = CarViewModel()
    
    var body: some View {
        
        @Bindable var model = model
        
        NavigationStack {
            
            VStack {
                
                Spacer()
                
                HStack {
                    Spacer()
                    Button {
                        /// I want model.rentDate to update as car1.rentDate updates...
                        model.rentDate = car1.rentDate
                        
                        //tried this:
                        /// Cannot assign value of type 'Binding<Date>' to type 'Date' but gives `model.rentDate = $car1.rentDate`
                    } label: {
                        Text("Bind to Car1")
                    }
                    Spacer()
                    Button {
                        /// I want model.rentDate to update as car2.rentDate updates...
                        model.rentDate = car2.rentDate
                    } label: {
                        Text("Bind to Car2")
                    }
                    Spacer()
                }
                
                Spacer()
                
                HStack {
                    Spacer()
                    Button {
                        /// when I change this I want `Text("DATE: (model.rentDate)")` below to update...
                        car1.rentDate = .now
                    } label: {
                        Text("Change Car1")
                    }
                    Spacer()
                    Button {
                        /// when I change this I want `Text("DATE: (model.rentDate)")` below to update...
                        car2.rentDate = .now
                    } label: {
                        Text("Change Car2")
                    }
                    Spacer()
                }
                
                Spacer()
                
                Text("DATE: (model.rentDate)")
                
                Spacer()
                
            }
            
        }

    }
    
}

#Preview {
    ContentView()
}

I want CarViewModel rentDate to bind to either Car1 or Car2 rentDate so that when the model layer in other parts of the app changes car2.rentDate the change also shows up in ContentView Text("DATE: (model.rentDate)") and this allows me to have a view model that can work with different dates found in other models…

Basically I am trying to have a way to bind a model to follow another model. In my case the one that drives the view uses @Observable and the other models use ObservableObject

This is just some demo code I would like to get working to prove the functionality. So I would click on "Bind to Car1" and then tapping on "Change Car1" would trigger the date at the bottom to update based on car1 model.rentDate. Same for car2…

How can I get CarViewModel to follow either car1 or car2 model?

2

Answers


  1. Since this is SwiftUI you need to make View structs for your car and not view model objects.

    Login or Signup to reply.
  2. You could try this approach using the NotificationCenter, ...to make CarViewModel follow one of the car1 or car2 models at the time.

    The example code shows how to automatically "send" (in didSet) a notification to the CarViewModel whenever Car1 or Car2 rentDate changes, thus updating the CarViewModel rentDate automatically.

    EDIT-1

    Here is a more complete example of choosing which car you want to bind to

    import Foundation
    import SwiftUI
    import Observation
    
    class Car1: ObservableObject {
        @Published var rentDate: Date = .now {
            didSet {
                NotificationCenter.default.post(
                    name: CarViewModel.rentNotification,
                    object: (rentDate, "car1")
                )
            }
        }
    }
    
    class Car2: ObservableObject {
        @Published var rentDate: Date = .now {
            didSet {
                NotificationCenter.default.post(
                    name: CarViewModel.rentNotification,
                    object: (rentDate, "car2")
                )
            }
        }
    }
    
    @Observable class CarViewModel {
        var rentDate: Date = .now
        var carType: String = ""
        
        public static let rentNotification = Notification.Name(rawValue: "rent")
        
        init() {
            NotificationCenter.default.addObserver(self, selector: #selector(rentDateUpdate(_:)), name: CarViewModel.rentNotification, object: nil)
        }
        
        // this function is called whenever a rentNotification is received
        @objc func rentDateUpdate(_ notification: Notification) {
            if let (date, car) = notification.object as? (Date, String) {
                if carType == car {
                    rentDate = date
                }
            }
        }
    
        func bindTo(_ car: String) {
            carType = car
        }
    }
    
    struct ContentView: View {
        @StateObject var car1 = Car1()
        @StateObject var car2 = Car2()
        @State var model = CarViewModel()
        
        var body: some View {
            NavigationStack {
                VStack {
                    Spacer()
                    HStack {
                        Spacer()
                        Button("Bind to Car1") {
                            model.bindTo("car1")
                        }
                        Spacer()
                        Button("Bind to Car2") {
                            model.bindTo("car2")
                        }
                        Spacer()
                    }
                    
                    Spacer()
                    HStack {
                        Spacer()
                        Button("Change Car1") {
                            car1.rentDate = .now
                        }
                        Spacer()
                        Button("Change Car2") {
                            car2.rentDate = .now
                        }
                        Spacer()
                    }
                    Spacer()
                    Text("model.rentDate: (model.rentDate)")
                    Spacer()
                }
            }
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search