Swift使用Firebase获取数据时生成16张重复图片的问题求解
问题根因
你得到16张图片的核心原因是逻辑产生了笛卡尔积:
- 你共有4个Firestore文档,每遍历1个文档就会调用1次
getImage()方法 getImage()方法内部会拉取storage中pictures目录下的全部4张图片,每张图片加载完成后都会触发回调返回- 4次
getImage()调用 × 每次返回4张图片 = 16次回调,最终就得到了16张重复图片
另外还有两处冗余逻辑:
- 遍历文档内所有字段判断
name == "field1"完全没必要,可直接通过key取值 - 文档和图片没有做对应关联,属于随机拼接,不符合业务逻辑预期
修复方案
最优方案(推荐)
首先在每个Firestore文档中存储对应图片的唯一文件名,拉取文档时直接根据文件名拉取对应的单张图片,避免拉取全部图片。
修正后的代码如下:
class APIManager { private init() {} static let shared = APIManager() private let storageRef = Storage.storage().reference(forURL: "gs://sneakershop-b309a.appspot.com").child("pictures") func fetchData(collectionType: CollectionType, completion: @escaping (Document) -> Void) { let db = Firestore.firestore() db.collection(collectionType.rawValue).getDocuments { (querySnapshot, error) in if let error = error { print("拉取数据出错: \(error)") return } guard let documents = querySnapshot?.documents else { return } for queryItem in documents { // 直接取field1值,不需要遍历所有字段 guard let field1 = queryItem.get("field1") as? String, // 这里假设文档中存储了对应图片的字段imageName,替换为你实际的字段名 let imageName = queryItem.get("imageName") as? String else { continue } // 仅拉取当前文档对应的单张图片 self.getSingleImage(imageName: imageName) { image in let document = Document(name: queryItem.documentID, field1: field1, field2: queryItem.documentID, image: image) completion(document) } } } } // 新增单张图片拉取方法 private func getSingleImage(imageName: String, completion: @escaping (UIImage) -> Void) { let imageRef = storageRef.child(imageName) imageRef.getData(maxSize: 1024 * 1024) { data, error in if let error = error { print("拉取图片出错: \(error.localizedDescription)") return } guard let data = data, let image = UIImage(data: data) else { return } completion(image) } } }
临时方案(按顺序匹配文档和图片)
如果你不想修改Firestore文档结构,一定要按顺序匹配4个文档和4张图片,可先一次性拉取全部图片存储为数组,再遍历文档按索引取对应图片,避免重复拉取:
class APIManager { private init() {} static let shared = APIManager() private let storageRef = Storage.storage().reference(forURL: "gs://sneakershop-b309a.appspot.com").child("pictures") func fetchData(collectionType: CollectionType, completion: @escaping ([Document]) -> Void) { let db = Firestore.firestore() // 先拉取全部图片 self.getAllImages { [weak self] images in guard let self = self else { return } // 再拉取文档 db.collection(collectionType.rawValue).getDocuments { (querySnapshot, error) in if let error = error { print("拉取数据出错: \(error)") return } guard let documents = querySnapshot?.documents, images.count == documents.count else { return } var result = [Document]() for (index, queryItem) in documents.enumerated() { guard let field1 = queryItem.get("field1") as? String else { continue } let document = Document(name: queryItem.documentID, field1: field1, field2: queryItem.documentID, image: images[index]) result.append(document) } completion(result) } } } // 拉取全部图片,仅调用一次 private func getAllImages(completion: @escaping ([UIImage]) -> Void) { storageRef.listAll { (result, error) in if let error = error { print("拉取图片列表出错: \(error)") return } var images = [UIImage]() let group = DispatchGroup() for item in result.items { group.enter() item.getData(maxSize: 1024 * 1024) { data, error in defer { group.leave() } if let error = error { print("拉取图片出错: \(error.localizedDescription)") return } guard let data = data, let image = UIImage(data: data) else { return } images.append(image) } } group.notify(queue: .main) { completion(images) } } } }
内容的提问来源于stack exchange,提问作者Uladzislau Yakubovsky
相关产品推荐
相关产品推荐

