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 unlockDate
in 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 BoughtItemOverlay
should 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()
}
}
}