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

Posted

技术标签:

【中文标题】带有 Repository/Firestore 的 MVVM - 存储来自单个集合的不同查询数组的最佳位置在哪里?【英文标题】:MVVM with Repository/Firestore - Where is the best place to store different queried arrays from a single collection? 【发布时间】:2021-03-03 03:37:04 【问题描述】:

我正在使用基于 google tutorial 的 Firestore 构建一个待办事项列表应用程序,以及一个使用 MVVM/存储库模式的 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
                    
                
            

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

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

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

【问题讨论】:

过去几天没有人回答这个问题,这里有一个快速建议:将数据提取到已发布的属性中,就像您所做的那样。听起来您的其他列表是此主列表的过滤子集,您应该能够添加几个组合管道来执行过滤并将结果分配给其他一些已发布的属性。 LMK 如果这有帮助,我很乐意草拟一个更详细的解决方案作为答案。 @PeterFriese 感谢您的回复!我最终做了你建议的事情,将我的其他过滤列表直接放在 ViewModel 中。这似乎现在工作正常。感谢您的帮助! 太棒了,很高兴听到你能够解决这个问题:-) 【参考方案1】:

我最终根据彼得的建议做了一些事情。我在我的 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)
    

【讨论】:

以上是关于带有 Repository/Firestore 的 MVVM - 存储来自单个集合的不同查询数组的最佳位置在哪里?的主要内容,如果未能解决你的问题,请参考以下文章

使用带有 uuencode 的“sendmail”发送邮件,并带有主题

带有和不带有聚合的 sql 查询

带有滚动的 Div 和带有绝对位置的内容

带有 RecyclerView 的 DialogFragment 比带有 Recyclerview 的 Fragment 慢

如何翻转正面带有标签而背面带有另一个标签的视图 - 参见图片

CakePHP 如何处理带有/不带有 'id' 字段的 HABTM 表?