首页 > 解决方案 > 返回并订阅 Firestore 子集合中的最新值

问题描述

我正在尝试加载或创建一个“季节”,它基本上是一个季度或一个月的时间块,作为子集合文档存储在云 Firestore 中的用户配置文件中。

我在 Season 子集合中有一个“order”字段,它随着每个新创建的季节而增加,我的查询只是找到最近的一个。

我调用这个函数(loadOrCreateCurrentSeason)来加载最近的季节,它返回一个组合未来——我的数据模型中的一个季节或一个错误,它订阅了它。只要没有错误,它就会返回它找到的季节,只要它返回的季节包含当前日期。

如果其中任何一个不正确,它将创建一个新文档并将该文档添加到 Firebase。

我遇到的问题是,在我用来返回最新季节的辅助函数中,当它加载文档并尝试将其转换为我的应用程序的季节模型(let x = try document.data(as: Season.self)时,我的应用程序一直返回 x 为零。

我不确定这是否是我的查询错误,或者我如何设置函数以将数据作为季节返回,但我想知道是否有任何东西可能阻止应用程序转换 Firestore文档成一个季节?

初始化存储库时调用的主函数:

    func loadOrCreateCurrentSeason() {
        var _ = loadMostRecentSeason()
            .sink(receiveCompletion: { error in
                self.addSeasonToSubcollection(season: Season(lastOrder: 0))
            }, receiveValue: { [weak self] season in
                    if season.endDate < Date() {
                        let oldOrder = season.order
                        self?.saveCurrentSeasonToPast()
                        self?.currentSeason = self?.createCurrentSeason(order: oldOrder)
                        self?.addSeasonToSubcollection(season: season)
                    } else {
                        self?.currentSeason = season
                    }
                })
            .store(in: &cancellables)
    }

调用帮助函数以专门从 Firebase 加载最新季节:

    func loadMostRecentSeason() -> Future<Season, ErrorLoadingSeason> {
        let seasonRef = db.collection("users").document(userProfile.id!).collection("season")
        
        return Future<Season, ErrorLoadingSeason> { promise in
            seasonRef.order(by: "order", descending: true)
                .limit(to: 1)
                .getDocuments() { (querySnapshot, err) in
                    if let err = err {
                        print("Error getting most recent season: \(err.localizedDescription)")
                        promise(Result.failure(ErrorLoadingSeason.NoSeasonFound))
                    } else {
                        for document in querySnapshot!.documents {
                            do {
                                let x = try document.data(as: Season.self)
                                if x != nil {
                                    promise(Result.success(x!))
                                } else {
                                    promise(Result.failure(ErrorLoadingSeason.NoSeasonFound))
                                }
                            } catch {
                                print("An error converting the most recent season")
                                print(error)
                                promise(Result.failure(ErrorLoadingSeason.couldNotConvertData))
                            }
                        }
                    }
                }
        }
    }

如果这有所不同,这是我的季节结构:

struct Season: Codable, Identifiable {
    
    // Identifiers
    @DocumentID var id: String?
    var title: String?
    var userId: String
    var order: Int
    
    //Completion Details
    var startDate: Date
    var endDate: Date
    // var seasonType: SeasonType?
    
    init(lastOrder: Int) {
        let userProfile = CurrentUserProfile.shared.currentUser!
        
        self.userId = userProfile.id!
        self.startDate = Date().startOfPeriod(userProfile.seasonLength ?? .quarter)
        self.endDate = Date().endOfPeriod(userProfile.seasonLength ?? .quarter)
        self.order = lastOrder + 1
        self.title = setTitle(userProfile: userProfile)
    }
    
}

标签: swiftgoogle-cloud-firestorecombine

解决方案


看起来这个问题与我如何在 .sink 方法中接收完成有关。

以前我把它设置为:

            .sink(receiveCompletion: { error in
                self.addSeasonToSubcollection(season: Season(lastOrder: 0))
            }, receiveValue: ...

我曾认为因为它没有撞到receiveValue:块,所以 x 一定没有设置,但似乎我需要对错误和成功完成的失败进行不同的处理(似乎是一个非常基本的错误)。

如果成功完成,添加一个 switch 语句来中断,似乎已经解决了这个问题:

    func loadOrCreateCurrentSeason() {
        var _ = loadMostRecentSeason()
            .sink(receiveCompletion: { completion in
                switch completion {
                case .finished:
                    break
                case .failure(let error):
                    print(error)
                    self.addSeasonToSubcollection(season: Season(lastOrder: 0))
                }
            }, receiveValue: { [weak self] season in
                    if season.endDate < Date() {
                        let oldOrder = season.order
                        self?.currentSeason = self?.createCurrentSeason(order: oldOrder)
                        self?.addSeasonToSubcollection(season: Season(lastOrder: oldOrder))
                    } else {
                        self?.currentSeason = season
                    }
                })
            .store(in: &cancellables)
    }

推荐阅读