CocoaPods trunk is moving to be read-only. Read more on the blog, there are 13 months to go.
| TestsTested | ✓ |
| LangLanguage | SwiftSwift |
| License | MIT |
| ReleasedLast Release | Mar 2017 |
| SwiftSwift Version | 3.0 |
| SPMSupports SPM | ✗ |
Maintained by Sam Williams.
This is a framework for working with model objects.
pod 'ModelKit'Or, include only some components:
Fields give you:
Basic usage:
class Person {
let age = Field<Int>()
}
person.age.value = 10For multivalued fields, there is ArrayField, which wraps a field object describing the single-valued type.
let tags = ArrayField(Field<String>(), name: "Tags")The inner field is responsible for validations, transformations, etc.. The ArrayField owns top-level attributes like name, key, etc. – but for convenience, it will copy them from the inner field at initialization.
The unary postfix operator * is provided to wrap a Field in an ArrayField. So you can also write the above declaration like this:
let tags = Field<String>(name: "Tags")*Simple closure validations:
let age = Field<Int>().require { $0 > 0 }Rules can be chained, too, implying an AND. Order is not important.
let age = Field<Int>().require { $0 > 0 }.require { $0 % 2 == 0 }By default, nil values will be considered valid. To change that for a given rule, pass allowNil: false to require.
To validate a field value, call field.validate(), which returns a ValidationState enum:
public enum ValidationState:Equatable {
case unknown
case invalid([String])
case valid
}The associated value of the .invalid case is a list of error messages (e.g., ["must be greater than 0", "is required"]).
Fields will automatically have the following timestamps:
updatedAt: the last time any value was setchangedAt: the last time a new value was set (compared using ==)This library includes the ValueObserver and ValueObservable protocols for generic, type-safe change observation. Fields implement both protocols.
A ValueObservable can have any number of registered ValueObserver objects. The --> operator is a shortcut for the addObserver method (<-- works the same, only with its arguments swapped). Observation events are triggered once when the observer is added, and after that whenever a field value is set.
An observer can be added if it implements the ValueObserver protocol, which has a valueChanged(observable, value: value) method.
field --> observerOr, a closure can be provided. In place of an observer object, an owner is used only to identify each closure.
field --> owner { value in
print(value)
}We can still register a closure even if no observer is given. This is effectively registering the closure with a null observer.
age --> { value in
print("Age was changed to \(value)")
}Since Field itself implements both ValueObservable and ValueObserver, the --> operator can be used to create a link between two field values.
sourceField --> destinationFieldThis will set the value of destinationField to that of sourceField immediately, and again whenever sourceField’s value changes.
The <--> operator is a shortcut for <-- followed by --> (and can only be used between two Fields).
field1 <--> field2Since <-- is called first, both fields will initially have the value of field2.
Unregistering observers is done with the removeObserver method, or the -/-> operator. All observers can be removed with removeAllObservers().
A Model object automatically converts to and from a dictionary representation of its Field properties.
person.dictionaryValue()
// --> ["name": "Bob", "tags": ["red", "blue", "green"]]
If your field’s value is a subclass of Model, you should use the ModelField subclass.
let companies = ModelField<Company>()*This library provides a Promise-based interface for abstract data stores, specified in the ModelStore protocol, as well as several concrete implementations.
func create<T: Model>(model:T) -> Promise<T>
func update<T: Model>(model:T) -> Promise<T>
func delete<T: Model>(model:T) -> Promise<T>
func lookup<T: Model>(modelClass:T.Type, identifier:String) -> Promise<T>
func list<T: Model>(modelClass:T.Type) -> Promise<[T]>This stores models in memory. It adds a lookupImmediately method for synchronous identifier-based lookups.
This is intended as a base class for your RESTful server interface. It includes a number of overridable hooks that you can customize for your particular needs, like:
defaultHeadershandleErrorconstructResponseA RESTRouter is responsible for generating paths for resource locations.
collectionPath(for: Person.self)
instancePath(for: person)Nested routes are generated automatically if your model conforms to HasOwnerField, which requires it to specify an owning field. If a person’s owner field is its company field, for example, you might get "/companies/10/employees/45" for its instance path.
It also contains a ValueTransformerContext var that can be used to customize serialization. For example:
context.keyCase - specify the casing style of keys (.snake, .upperCamel, .lowerCamel)context.explicitNull - decide whether keys for null values should be included