NamadaNativefier 0.1.0

NamadaNativefier 0.1.0

Maintained by nayanda1.



  • By
  • nayanda

NamadaNativefier

NamadaNativiefier is local persistent data storage for iOS. This is the updated version of my old library iOSNativefier

CI Status Version License Platform

Example

To run the example project, clone the repo, and run pod install from the Example directory first.

Requirements

Installation

NamadaNativefier is available through CocoaPods. To install it, simply add the following line to your Podfile:

pod 'NamadaNativefier'

Author

nayanda, [email protected]

License

NamadaNativefier is available under the MIT license. See the LICENSE file for more info.

Storage Algorithm

NamadaNativefier is using LRU Algorithm. It contains 2 type of storage which is Memory Storage and Disk Storage. Both size can be assigned manually.

Store Data

  1. Store data to Memory Storage
  2. If Memory Storage is is full, it will remove the most oldest accessed data from memory until the space is enough for new data
  3. Data stored to the memory
  4. Store data to Disk Storage
  5. If Disk Storage is is full, it will remove the most oldest accessed data from memory until the space is enough for new data
  6. Data stored to the disk

Getting Data

  1. Find data from the Memory Storage
  2. If the data is exist, it will return the data and the step ended
  3. If the data is not exist in the memory, it will try to find data from Disk Storage
  4. If the data is exist, it will store the data to the Memory Storage for future faster use and return the data and the step ended
  5. If the data is not exist, it will return nil

Usage Example

Basic Usage

NamadaNativefier is designed as complementary for NamadaJSON. All you need to do is just get the Cache Manager from factory and store your object which implement Archivable, CodableArchivable or JSONArchivable:

let cache = try! NativefierFactory.shared.cache(
    for: MyArchivable.self,
    initialMaxMemorySize: 10.megaByte, 
    initialMaxDiskSize: 20.megaByte
)

// will insert object
cache.insert(myObject)

let object = cache.get(objectWithKey: "object_key")

Archivable

Archivable actually is just a protocol which have methods to convert object to data or vice versa. Archivable make sure the object have keys too:

class User: Archivable {
    
    var primaryKey: String { userName }
    var userName: String = ""
    var fullName: String = ""
    var age: Int = 0
    
    func archive() throws -> Data {
        // do something to convert the object to Data
    }
    
    static func deArchive(fromData data: Data) throws -> Archivable {
        // do something to convert the data to object
    }
}

CodableArchivable

If your object are Codable, just add Archivable or change the Codable into CodableArchivable since CodableArchivable is typealias of Codable & Archivable, your object will have those methods automatically. You just need to add primaryKey property you want as primary key as long as the value is String:

struct User: CodableArchivable {
    var primaryKey: String { userName }
    var userName: String
    var fullName: String
    var age: Int
}

JSONArchivable

If your object are JSONAble, just add Archivable or change the JSONAble into JSONArchivable since JSONArchivable is typealias of JSONAble & Archivable, your object will have those methods automatically. You just need to add @PKMapping attributes to property you want as primary key as long as the value is JSONParseable. The key will be string version of the property:

class User: JSONArchivable {
    @PKMapping var userName: String = ""
    @AutoMapping var fullName: String = ""
    @AutoMapping var age: Int = 0
}

The JSONArchivable have the one advantage among the others since it will automatically update the property which implement JSONArchivable if it updated independently:

// user is JSONArchivable
userCache.insert(user)

// shop is JSONArchivable
user.shop.imageUrl = updatedUrl
shopCache.insert(user.shop)

let userInCache = userCache.get(objectWithKey: user.primaryKey)
// will print true since the cache will be updated
print(user.shop.imageUrl == userInCache.shop.imageUrl)

CacheManager

To get CacheManager, you can use NativefierFactory. You can assign the maximum size in byte for memory size and disk size. But keep in mind, the size will only applicable on the first creation of the CacheManager, If the cache manager already created, then the memory size and disk size is same like the first one used when create the CacheManager. If you don't assign the memory size or disk size, it will use the default value which are 1 mega byte for memory and 2 mega byte disk size:

