具有 Repository/Firestore 的 MVVM - 存储来自单个集合的不同查询数组的最佳位置在哪里?

MVVM with Repository/Firestore - Where is the best place to store different queried arrays from a single collection?

我正在基于此 google tutorial 使用 Firestore 构建一个待办事项列表应用程序,并使用一个使用 MVVM/repository 模式的 SwiftUI 应用程序,它使用一个加载查询来查找所有任务(“操作”) ,并且我正在尝试对其进行设置,以便我可以进行多个基于日期的查询(例如,显示今天、下周的日期,可能在同一屏幕上)

我正在使用的当前版本在存储库中只有一个“loadData”函数,该函数保存到一个已发布的“actions”变量中,并在初始化时调用。

class ActionRepository: ObservableObject, ActionStoreType {
    
    let db = Firestore.firestore()
    
    @Published var actions = [Action]()
    
    init() {
        loadData()
    }
    
    func loadData() {
        let userId = Auth.auth().currentUser?.uid
        
        db.collection("action")
            .order(by: "createdTime")
            .whereField("userId", isEqualTo: userId!)
            .addSnapshotListener { (querySnapshot, error) in
                if let querySnapshot = querySnapshot {
                    self.actions = querySnapshot.documents.compactMap { document in
                        do {
                            let x = try document.data(as: Action.self)
                            return x
                        }
                        catch {
                            print(error)
                        }
                        return nil
                    }
                }
            }
    }

我的视图模型只是调用没有参数的存储库。

class ActionListViewModel: ObservableObject {
    @Published var actionRepository = ActionRepository()
    @Published var actionCellViewModels = [ActionCellViewModel]()
    
    private var cancellables = Set<AnyCancellable>()
    
    init() {
        actionRepository.$actions.map { actions in
            actions.map { action in
                ActionCellViewModel(action: action)
            }
        }
        .assign(to: \.actionCellViewModels, on: self)
        .store(in: &cancellables)
    }

我想添加一个函数来按日期加载数据,我可以调用任意多次:

