You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 22:21:02