let cache = try! NativefierFactory.shared.cache(
    for: User.self, 
    initialMaxMemorySize: 10.kiloByte, 
    initialMaxDiskSize: 20.kiloByte
)

// or not explicit
let cache: CacheManager<User> = try! NativefierFactory.shared.cache( 
    initialMaxMemorySize: 10.kiloByte, 
    initialMaxDiskSize: 20.kiloByte
)

the cache manager have some usable methods and property which are:

  • var maxSize: Int { get } to get maximum size of the cache
  • var currentSize: Int { get } to get current used size of the cache
  • func latestAccessedTime(for key: String) -> Date? to get the latest time the object with same key accessed
  • func removeAllInvalidateObject(invalidateTimeInterval: TimeInterval) to remove all object older than time interval
  • func insert(_ object: Archive) to insert object
  • func update(_ object: Archive) to update existing object, or insert if have none
  • func get(objectWithKey key: String) -> Archive? to get object with given key
  • func getAll(limitedBy limit: Int) -> [Archive] to get all object limited by limit
  • func getAll() -> [Archive] to get all object stored in cache
  • func process(queries: [QueryCompatible]) -> [Archive] to process query. This will be disucessed later
  • func delete(objectWithKey key: String) to delete object with given key
  • func clear() to remove all object from cache

Query

You can do a query from cache. there are 3 types of query which are:

  • QueryFinder to find the object/results by its properties
  • QuerySorter to sort the results by its properties
  • QueryLimiter to limit the results by limit

All Query can be combined and act like AND condition:

let results = userCache.findWhere
    .userName(contains("premium"))
    .fullName(isNotNil())
    .getResults()

The code above will find all user in cache which its userName contains "premium" and its fullName is not nill. The results is array of User

let results = userCache.allSortedBy
    .age(.ascending)
    .fullName(.descending)
    .getResults()

The code above will get all user in cache and sorted it by its age ascendingly and then its fullName descendingly. The results is sorted array of User

You can add the limit too

let results = userCache.allSortedBy
    .age(.ascending)
    .fullName(.descending)
    .limit(by: 10)
    .getResults()

The code above will limit the results maximum just 10

You can even combine the query if you want:

let results = userCache.findWhere
    .userName(contains("premium"))
    .fullName(isNotNil())
    .sortedBy
    .age(.ascending)
    .fullName(.descending)
    .limit(by: 10)
    .getResults()

The code above will find all user in cache which its userName contains "premium" and its fullName is not nill, then sort it by its age ascendingly and then its fullName descendingly. The results are limited by 10.

here are the list of finder that can be used with QueryFinder:

  • isNil() match if property nil
  • isNotNil() match if property did not nil
  • contains(with: ) match if string property contains given string
  • matches(withRegex: ) match if string property matches with given regex
  • contains(_: ) match if collection property is contains given element
  • contains(atLeastOne: ) match if collection property contains at least one of given element
  • contains(all: ) match if collection property contains all given element
  • countEqual(with: ) match if collection property count equal with given number
  • countGreater(than: ) match if collection property count greater than given number
  • countLess(than: ) match if collection property count less than given number
  • countGreaterThanOrEqual(with: ) match if collection property count greater than or equal with given number
  • countLessThanOrEqual(with: ) match if collection property count greater than or equal with given number
  • equal(with: ) match if property equal with given value
  • notEqual(with: ) match if property not equal with given value
  • greater(than: ) match if property greater than given value
  • less(than: ) match if property less than given value
  • greaterThanOrEqual(with: ) match if property greater than or equal with given value
  • lessThanOrEqual(with: ) match if property less than or equal with given value

if you want to validate manually, you can just use valid(with validator: (Property) -> Bool):

let results = userCache.findWhere
    .userName(valid(with: { $0.contains("premium") }))
    .getResults()

Contribute

You know how, just clone and do pull request