CacheTrackerConsumer 1.8.0

CacheTrackerConsumer 1.8.0

测试已测试
语种语言 SwiftSwift
许可证 MIT
发布最后发布2023年5月
SPM支持SPM

Siarhei Ladzeika维护。



  • Siarhei Ladzeika

CacheTrackerConsumer

提供辅助类,用作CacheTracker和UI控件(UITableView、UICollectionView)之间的中介。它与缓存跟踪存储同步保持本地的单维或二维数组。UI控件可以直接与消费者交互。这对于使用VIPER架构非常有用。您将消费者放入VIEW,并通过PRESENTER将交易从INTERACTOR传递到您的VIEW。在这种情况下,VIEW保持被动,符合VIPER规则,其状态仅由PRESENTER控制。

更改

参见变更日志

类型

CacheTrackerPlainConsumer

仅在线性数组中保留普通项,与缓存跟踪存储同步,并为UI控件生成更新。

CacheTrackerPlainRecurrentConsumer

与CacheTrackerPlainConsumer功能类似,唯一的区别在于:当某个项被更新或移动到另一个索引时,可以将一些属性从旧值保存到新值,这对于某些应用场景很有用,例如,向数组添加新的“人”对象时,具有“个人编号”的该编号应保持不变,直到项从数组中删除,其他字段可以更改。

class Man: CacheTrackerPlainRecurrentConsumerItem {
    
  let personalNumber: String      // this value should be stay the same
  let firstName: String           // can change
  let lastName: String            // can change

  init(personalNumber: String, firstName: String, lastName: String) {
  	self.personalNumber = personalNumber
  	self.firstName = firstName
  	self.lastName = lastName
  }
    
  // MARK: CacheTrackerPlainModel
  
  init() {
  	personalNumber = ""
  	firstName = ""
  	lastName = ""
  }
  
  // MARK: CacheTrackerPlainRecurrentConsumerItem
  
  func recurrentPlainConsumerItem(using oldValue: CacheTrackerPlainRecurrentConsumerItem) -> CacheTrackerPlainRecurrentConsumerItem {
      let oldValue = oldValue as! Man
      // Even if item is updated its 'personalNumber' will have the same value since it first time appeared in consumer.
      return Man(personalNumber: oldValue.personalNumber, firstName: firstName, lastName: lastName)
  }
  
}


let consumer = CacheTrackerPlainRecurrentConsumer<Man>()
consumer.willChange()
consumer.add(Man(personalNumber: "123", firstName:"John", lastName:"Smith"), at: 0)
consumer.didChange()

consumer.willChange()
// NOTE: new value "456" will be ignored
consumer.update(Man(personalNumber: "456", firstName:"John", lastName:"Smith"), at: 0) 
consumer.didChange()

let personalNumber = consumer.object(at: 0).personalNumber

// will print '123', NOT '456'
print(personalNumber) 

CacheTrackerSectionedConsumer

仅在分节数组中保留普通项,与缓存跟踪存储同步,并为UI控件生成更新。缓存跟踪生成线性数组中的普通项,但它们被转换为分节数组,UI控件似乎正在使用分节进行操作。可以说,“为什么不直接使用CacheTracker的分节通知?”- 答案是:我花费了两周时间分析CoreData/Realm FRC产生的通知行为,我理解,当在FRC中尝试使用分节时,这是一个“地狱”,而且在没有正常方法将项保存在自己的二维数组中以与仅由FRC管理的存储同步的情况下,所以我决定编写这些类以使其更容易。

注意:如果您有数百万条项目,则建议您直接与CacheTracker进行交互,因为性能问题。

使用方法

要与 CacheTrackerSectionedConsumer 一起使用,只需确保你的普通模型兼容 CacheTrackerSectionedConsumerModel 协议。完整的示例请参考 Demo 项目。

class PlainItem: CacheTrackerPlainModel, CacheTrackerSectionedConsumerModel {
    
