Skip to content

Instantly share code, notes, and snippets.

@beccadax
Last active May 25, 2016 04:25
Show Gist options
  • Save beccadax/6b7c6837b6f8bd328656e7227fdea909 to your computer and use it in GitHub Desktop.
Save beccadax/6b7c6837b6f8bd328656e7227fdea909 to your computer and use it in GitHub Desktop.
A sketch of something rather like Core Data, written in pure Swift with property behaviors and a couple other likely future features.
// Here's what usage looks like:
class Person: ManagedObject {
// Note: Although the proposal shows `var [foo]`, current thinking is
// actually that we'll use `@foo var`.
@persistent var name: String
@persistent var birthDate: Date
@persistent var socialSecurityNumber: String?
@persistent var children: Set<Person>
@persistent var parents: Set<Person>
}
// And here's select parts of the Core Data-like library it uses:
/// Superclass of all managed objects. You may use this class directly,
/// or you may create a subclass for your particular managed object.
public class ManagedObject: PersistentValue {
private let properties: PersistentProperties
/// Accesses a persistent value inside the object.
///
/// Throws an error if you try to access a value that doesn't exist,
/// or if you try to access it with an incompatible type.
//
// Note: Subscripts don't actually support generics or throws
// yet; you could implement this as a pair of methods instead.
public subscript<T: PersistentValue>(persistentValue name: String) throws -> T {
get {
try properties.checkPermitsProperty(name: name, of: T.schemaType)
return Value(fromName: name, in: properties)
}
set {
try properties.checkPermitsProperty(name: name, of: T.schemaType)
newValue.write(toName: name, in: properties)
}
}
// There would also be objectID, context, and all the other
// usual things we expect from something like Core Data.
}
/// Marks a property of a ManagedObject subclass which accesses
/// a persistentValue.
public var behavior persistent<Value: PersistentValue>: Value where Self: ManagedObject {
// Captures the property name; this is not in the base property
// behaviors proposal, but it's a likely extension.
name: String
// We use try! here because the subscript should only throw
// "wrong type" and "property doesn't exist" errors, and
// those ought not to happen with a declared property.
get {
return try! self[persistentValue: name]
}
set {
try! self[persistentValue: name] = newValue
}
}
/// A value which can be stored in a ManagedObject's persistent values.
///
/// -Note: The conformances in the library itself use private APIs. You can
/// write your own conformances by conforming to CustomPersistentValue
/// and using one of the built-in conformances through it.
public protocol PersistentValue {
static var schemaType: PersistentStoreSchemaType { get }
init(fromName name: String, in properties: PersistentProperties)
func write(toName name: String, in properties: PersistentProperties)
}
extension String: PersistentValue {
static var schemaType = .string
init(fromName name: String, in properties: PersistentProperties) {
self = properties.string(forName: name)
}
func write(toName name: String, in properties: PersistentProperties) {
properties.setString(self, forName: name)
}
}
import struct Foundation.Date // Coming soon to a framework near you!
extension Date: PersistentValue {...}
extension Int: PersistentValue {...}
extension Double: PersistentValue {...}
extension Bool: PersistentValue {...}
// etc.
// Conditional conformances are a planned future Swift feature. Here, any
// Set<ManagedObject> (or any subclass) becomes a PersistentValue.
extension Set: PersistentValue where Element: ManagedObject {...}
// Similarly, any value can be made Optional (i.e. can be null). In a real
// version, we would use a more convoluted protocol hierarchy to disallow
// combinations like `Int??` and `Set<ManagedObject>` which probably wouldn't
// be supported by our backing store.
extension Optional: PersistentValue where Wrapped: PersistentValue {...}
/// A value which can be stored in a ManagedObject's persistent values
/// by converting it to a type natively supported by the library.
public protocol CustomPersistentValue: PersistentValue {
associatedtype BasePersistentValue: PersistentValue
init(baseValue: BasePersistentValue)
var baseValue: BasePersistentValue { get }
}
extension CustomPersistentValue {
public static var schemaType: PersistentStoreSchemaType {
return BasePersistentValue.schemaType
}
public init(fromName name: String, in properties: PersistentProperties) {
self.init(baseValue: BasePersistentValue(fromName: name, in: properties))
}
public func write(toName name: String, in properties: PersistentProperties) {
baseValue.write(toName: name, in: properties)
}
}
/// Represents types supported natively by the library.
public enum PersistentStoreSchemaType {
case string, date, int, double, bool, ...
case singularRelation, unorderedRelation, orderedRelation
indirect case nullable(PersistentStoreSchemaType)
}
/// Represents a collection of properties in a database row,
/// a query result, or any of a number of other things.
///
/// -Note: This class has no public APIs, but types conforming to
/// ManagedValue know how to read from and write to it.
public class PersistentProperties {
private func checkPermitsProperty(name: String, of schemaType: PersistentStoreSchemaType) throws {...}
private func string(forName name: String) {...}
private func setString(_ value: String, forName name: String) {...}
// etc.
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment