Data.swift is a Swift (2.0) framework for working with data models.
Data.swift is built on the fantastic YapDatabase which is built on Sqlite3
Swift 1.2 can be found on branch swift1.2
It aims to have the following attributes:
- Threadsafe
- Typesafe
- Tested
- Protocols not Inheritance
platform :ios, '8.0'
use_frameworks!
pod 'Data', git: 'https://github.com/DanBrooker/Data'
YapDatabase now has a framework, I just need to add it :D
Import Data module
import Data
Data.Model is simple protocol which requires a uid:String property and a means to archive and unarchive an object
class YourModel : Data.Model {
let uid: String // Required property
init() {
self.uid = "A UID OF YOUR CHOOSING"
}
// Required init
required init(archive: Archive) {
uid = archive["uid"] as! String
}
// Required archive property, [String: AnyObject]
var archive : Archive {
return ["uid": uid]
}
}
struct YourStruct : Data.Model {
let uid: String // Required property
init() {
self.uid = "A UID OF YOUR CHOOSING"
}
// Required init
init(archive: Archive) {
uid = archive["uid"] as! String
}
// Required archive property, [String: AnyObject]
var archive : Archive {
return ["uid": uid]
}
}
Created a data store
let store = YapStore()
let model = YourModel()
store.append(model)
let model: YourModel = store.find(modelsUID)
let models: [YourModel] = store.filter({ $0.someProperty > 0 })
let models: [YourModel] = store.all()
let count = store.count(YourModel.self)
Update is important because your object is not being observed for changes and therefore once you're done changing a object you will need to save it
store.update(model)
store.remove(model)
store.truncate(YourModel.self)
Now that you've got the basics
public struct Query<T : Model> {
let filter: ( (element: T) -> Bool )?
let window: Range<Int>?
let order: ( (a: T, b: T) -> Bool )?
}
A simple query for all YourModel objects
let query = Query<YourModel>()
A more complex query
Filter models based on enabled being true, limited to the first 20 elements and ordered by property
let query = Query<YourModel>(
filter: { $0.enabled == true },
window: 0..<20,
order: { $0.property > $1.property }
)
Options:
a) You can observe the following notifications for changes
- "dataStoreAdded"
Not actually called yet, cannot distinguish between modified and added currently
- "dataStoreModified"
- "dataStoreRemoved"
b) And this is the best option use Collection<T>
collections
Collection<T>
is a drop in replacement for Array with the added benefit of being persistent and automatically updating when someone changes an object in the underlying data store
let query = Query<YourModel>()
let data: Collection<YourModel> = Data(query: query, store: store)
See querying above for more information about populating Collection<T>
with a query
Collection<T>
has a delegate DataDelegate
and it generates some very useful callbacks for UITableView
s
extension ViewController: DataDelegate {
func beginUpdates() {
tableView.beginUpdates()
}
func endUpdates() {
tableView.endUpdates()
}
func objectAdded(indexPaths: [NSIndexPath]) {
tableView.insertRowsAtIndexPaths(indexPaths, withRowAnimation: .Automatic)
}
func objectRemoved(indexPaths: [NSIndexPath]) {
tableView.deleteRowsAtIndexPaths(indexPaths, withRowAnimation: .Automatic)
}
func objectUpdated(indexPaths: [NSIndexPath]) {
tableView.reloadRowsAtIndexPaths(indexPaths, withRowAnimation: .Automatic)
}
}
YapDatabase is actually a key-value store, or more correctly a hash of hashes.
You can therefore just store arbitrary key-values if you need to
// Set
store.setObjectForKey(object, forKey:"key")
// Get
let object : ObjectType = store.objectForKey("key")
Basic indexes are working but the API is not solidified yet Aggregate index
filter
ing not yet supported
Setup Indexes, once per model
struct TestModel: Data.Model {
let text: String
//.. init and archive functions removed for this example
// function used to index properties
func indexes() -> [Index] {
return [
Index(key: "text", value: text)
]
}
}
let example = TestModel(uid: "doesn't matter")
store.index(example)
Indexable types String, Int Double, Float, Bool
var unique: TestModel? = store.find("enabled", value: true)
var enabled: [TestModel] = store.filter("enabled", value: true)
sqlite full text search. reference
You need to setup indexes to use search. Only searches TEXT columns
struct Tweet: Data.Model {
let text: String
let authorName: String
//.. init and archive functions removed for this example
// function used to index properties
func indexes() -> [Index] {
return [
Index(key: "text", value: text),
Index(key: "authorName", value: authorName)
]
}
}
let example = Tweet(uid: "doesn't matter", text: "also doesn't matter", authorName: "anon")
store.index(example)
Search using various methods
var results : [Tweet] = store.search(string: "yapdatabase") // Basic Keyword Search
results = store.search(phrase: "yapdatabase is good") // Exact Phrase
results = store.search(string: "authorName:draconisNZ") // Only Search Property
results = store.search(string: "^yap") // Starts with
results = store.search(string: "yap*" // Wildcard
results = store.search(string: "'yapdatabase OR yapdatabse'") // OR
results = store.search(string: "'tweet NOT storm'") // NOT
results = store.search(string: "'tweet NEAR/2 storm'") // Keywords are with '2' tokens of each other
- Secondary index aggregation i.e. OR and AND
- Search snippets and return results across models if required
- Relationships (Delete rules)
- Metadata helpers for things like caching table row height
- Tableview sections with Collection<>
- Data syncing - CloudKit, Dropbox ...
Data.swift is released under an MIT license. See LICENSE for more information.