    let name: String
    let section: String
    
    init(name: String) {
        self.name = name
        self.section = String(name.prefix(1))
    }
    
    // MARK: - CacheTrackerPlainModel
    
    required init() {
        self.name = ""
        self.section = ""
    }
    
    // MARK: - CacheTrackerSectionedConsumerModel
    
    func sectionTitle() -> String {
        return self.section
    }

}

创建消费者并设置其代理

consumer = CacheTrackerSectionedConsumer<PlainItem>()
consumer.delegate = self

当使用 CacheTrackerSectionedConsumer 时,不要忘记将分区键作为第一个排序描述符传递(在这个例子中,我们希望使用 'section' 作为分区键)

let cacheRequest = CacheRequest(predicate: NSPredicate(value: true), sortDescriptors: [
    NSSortDescriptor(key: #keyPath(CoreDataItem.section), ascending: true),
    NSSortDescriptor(key: #keyPath(CoreDataItem.name), ascending: true)
    ])

如果需要,使用初始对象集填充消费者

  • 使用动画插入
consumer.willChange()
consumer.consume(transactions: cacheTracker.transactionsForCurrentState())
consumer.didChange()
  • 完全重新加载
consumer.reset(with: cacheTracker.transactionsForCurrentState())
tableView.reloadData()

注意:应在调用消费者的 consume()add()remove()update() 的任何批量调用之前调用 willChange()。在调用所有批量操作后,必须像上面示例中那样完成与 didChange() 的交互。

实现表格视图数据源(或集合视图),在那里你可以调用消费者方法

  • 当 cacheTrackerSectionOffset 为 0 且 cacheTrackerItemsOffset 为 0(默认)时
override func numberOfSections(in tableView: UITableView) -> Int {
    return consumer.sectionsCount()
}
    
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return consumer.numberOfItems(at: section)
}
    
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    return tableView.dequeueReusableCell(withIdentifier: "Default")!
}
    
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    let item = consumer.object(at: indexPath)
    cell.textLabel?.text = item.name
}
    
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
    let item = consumer.object(at: IndexPath(row: 0, section: section))
    return item.section
}
  • 当 cacheTrackerSectionOffset > 0 或 cacheTrackerItemsOffset > 0 时
override func numberOfSections(in tableView: UITableView) -> Int {
    return self.globalSectionCount(1)
}
    
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    if self.isPlainDataSection(section) {
        return consumer.numberOfItems() + cacheTrackerItemsOffset
    }
    else {
        return 1
    }
}
    
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    if self.isPlainDataIndexPath(indexPath) {
        return tableView.dequeueReusableCell(withIdentifier: "Default")!
    }
    else {
        return tableView.dequeueReusableCell(withIdentifier: "Before")!
    }
}
    
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    if self.isPlainDataIndexPath(indexPath) {
        let item = consumer.object(at: self.plainIndexPath(from: indexPath).row)
        cell.textLabel?.text = item.name
    }
    else {
        cell.textLabel?.text = "Before \(indexPath)"
    }
}

偏移量

如果你想显示非起始位置的项目,则可以使用以下方式设置偏移量

  • var cacheTrackerItemsOffset: Int = 0
  • var cacheTrackerSectionOffset: Int = 0

它们控制数据分区/项目从表格视图或集合视图开始的偏移量。

更多详细示例请参考 Demo 项目

安装

Cocoapods

将以下内容添加到你的 Podfile 中

pod 'CacheTrackerConsumer'

如果您想使用UIKit扩展

pod 'CacheTrackerConsumer'
pod 'CacheTrackerConsumer/UIKit'

UIKit扩展包含了CacheTrackerPlainConsumerDelegate(或CacheTrackerSectionedConsumerDelegate)的默认实现,适用于UITableViewUICollectionViewUITableViewControllerUICollectionViewController等...

最后,导入模块

import CacheTrackerConsumer