如何将Identifiable结构体数组存入Firestore并读取子集合文档
存储viewModels到Firestore实现方案
你需要先将Swift自定义结构体转换为Firestore支持的键值对格式,再写入子集合,读取时再映射回原有结构体即可完整保留数据结构。
1. 为结构体新增序列化/反序列化方法
给Restaurants结构体增加转字典方法和从Firestore文档初始化的构造器:
struct Restaurants: Identifiable { let name: String let imageUrl: URL let id: String let rating: Double let url: String let category: String // 序列化为Firestore可存储的字典格式 func toDictionary() -> [String: Any] { return [ "name": name, "imageUrl": imageUrl.absoluteString, "id": id, "rating": rating, "url": url, "category": category ] } // 从Firestore文档反序列化为结构体 init?(document: [String: Any]) { guard let name = document["name"] as? String, let imageUrlStr = document["imageUrl"] as? String, let imageUrl = URL(string: imageUrlStr), let id = document["id"] as? String, let rating = document["rating"] as? Double, let url = document["url"] as? String, let category = document["category"] as? String else { return nil } self.name = name self.imageUrl = imageUrl self.id = id self.rating = rating self.url = url self.category = category } static var viewModels: [Restaurants] = [ Restaurants(name: "Silverio's Mexican Kitchen", imageUrl: URL(string: "https://mainsite-prod-cdn.azureedge.net/partner-images/432257/micrositeimage_p1.jpg")!, id: "hjkhjhjh", rating: 2, url: "https://google.com", category: "Mexican"), Restaurants(name: "Taqueria La Esquinita", imageUrl: URL(string: "https://s3-media0.fl.yelpcdn.com/bphoto/x-KCQ7osmvBWLA9WpPdO_Q/o.jpg")!, id: "hjdha", rating: 3, url: "https://google.com", category: "Mexican") ] { didSet { print("data set") } } }
注意:URL类型无法直接存入Firestore,存储时转成字符串、读取时转回URL即可完全还原原有结构
你的数据结构参考:
2. 将viewModels写入Firestore子集合
假设你的根集合为聚餐群组集合groups,每个群组文档下的餐厅子集合名为restaurants,存储代码如下:
import FirebaseFirestore let db = Firestore.firestore() // 替换为你当前实际的群组文档ID let currentGroupId = "你的群组文档ID" let groupRef = db.collection("groups").document(currentGroupId) // 遍历所有餐厅数据写入子集合,用餐厅id作为子集合文档ID方便后续查询 for restaurant in Restaurants.viewModels { groupRef.collection("restaurants").document(restaurant.id).setData(restaurant.toDictionary()) { error in if let error = error { print("存储失败: \(error.localizedDescription)") } } }
3. 从子集合读取文档
3.1 读取子集合所有餐厅数据
func fetchAllRestaurants(groupId: String) { let db = Firestore.firestore() db.collection("groups").document(groupId).collection("restaurants").getDocuments { snapshot, error in if let error = error { print("读取失败: \(error.localizedDescription)") return } guard let docs = snapshot?.documents else { print("无餐厅数据") return } // 映射为原有结构体数组,直接赋值给viewModels即可使用 let restaurants = docs.compactMap { Restaurants(document: $0.data()) } Restaurants.viewModels = restaurants } }
3.2 读取子集合单个指定餐厅文档
func fetchSingleRestaurant(groupId: String, restaurantId: String) { let db = Firestore.firestore() db.collection("groups").document(groupId).collection("restaurants").document(restaurantId).getDocument { snapshot, error in if let error = error { print("读取失败: \(error.localizedDescription)") return } guard let data = snapshot?.data(), let restaurant = Restaurants(document: data) else { print("找不到对应餐厅") return } // 直接使用拿到的restaurant对象即可 print(restaurant.name) } }
4. 多设备实时同步方案
如果需要多设备自动同步数据,替换读取方法为快照监听器即可,子集合数据发生变化时所有设备会自动收到更新:
var listener: ListenerRegistration? // 添加实时监听 func addRestaurantsSyncListener(groupId: String) { let db = Firestore.firestore() listener = db.collection("groups").document(groupId).collection("restaurants").addSnapshotListener { snapshot, error in guard let docs = snapshot?.documents else { return } let restaurants = docs.compactMap { Restaurants(document: $0.data()) } Restaurants.viewModels = restaurants } } // 页面销毁时移除监听避免内存泄漏 func removeSyncListener() { listener?.remove() }
内容的提问来源于stack exchange,提问作者Mcrich
相关产品推荐
相关产品推荐

