Giter VIP home page Giter VIP logo

cachetrackerconsumer's Introduction

CacheTrackerConsumer

Helper classes for use as mediator between CacheTracker and UI controls (UITableView, UICollectionView). It keeps local one or two dimentional array of items in sync with cache tracker storage. UI controls can interact with consumer directly. This is very helpful when you use VIPER architecture. You place consumer in VIEW and pass transactions from INTERACTOR through PRESENTER to your VIEW. VIEW in this case stays passive as VIPER rules requires and its state is controlled from PRESENTER only.

Changes

See CHANGELOG

Types

CacheTrackerPlainConsumer

Just keep plain items in linear array in sync with cache tracker storage and generates updates for UI controls.

CacheTrackerPlainRecurrentConsumer

Works like CacheTrackerPlainConsumer, the only difference: whne some item is updated or moved to another index, then you can save some properties from old value to new one, this can be usefull, for example, when you add new 'man' object to array with 'personal number', this number should not be changed until item is deleted from array, other fields can change:

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

Just keep plain items in sectioned array in sync with cache tracker storage and generates updates for UI controls. Cache tracker generates linear array of plain items, but they are converted to sectioned array and UI controls think they work with sections. You can say that "why do not use direct section notifications from CacheTracker" - and the answer is: i spent two weeks for analyzing behaviour of notifications generated by CoreData/Realm FRC, i understood, that it is "hell" when you try to use sections in FRC, and there is no normal way to keep items in own two dimentional array in sync with storage managed by only FRC, so i decided to write these classes to make it easy.

NOTE: If you have millions of items, then i recommend to you direct interaction of VIEW with CacheTracker, because of performance issue.

Usage

To use with CacheTrackerSectionedConsumer just make your plain model compatibel with CacheTrackerSectionedConsumerModel protocol. For complete examples see Demo project.

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
    }

}

Create consumer and set its delegate

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

When you use CacheTrackerSectionedConsumer then do not forget to pass section key as first sort descriptor (in this example we want to use 'section' as section key)

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

Fill up consumer with initial set of objects if required

  • using animated insert
consumer.willChange()
consumer.consume(transactions: cacheTracker.transactionsForCurrentState())
consumer.didChange()
  • complete reload
consumer.reset(with: cacheTracker.transactionsForCurrentState())
tableView.reloadData()

NOTE: You should call willChange() before any batch calls to consume(), add(), remove(), update() of consumer. After all batch operations were called you have to complete interaction with didChange() as in example above.

Implement table view datasource (or collection view), where you will refer to consumer methods:

  • where cacheTrackerSectionOffset == 0 and cacheTrackerItemsOffset == 0 (by default)
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
}
  • where cacheTrackerSectionOffset > 0 or 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)"
    }
}

Offsets

If you want to show items not from the beginning, then you can set offsets using:

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

They control offset of data section / items from the beginning of the tableView or collectionView.

For more detailed example see Demo project

Installation

Cocoapods

Add to you Podfile

pod 'CacheTrackerConsumer'

If you want to use UIKit extensions

pod 'CacheTrackerConsumer'
pod 'CacheTrackerConsumer/UIKit'

UIKit extensions contains default implementations of CacheTrackerPlainConsumerDelegate (or CacheTrackerSectionedConsumerDelegate) for UITableView, UICollectionView, UITableViewController, UICollectionViewController, etc...

And finally import the module

import CacheTrackerConsumer

cachetrackerconsumer's People

Contributors

ladeiko avatar

Watchers

 avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.