skip to Main Content

I need to fetch some data before passing it to a View. I created a ViewModel BoughtItemViewModel for the my BoughtItemView.

For simplicity I only provide one case, my enum has some more cases I switch through depending on what item is bought. This is the BoughtItemView:

enum SomeType {
    case song(Song)
}
struct BoughtItemCard: View {
    @State var type: SomeType
    @StateObject var vm = BoughtItemViewModel()
    
    var body: some View {
        switch type {
        case .song(let song):
            WebImage(url: URL(string: song.image))
                .resizable()
                .frame(width: 150, height: 150)
                .overlay(BoughtItemOverlay(type: type)
                .environmentObject(vm)
                .onAppear() {
                        vm.fetchUnlockDate(path: "songs", docId: song.id ?? "")
      }
   }
}

This is the BoughtItemViewModel:

class BoughtItemViewModel: ObservableObject {
    @Published var unlockDate = Date()
    
    func fetchUnlockDate(path: String, docId: String) {
        let uid = FirebaseManager.shared.auth.currentUser?.uid  ?? ""
        FirebaseManager.shared.firestore.collection(path)
        .document(docId).collection("unlocked").document(uid).getDocument { docSnapshot, error in
            if let error = error {
                print(error)
                return
            }
            let data = docSnapshot?.data()
            self.unlockDate = (data?["unlockDate"] as? Date ?? Date())
        }
    }
}

Now I want the unlockDatein my BoughtItemOverlay View to show the fetched date.
Again for simplicity I provide one case, this is the BoughtItemOverlay:

struct BoughtItemOverlay: View {
    @State var showModal: Bool = false
    @State var type: SomeType
    @State var unlockDate = Date()
    @EnvironmentObject var vm: BoughtItemViewModel
    
    var body: some View {
        switch type {
        case .song(let song):
            VStack {
                Spacer().onAppear() {
                    unlockDate = vm.unlockDate
                }
                Text("Purchased (unlockDate.formatted(.dateTime.day().month(.defaultDigits).year()))")
         }
      }
   }
}

Instead of displaying the unlockDate it always displays the date of today. I’m not sure why that is since the BoughtItemOverlayshould refresh after the State changes in the onAppear() setting the value to the BoughtItemViewModel value. Atleast that I think it should work that way, but correct me if I’m wrong.

2

Answers


  1. Chosen as BEST ANSWER

    Firebase returns a FIRTimestamp so I had to change my code to:

    import Foundation
    import FirebaseFirestore
    
    class BoughtItemViewModel: ObservableObject {
        @Published var unlockDate = Date()
        
        func fetchUnlockDate(path: String, docId: String) {
            let uid = FirebaseManager.shared.auth.currentUser?.uid  ?? ""
            FirebaseManager.shared.firestore.collection(path).document(docId).collection("unlocked").document(uid).getDocument { docSnapshot, error in
                if let error = error {
                    print(error)
                    return
                }
                let data = docSnapshot?.data()
                let timestamp = data?["unlockDate"] as? Timestamp
                self.unlockDate = timestamp?.dateValue() ?? Date()
            }
        }
    }
    
    

  2. With @State var unlockDate = Date() you are creating a new source of truth and try to synchronize it with unlockDate = vm.unlockDate.

    Don´t do that, use the Viewmodel itself in your Childview:

    struct BoughtItemOverlay: View {
        @State var showModal: Bool = false
        @State var type: SomeType
        
        @EnvironmentObject var vm: BoughtItemViewModel
        
        var body: some View {
            switch type {
            case .song(let song):
                VStack {
                    Spacer()
                    // use the vm value here
                    Text("Purchased (vm.unlockDate.formatted(.dateTime.day().month(.defaultDigits).year()))")
             }
          }
       }
    }
    

    The Viewmodel will notify the View when unlockDate has changed and the View will show the new date.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search