Home > Software engineering >  Is there a way to execute a task before anything else happens?
Is there a way to execute a task before anything else happens?

Time:09-10

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.

CodePudding user response:

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.

CodePudding user response:

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()
        }
    }
}

  • Related