Last active
May 25, 2016 04:25
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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