Reputation:
I am able to get all other fields, but I am not sure how to map the field newItems
inside the Rooms
since it is an array of struct Item
.
I have tried let newItem = data["newItems"] as? [Item] ?? []
, but this does not seem to be working.
Room.swift
struct Room: Codable, Identifiable {
var id: String
var title: String
var newItems: [Item]
var members: [String]
}
Item.swift
struct Item: Codable, Identifiable {
var id: String?
var name: String
var desc: String
var qty: String
var assignedTo: String
}
Auth.swift
@Published var rooms = [Room]()
func populateRoomList () {
DispatchQueue.main.async {
Firestore.firestore().collection("rooms")
.whereField("members", arrayContains: self.userSession!.uid)
.addSnapshotListener { snapshot, error in
guard let doc = snapshot?.documents else {
print("No Doc Found")
return
}
self.rooms = doc.map({ docSnapshot -> Room in
let data = docSnapshot.data()
let docId = docSnapshot.documentID
let title = data["title"] as? String ?? ""
let mem = data["members"] as? [String] ?? []
return Room(id: docId, title: title, newItems: , members: mem)
})
}
}
}
Upvotes: 0
Views: 590
Reputation: 7254
You're almost there - using Codable
for your data structs is the first step.
Instead of mapping your documents manually (as you do), Codable
allows you to rely on the Firestore SDK and the Swift compiler to do the heavy lifting for you. I've written a comprehensive guide about how this works and how to use it, including code snippets for fetching single documents and entire collections or queries - check it out: Mapping Firestore Data in Swift - The Comprehensive Guide.
In your case, here is a modified version of the code for your repository (I wouldn't recommend putting this code Auth.swift
, as the main concern is not about auth, but fetching data):
class RoomRepository: ObservableObject {
@Published var rooms = [Room]()
@Published var user: User
@Published var errorMessage: String?
private var db = Firestore.firestore()
private var listenerRegistration: ListenerRegistration?
public func unsubscribe() {
if listenerRegistration != nil {
listenerRegistration?.remove()
listenerRegistration = nil
}
}
func subscribe() {
if listenerRegistration == nil {
listenerRegistration = db.collection("rooms")
.whereField("members", arrayContains: user.uid)
.addSnapshotListener { [weak self] (querySnapshot, error) in
guard let documents = querySnapshot?.documents else {
self?.errorMessage = "No documents in 'colors' collection"
return
}
self.rooms = documents.compactMap { queryDocumentSnapshot in
let result = Result { try queryDocumentSnapshot.data(as: Room.self) }
switch result {
case .success(let room):
if let room = room {
// A Room value was successfully initialized from the DocumentSnapshot.
self?.errorMessage = nil
return room
} else {
// A nil value was successfully initialized from the DocumentSnapshot,
// or the DocumentSnapshot was nil.
self?.errorMessage = "Document doesn't exist."
return nil
}
case .failure(let error):
// A Room value could not be initialized from the DocumentSnapshot.
self?.errorMessage = error.localizedDescription
return nil
}
}
}
}
}
}
A couple of notes:
DispatchQueue.main.async
. Instead of wrapping the entire block, you should only wrap the code that accessed the UI (i.e. the code inside the completion block).Codable is a very powerful tool, have a look at my blog post to learn about all the different ways you can use it to map data - I cover a wide range of use cases and data types. If you find anything is missing, let me know (by filing a feature request here) and I will add it. There's also a GitHub repo with all the source code: https://github.com/peterfriese/Swift-Firestore-Guide
Upvotes: 1