disallow "empty" default values on some ImportableAttributeTypes

This commit is contained in:
John Rommel Estropia
2017-05-28 10:50:25 +09:00
parent ef0937fec4
commit da9e8c1550
3 changed files with 238 additions and 163 deletions

View File

@@ -32,7 +32,7 @@ import Foundation
public extension DynamicSchema {
/**
Prints the `DynamicSchema` as their corresponding `CoreStoreObject` Swift declarations. This is useful for converting current `XcodeDataModelSchema`-based models into the new `CoreStoreSchema` framework. Additional adjustments may need to be done to the generated source code for "Transformable" attributes.
Prints the `DynamicSchema` as their corresponding `CoreStoreObject` Swift declarations. This is useful for converting current `XcodeDataModelSchema`-based models into the new `CoreStoreSchema` framework. Additional adjustments may need to be done to the generated source code; for example: `Transformable` concrete types need to be provided, as well as `default` values.
- Important: After transitioning to the new `CoreStoreSchema` framework, it is recommended to add the new schema as a new version that the existing versions' `XcodeDataModelSchema` can migrate to. It is discouraged to load existing SQLite files created with `XcodeDataModelSchema` directly into a `CoreStoreSchema`.
- returns: a string that represents the source code for the `DynamicSchema` as their corresponding `CoreStoreObject` Swift declarations.
@@ -153,8 +153,7 @@ public extension DynamicSchema {
}
case .dateAttributeType:
valueType = Date.self
if let defaultValue = (attribute.defaultValue as! Date.ImportableNativeType?).flatMap(Date.cs_fromImportableNativeType),
defaultValue != Date.cs_emptyValue() {
if let defaultValue = (attribute.defaultValue as! Date.ImportableNativeType?).flatMap(Date.cs_fromImportableNativeType) {
defaultString = ", default: Date(timeIntervalSinceReferenceDate: \(defaultValue.timeIntervalSinceReferenceDate))"
}

View File

@@ -64,12 +64,6 @@ public protocol ImportableAttributeType: QueryableAttributeType {
*/
associatedtype ImportableNativeType: QueryableNativeType
/**
Returns the default "empty" value for this type.
*/
@inline(__always)
static func cs_emptyValue() -> Self
/**
Creates an instance of this type from its `ImportableNativeType` value.
*/
@@ -84,18 +78,31 @@ public protocol ImportableAttributeType: QueryableAttributeType {
}
// MARK: - EmptyableAttributeType
/**
`ImportableAttributeType`s that have a natural "empty" value. Example: `0` for `Int`, `""` for `String`.
- Discussion: Not all `ImportableAttributeType`s can have empty values. `URL`s and `Date`s for example have no obvious empty values.
*/
public protocol EmptyableAttributeType: ImportableAttributeType {
/**
Returns the default "empty" value for this type.
*/
@inline(__always)
static func cs_emptyValue() -> Self
}
// MARK: - Bool
extension Bool: ImportableAttributeType {
extension Bool: ImportableAttributeType, EmptyableAttributeType {
// MARK: ImportableAttributeType
public typealias ImportableNativeType = NSNumber
@inline(__always)
public static func cs_emptyValue() -> Bool {
return false
}
@inline(__always)
public static func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Bool? {
@@ -107,21 +114,26 @@ extension Bool: ImportableAttributeType {
return self.cs_toQueryableNativeType()
}
// MARK: EmptyableAttributeType
@inline(__always)
public static func cs_emptyValue() -> Bool {
return false
}
}
// MARK: - CGFloat
extension CGFloat: ImportableAttributeType {
extension CGFloat: ImportableAttributeType, EmptyableAttributeType {
// MARK: ImportableAttributeType
public typealias ImportableNativeType = NSNumber
@inline(__always)
public static func cs_emptyValue() -> CGFloat {
return 0
}
@inline(__always)
public static func cs_fromImportableNativeType(_ value: ImportableNativeType) -> CGFloat? {
@@ -133,21 +145,26 @@ extension CGFloat: ImportableAttributeType {
return self.cs_toQueryableNativeType()
}
// MARK: EmptyableAttributeType
@inline(__always)
public static func cs_emptyValue() -> CGFloat {
return 0
}
}
// MARK: - Data
extension Data: ImportableAttributeType {
extension Data: ImportableAttributeType, EmptyableAttributeType {
// MARK: ImportableAttributeType
public typealias ImportableNativeType = NSData
@inline(__always)
public static func cs_emptyValue() -> Data {
return Data()
}
@inline(__always)
public static func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Data? {
@@ -159,6 +176,15 @@ extension Data: ImportableAttributeType {
return self.cs_toQueryableNativeType()
}
// MARK: EmptyableAttributeType
@inline(__always)
public static func cs_emptyValue() -> Data {
return Data()
}
}
@@ -166,13 +192,9 @@ extension Data: ImportableAttributeType {
extension Date: ImportableAttributeType {
public typealias ImportableNativeType = NSDate
// MARK: ImportableAttributeType
@inline(__always)
public static func cs_emptyValue() -> Date {
return Date(timeIntervalSinceReferenceDate: 0)
}
public typealias ImportableNativeType = NSDate
@inline(__always)
public static func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Date? {
@@ -190,16 +212,12 @@ extension Date: ImportableAttributeType {
// MARK: - Double
extension Double: ImportableAttributeType {
extension Double: ImportableAttributeType, EmptyableAttributeType {
// MARK: ImportableAttributeType
public typealias ImportableNativeType = NSNumber
@inline(__always)
public static func cs_emptyValue() -> Double {
return 0
}
@inline(__always)
public static func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Double? {
@@ -211,21 +229,26 @@ extension Double: ImportableAttributeType {
return self.cs_toQueryableNativeType()
}
// MARK: EmptyableAttributeType
@inline(__always)
public static func cs_emptyValue() -> Double {
return 0
}
}
// MARK: - Float
extension Float: ImportableAttributeType {
extension Float: ImportableAttributeType, EmptyableAttributeType {
// MARK: ImportableAttributeType
public typealias ImportableNativeType = NSNumber
@inline(__always)
public static func cs_emptyValue() -> Float {
return 0
}
@inline(__always)
public static func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Float? {
@@ -237,21 +260,26 @@ extension Float: ImportableAttributeType {
return self.cs_toQueryableNativeType()
}
// MARK: EmptyableAttributeType
@inline(__always)
public static func cs_emptyValue() -> Float {
return 0
}
}
// MARK: - Int
extension Int: ImportableAttributeType {
extension Int: ImportableAttributeType, EmptyableAttributeType {
// MARK: ImportableAttributeType
public typealias ImportableNativeType = NSNumber
@inline(__always)
public static func cs_emptyValue() -> Int {
return 0
}
@inline(__always)
public static func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Int? {
@@ -263,21 +291,26 @@ extension Int: ImportableAttributeType {
return self.cs_toQueryableNativeType()
}
// MARK: EmptyableAttributeType
@inline(__always)
public static func cs_emptyValue() -> Int {
return 0
}
}
// MARK: - Int8
extension Int8: ImportableAttributeType {
extension Int8: ImportableAttributeType, EmptyableAttributeType {
// MARK: ImportableAttributeType
public typealias ImportableNativeType = NSNumber
@inline(__always)
public static func cs_emptyValue() -> Int8 {
return 0
}
@inline(__always)
public static func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Int8? {
@@ -289,21 +322,26 @@ extension Int8: ImportableAttributeType {
return self.cs_toQueryableNativeType()
}
// MARK: EmptyableAttributeType
@inline(__always)
public static func cs_emptyValue() -> Int8 {
return 0
}
}
// MARK: - Int16
extension Int16: ImportableAttributeType {
extension Int16: ImportableAttributeType, EmptyableAttributeType {
// MARK: ImportableAttributeType
public typealias ImportableNativeType = NSNumber
@inline(__always)
public static func cs_emptyValue() -> Int16 {
return 0
}
@inline(__always)
public static func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Int16? {
@@ -315,21 +353,26 @@ extension Int16: ImportableAttributeType {
return self.cs_toQueryableNativeType()
}
// MARK: EmptyableAttributeType
@inline(__always)
public static func cs_emptyValue() -> Int16 {
return 0
}
}
// MARK: - Int32
extension Int32: ImportableAttributeType {
extension Int32: ImportableAttributeType, EmptyableAttributeType {
// MARK: ImportableAttributeType
public typealias ImportableNativeType = NSNumber
@inline(__always)
public static func cs_emptyValue() -> Int32 {
return 0
}
@inline(__always)
public static func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Int32? {
@@ -341,21 +384,26 @@ extension Int32: ImportableAttributeType {
return self.cs_toQueryableNativeType()
}
// MARK: EmptyableAttributeType
@inline(__always)
public static func cs_emptyValue() -> Int32 {
return 0
}
}
// MARK: - Int64
extension Int64: ImportableAttributeType {
extension Int64: ImportableAttributeType, EmptyableAttributeType {
// MARK: ImportableAttributeType
public typealias ImportableNativeType = NSNumber
@inline(__always)
public static func cs_emptyValue() -> Int64 {
return 0
}
@inline(__always)
public static func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Int64? {
@@ -367,21 +415,26 @@ extension Int64: ImportableAttributeType {
return self.cs_toQueryableNativeType()
}
// MARK: EmptyableAttributeType
@inline(__always)
public static func cs_emptyValue() -> Int64 {
return 0
}
}
// MARK: - NSData
extension NSData: ImportableAttributeType {
extension NSData: ImportableAttributeType, EmptyableAttributeType {
// MARK: ImportableAttributeType
public typealias ImportableNativeType = NSData
@nonobjc @inline(__always)
public class func cs_emptyValue() -> Self {
return self.init()
}
@nonobjc @inline(__always)
public class func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Self? {
@@ -393,6 +446,15 @@ extension NSData: ImportableAttributeType {
return self.cs_toQueryableNativeType()
}
// MARK: EmptyableAttributeType
@nonobjc @inline(__always)
public class func cs_emptyValue() -> Self {
return self.init()
}
}
@@ -400,13 +462,9 @@ extension NSData: ImportableAttributeType {
extension NSDate: ImportableAttributeType {
public typealias ImportableNativeType = NSDate
// MARK: ImportableAttributeType
@nonobjc @inline(__always)
public class func cs_emptyValue() -> Self {
return self.init(timeIntervalSinceReferenceDate: 0)
}
public typealias ImportableNativeType = NSDate
@nonobjc @inline(__always)
public class func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Self? {
@@ -424,16 +482,12 @@ extension NSDate: ImportableAttributeType {
// MARK: - NSNumber
extension NSNumber: ImportableAttributeType {
extension NSNumber: ImportableAttributeType, EmptyableAttributeType {
// MARK: ImportableAttributeType
public typealias ImportableNativeType = NSNumber
@nonobjc @inline(__always)
public class func cs_emptyValue() -> Self {
return self.init()
}
@nonobjc @inline(__always)
public class func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Self? {
@@ -445,21 +499,26 @@ extension NSNumber: ImportableAttributeType {
return self.cs_toQueryableNativeType()
}
// MARK: EmptyableAttributeType
@nonobjc @inline(__always)
public class func cs_emptyValue() -> Self {
return self.init()
}
}
// MARK: - NSString
extension NSString: ImportableAttributeType {
extension NSString: ImportableAttributeType, EmptyableAttributeType {
// MARK: ImportableAttributeType
public typealias ImportableNativeType = NSString
@nonobjc @inline(__always)
public class func cs_emptyValue() -> Self {
return self.init()
}
@nonobjc @inline(__always)
public class func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Self? {
@@ -471,6 +530,15 @@ extension NSString: ImportableAttributeType {
return self.cs_toQueryableNativeType()
}
// MARK: EmptyableAttributeType
@nonobjc @inline(__always)
public class func cs_emptyValue() -> Self {
return self.init()
}
}
@@ -478,13 +546,9 @@ extension NSString: ImportableAttributeType {
extension NSURL: ImportableAttributeType {
public typealias ImportableNativeType = NSString
// MARK: ImportableAttributeType
@nonobjc @inline(__always)
public class func cs_emptyValue() -> Self {
return self.init(string: "")!
}
public typealias ImportableNativeType = NSString
@nonobjc @inline(__always)
public class func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Self? {
@@ -504,16 +568,9 @@ extension NSURL: ImportableAttributeType {
extension NSUUID: ImportableAttributeType {
public typealias ImportableNativeType = NSString
// MARK: ImportableAttributeType
public class func cs_emptyValue() -> Self {
enum Static {
static var zero = Array<UInt8>(repeating: 0, count: 16)
}
return self.init(uuidBytes: &Static.zero)
}
public typealias ImportableNativeType = NSString
@nonobjc @inline(__always)
public class func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Self? {
@@ -531,16 +588,12 @@ extension NSUUID: ImportableAttributeType {
// MARK: - String
extension String: ImportableAttributeType {
extension String: ImportableAttributeType, EmptyableAttributeType {
// MARK: ImportableAttributeType
public typealias ImportableNativeType = NSString
@inline(__always)
public static func cs_emptyValue() -> String {
return ""
}
@inline(__always)
public static func cs_fromImportableNativeType(_ value: ImportableNativeType) -> String? {
@@ -552,6 +605,15 @@ extension String: ImportableAttributeType {
return self.cs_toQueryableNativeType()
}
// MARK: EmptyableAttributeType
@inline(__always)
public static func cs_emptyValue() -> String {
return ""
}
}
@@ -559,16 +621,9 @@ extension String: ImportableAttributeType {
extension URL: ImportableAttributeType {
public typealias ImportableNativeType = NSString
// MARK: ImportableAttributeType
public static func cs_emptyValue() -> URL {
enum Static {
static let empty = URL(string: "")!
}
return Static.empty
}
public typealias ImportableNativeType = NSString
@inline(__always)
public static func cs_fromImportableNativeType(_ value: ImportableNativeType) -> URL? {
@@ -588,20 +643,9 @@ extension URL: ImportableAttributeType {
extension UUID: ImportableAttributeType {
public typealias ImportableNativeType = NSString
// MARK: ImportableAttributeType
public static func cs_emptyValue() -> UUID {
enum Static {
static let empty: UUID = cs_lazy {
var zero = Array<UInt8>(repeating: 0, count: 16)
return NSUUID(uuidBytes: &zero) as UUID
}
}
return Static.empty
}
public typealias ImportableNativeType = NSString
@inline(__always)
public static func cs_fromImportableNativeType(_ value: ImportableNativeType) -> UUID? {
@@ -623,11 +667,6 @@ extension RawRepresentable where RawValue: ImportableAttributeType {
public typealias ImportableNativeType = RawValue.ImportableNativeType
public static func cs_emptyValue() -> Self {
return self.init(rawValue: RawValue.cs_emptyValue())!
}
@inline(__always)
public static func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Self? {

View File

@@ -102,7 +102,7 @@ public enum ValueContainer<O: CoreStoreObject> {
}
```
- parameter keyPath: the permanent attribute name for this property.
- parameter default: the initial value for the property when the object is first created. Defaults to the `ImportableAttributeType`'s empty value if not specified.
- parameter default: the initial value for the property when the object is first created. For types that implement `EmptyableAttributeType`s, this argument may be omitted and the type's "empty" value will be used instead (e.g. `false` for `Bool`, `0` for `Int`, `""` for `String`, etc.)
- parameter isIndexed: `true` if the property should be indexed for searching, otherwise `false`. Defaults to `false` if not specified.
- parameter isTransient: `true` if the property is transient, otherwise `false`. Defaults to `false` if not specified. The transient flag specifies whether or not a property's value is ignored when an object is saved to a persistent store. Transient properties are not saved to the persistent store, but are still managed for undo, redo, validation, and so on.
- parameter versionHashModifier: used to mark or denote a property as being a different "version" than another even if all of the values which affect persistence are equal. (Such a difference is important in cases where the properties are unchanged but the format or content of its data are changed.)
@@ -115,7 +115,7 @@ public enum ValueContainer<O: CoreStoreObject> {
- parameter finalNewValue: the transformed new value
- parameter originalNewValue: the original new value
*/
public init(_ keyPath: KeyPath, `default`: V = V.cs_emptyValue(), isIndexed: Bool = false, isTransient: Bool = false, versionHashModifier: String? = nil, renamingIdentifier: String? = nil, customGetter: @escaping (_ `self`: O, _ getValue: () -> V) -> V = { $1() }, customSetter: @escaping (_ `self`: O, _ setValue: (_ finalNewValue: V) -> Void, _ originalNewValue: V) -> Void = { $1($2) }) {
public init(_ keyPath: KeyPath, `default`: V, isIndexed: Bool = false, isTransient: Bool = false, versionHashModifier: String? = nil, renamingIdentifier: String? = nil, customGetter: @escaping (_ `self`: O, _ getValue: () -> V) -> V = { $1() }, customSetter: @escaping (_ `self`: O, _ setValue: (_ finalNewValue: V) -> Void, _ originalNewValue: V) -> Void = { $1($2) }) {
self.keyPath = keyPath
self.isIndexed = isIndexed
@@ -338,6 +338,43 @@ public enum ValueContainer<O: CoreStoreObject> {
}
}
public extension ValueContainer.Required where V: EmptyableAttributeType {
/**
Initializes the metadata for the property. This convenience initializer uses the `EmptyableAttributeType`'s "empty" value as the initial value for the property when the object is first created (e.g. `false` for `Bool`, `0` for `Int`, `""` for `String`, etc.)
```
class Person: CoreStoreObject {
let title = Value.Required<String>("title") // initial value defaults to empty string
}
```
- parameter keyPath: the permanent attribute name for this property.
- parameter isIndexed: `true` if the property should be indexed for searching, otherwise `false`. Defaults to `false` if not specified.
- parameter isTransient: `true` if the property is transient, otherwise `false`. Defaults to `false` if not specified. The transient flag specifies whether or not a property's value is ignored when an object is saved to a persistent store. Transient properties are not saved to the persistent store, but are still managed for undo, redo, validation, and so on.
- parameter versionHashModifier: used to mark or denote a property as being a different "version" than another even if all of the values which affect persistence are equal. (Such a difference is important in cases where the properties are unchanged but the format or content of its data are changed.)
- parameter renamingIdentifier: used to resolve naming conflicts between models. When creating an entity mapping between entities in two managed object models, a source entity property and a destination entity property that share the same identifier indicate that a property mapping should be configured to migrate from the source to the destination. If unset, the identifier will be the property's name.
- parameter customGetter: use this closure to make final transformations to the property's value before returning from the getter.
- parameter self: the `CoreStoreObject`
- parameter getValue: the original getter for the property
- parameter customSetter: use this closure to make final transformations to the new value before assigning to the property.
- parameter setValue: the original setter for the property
- parameter finalNewValue: the transformed new value
- parameter originalNewValue: the original new value
*/
public convenience init(_ keyPath: KeyPath, isIndexed: Bool = false, isTransient: Bool = false, versionHashModifier: String? = nil, renamingIdentifier: String? = nil, customGetter: @escaping (_ `self`: O, _ getValue: () -> V) -> V = { $1() }, customSetter: @escaping (_ `self`: O, _ setValue: (_ finalNewValue: V) -> Void, _ originalNewValue: V) -> Void = { $1($2) }) {
self.init(
keyPath,
default: V.cs_emptyValue(),
isIndexed: isIndexed,
isTransient: isTransient,
versionHashModifier: versionHashModifier,
renamingIdentifier: renamingIdentifier,
customGetter: customGetter,
customSetter: customSetter
)
}
}
// MARK: - TransformableContainer