    func loadMyDataByDate2(from startDate: Date, to endDate: Date? = nil) {
        let userId = Auth.auth().currentUser?.uid
        let initialDate = startDate
        var finalDate: Date
        
        if endDate == nil {
            finalDate = Calendar.current.date(byAdding: .day, value: 1, to: initialDate)!
        } else {
            finalDate = endDate!
        }
        
        db.collection("action")
            .order(by: "createdTime")
            .whereField("userId", isEqualTo: userId!)
            .whereField("startDate", isGreaterThanOrEqualTo: initialDate)
            .whereField("startDate", isLessThanOrEqualTo: finalDate)
            .addSnapshotListener { (querySnapshot, error) in
                if let querySnapshot = querySnapshot {
                    self.actions = querySnapshot.documents.compactMap { document in
                        do {
                            let x = try document.data(as: Action.self)
                            return x
                        }
                        catch {
                            print(error)
                        }
                        return nil
                    }
                }
            }

但我不知道执行此操作的最佳方法。如果我希望我的视图模型具有三个任务列表:一个用于今天,一个用于本周剩余时间,一个用于下周,那么最好的方法是什么?

我应该在存储库或视图模型中创建单独的变量来存储这些不同的操作列表吗? 或者为存储库添加日期变量,以便我在视图模型中调用它的多个实例?

我只是想确保我在开始构建它时不会走上不明智的道路。

我最终根据彼得的建议做了一些事情。我在我的 ViewModel 中获取了所有这些过滤列表。我没有从存储库中获取并将它们全部存储在一个 ActionCellViewModel 属性 中,而是创建了四个不同的 ActionCellViewModel 属性。

现在我的初始化程序代码中有四个不同的函数,每个函数都获取操作列表,根据日期和完成状态对其进行过滤,并将其分配给适当的 CellViewModel 属性 以供我使用查看。

class ActionListViewModel: ObservableObject {
    @Published var actionRepository: ActionStoreType
    
    @Published var baseDateActionCellViewModels = [ActionCellViewModel]()
    @Published var baseDateWeekActionCellViewModels = [ActionCellViewModel]()
    @Published var previousActionCellViewModels = [ActionCellViewModel]()
    @Published var futureActionCellViewModels = [ActionCellViewModel]()
    
    @Published var baseDate: Date = Date()
    @Published var hideCompleted: Bool = true
    @Published var baseDateIsEndOfWeek: Bool = false
    
    private var cancellables = Set<AnyCancellable>()
    
    // MARK: Initializers
    
    // Default initializer for production code.
    init() {
        self.actionRepository = ActionRepository()
        self.baseDateIsEndOfWeek = isDateEndOfWeek(date: self.baseDate, weekEnd: self.baseDate.endOfWeekDate(weekStart: .sat))
        
        loadPastActions()
        loadBaseActions()
        loadWeekTasks()
        loadFutureActions()
    }
    
    
    // MARK: Functions for initializing the main groups of actions for the Homepage.
    
    
    func isDateEndOfWeek(date currentDate: Date, weekEnd endOfWeekDate: Date) -> Bool {

        if currentDate == endOfWeekDate {
            print("Current Date: \(currentDate) and endOfWeekDate: \(endOfWeekDate) are the same!")
            return true
        } else {
            print("The current date of \(currentDate) is not the end of the week (\(endOfWeekDate))")
            return false
        }
    }
    
    ///The loadPastActions function takes the published actions list from the repository, and pulls a list of actions from before the base date. (It hides completed actions by default, but this is impacted by the viewModel's "hideCompleted" parameter.
    ///
    ///- returns: Assigns a list of actions from prior to the base date to the pastActionCellViewModels published property in the viewModel.
    func loadPastActions() {
        self.actionRepository.actionsPublisher.map { actions in
            actions.filter { action in
                action.beforeDate(self.baseDate) && action.showIfIncomplete(onlyIncomplete: self.hideCompleted)
            }
            .map { action in
                ActionCellViewModel(action: action)
            }
        }
        .assign(to: \.previousActionCellViewModels, on: self)
        .store(in: &cancellables)
    }
    
    ///The loadBaseActions function takes the published actions list from the repository, and pulls a list of actions from the base date. (It hides completed actions by default, but this is impacted by the viewModel's "hideCompleted" parameter.
    ///
    ///- returns: Assigns a list of actions from the base date to the viewModel's baseDateActionCellViewModels property.
    func loadBaseActions() {
        self.actionRepository.actionsPublisher.map { actions in
            actions.filter { action in
                action.inDateRange(from: self.baseDate, to: self.baseDate) && action.showIfIncomplete(onlyIncomplete: self.hideCompleted)
            }
            .map { action in
                ActionCellViewModel(action: action)
            }
        }
        .assign(to: \.baseDateActionCellViewModels, on: self)
        .store(in: &cancellables)
    }
    
    /// The loadWeekActions takes the published actions list for the current user from the repository, and pulls a list of actions either from remainder of the current week (if not the end of the week), or from next week, if it's the last day of the week.
    ///
    ///- returns: Assigns a list of actions from the rest of this week or the next week to the viewModel's baseDateWeekActionCellViewModels property.
    func loadWeekTasks() {
        let startDate: Date = self.baseDate.tomorrowDate()
        print("Start date is \(startDate) and the end of that week is \(startDate.endOfWeekDate(weekStart: .sat))")
        
        self.actionRepository.actionsPublisher.map { actions in
            actions.filter { action in
                action.inDateRange(from: startDate, to: startDate.endOfWeekDate(weekStart: .sat)) && action.showIfIncomplete(onlyIncomplete: self.hideCompleted)
            }
            .map { action in
                ActionCellViewModel(action: action)
            }
        }
        .assign(to: \.baseDateWeekActionCellViewModels, on: self)
        .store(in: &cancellables)
    }
    
    /// The loadFutureActions function takes the published actions list for the current user from the repository, and pulls a list of actions from after the week tasks.
    ///
    ///- returns: Assigns a list of actions from the future (beyond this week or next, depending on whether the baseDate is the end of the week) to the futureActionCellViewModels property in the viewModel.
    func loadFutureActions() {
        let startAfter: Date = baseDate.tomorrowDate().endOfWeekDate(weekStart: .sat)
        
        self.actionRepository.actionsPublisher.map { actions in
            actions.filter { action in
                action.afterDate(startAfter) && action.showIfIncomplete(onlyIncomplete: self.hideCompleted)
            }
            .map { action in
                ActionCellViewModel(action: action)
            }
        }
        .assign(to: \.futureActionCellViewModels, on: self)
        .store(in: &cancellables)
    }