SwiftUI中Firestore新用户收藏文档创建的网络可靠性问题
问题背景
我的SwiftUI应用在Firebase Auth新用户注册完成后,会自动为其在Firestore中创建favourites文档。当前实现逻辑如下:
if let authResult = result, authResult.additionalUserInfo?.isNewUser == true { // 新用户,创建Favourites self.createFavourites() } else { // 已有用户 }
对应的文档创建函数:
func createCollection(userID: String, name: String, description: String, isPublic: Bool, completion: @escaping (Error?) -> Void){ let privateCollectionsRef = Firestore.firestore().collection("users").document(userID).collection("private_collections") let newCollectionData = [ "name": name, "description": description, "date": Date(), //"isPublic": isPublic, "favourites": [:] // 初始化为空的收藏映射 ] as [String : Any] privateCollectionsRef.addDocument(data: newCollectionData) { error in if let error = error { print("Error creating collection: \(error.localizedDescription)") completion(error) } else { print("Collection created successfully.") completion(nil) } } }
我担心网络中断会导致文档创建失败,为此考虑了几种方案:
- 修改为每次用户登录时检查文档(但怕重复创建或覆盖已有数据)
- 创建前先检查文档是否存在,仅在不存在时创建
- 实现重试机制
- 创建成功后用UserDefaults标记,避免后续重复尝试
但无论选哪种方案,我都有顾虑:要么创建前读不到已存在的文档,要么首次创建失败后没有补救措施。另外我还有个疑问:如果写入失败,Firestore缓存会不会保留这个文档?调用getDocuments时会不会返回缓存内容?
解决方案与关键说明
关于Firestore缓存的疑问
首先明确:如果写入操作因网络问题失败,Firestore本地缓存不会保存未成功提交的文档。只有当写入成功同步到后端后,缓存才会更新。因此调用getDocuments时,不会读到未成功创建的文档。
推荐实现方案
结合你的需求,最稳妥的方式是使用固定文档ID配合setData方法,同时利用Firestore内置的重试机制:
修改创建逻辑为固定文档ID
给favourites文档指定固定ID(比如直接用"favourites"),替换自动生成ID的addDocument,改用setData(merge: false):func createFavourites(userID: String, completion: @escaping (Error?) -> Void) { // 指定固定的文档ID,确保唯一性 let favouritesDocRef = Firestore.firestore() .collection("users") .document(userID) .collection("private_collections") .document("favourites") let initialData = [ "name": "Favourites", "description": "Your saved favourite items", "date": Date(), "favourites": [:] ] as [String : Any] // merge: false 表示仅在文档不存在时写入,存在则不修改 favouritesDocRef.setData(initialData, merge: false) { error in if let error = error { print("Failed to set favourites: \(error.localizedDescription)") completion(error) } else { print("Favourites ready (created or already exists)") completion(nil) } } }这种方式的核心优势:
- 幂等性:无论调用多少次,只会生成一个
favourites文档,不会重复创建 - 安全不覆盖:
merge: false时,若文档已存在,不会更改现有数据,直接返回成功
- 幂等性:无论调用多少次,只会生成一个
简化登录逻辑,依赖内置重试
不需要提前检查文档是否存在,直接在用户每次登录时调用createFavourites即可。Firestore SDK会自动处理网络波动的情况:当网络恢复后,未成功的写入请求会自动重试,无需手动实现重试逻辑。UserDefaults标记作为优化(可选)
如果想减少不必要的Firestore请求,可以在首次创建成功后,用UserDefaults存储一个用户专属标记:// 创建成功后调用 UserDefaults.standard.set(true, forKey: "hasFavourites_\(userID)")下次登录时先检查这个标记,只有标记不存在时才调用
createFavourites。但注意:用户卸载重装App后标记会丢失,所以必须保留setData的幂等逻辑作为兜底。
避坑关键点
- 不要用
addDocument:自动生成的ID会导致多次调用创建多个重复文档 - 信任Firestore内置重试:SDK会处理网络中断后的自动重试,无需自己写复杂逻辑
- 不要依赖本地缓存判断文档状态:缓存可能过期,必须以Firestore后端的实际状态为准
内容的提问来源于stack exchange,提问作者Phil

