From b4117eeb028facf83fee0828882d84fb9c1c354d Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Wed, 11 Oct 2017 07:50:24 +0900 Subject: [PATCH 1/5] updated documentation (fixes #198) --- README.md | 6 +- Sources/CoreStoreLogger.swift | 13 -- Sources/CoreStoreObject.swift | 7 +- Sources/CoreStoreSchema.swift | 12 +- Sources/DataStack.swift | 10 -- Sources/DefaultLogger.swift | 6 - Sources/Entity.swift | 4 +- Sources/ICloudStore.swift | 2 - Sources/NSManagedObject+Logging.swift | 208 -------------------------- Sources/PartialObject.swift | 67 +++++++++ Sources/SQLiteStore.swift | 2 - Sources/Transformable.swift | 16 +- Sources/Value.swift | 11 +- Sources/VersionLock.swift | 4 +- 14 files changed, 99 insertions(+), 269 deletions(-) diff --git a/README.md b/README.md index 7284f6e..38c4ab5 100644 --- a/README.md +++ b/README.md @@ -1675,7 +1675,7 @@ To use these syntax sugars, include *CoreStoreBridge.h* in your Objective-C sour Starting CoreStore 4.0, we can now create persisted objects without depending on *.xcdatamodeld* Core Data files. The new `CoreStoreObject` subclass replaces `NSManagedObject`, and specially-typed properties declared on these classes will be synthesized as Core Data attributes. ```swift class Animal: CoreStoreObject { - let species = Value.Required("species") + let species = Value.Required("species", initial: "") } class Dog: Animal { @@ -1684,14 +1684,14 @@ class Dog: Animal { } class Person: CoreStoreObject { - let name = Value.Required("name") + let name = Value.Required("name", initial: "") let pets = Relationship.ToManyUnordered("pets", inverse: { $0.master }) } ``` The property names to be saved to Core Data is specified as the `keyPath` argument. This lets us refactor our Swift code without affecting the underlying database. For example: ```swift class Person: CoreStoreObject { - private let _name = Value.Required("name") + private let _name = Value.Required("name", initial: "") // ... } ``` diff --git a/Sources/CoreStoreLogger.swift b/Sources/CoreStoreLogger.swift index 1f6832f..57e1714 100644 --- a/Sources/CoreStoreLogger.swift +++ b/Sources/CoreStoreLogger.swift @@ -47,12 +47,6 @@ public enum LogLevel { */ public protocol CoreStoreLogger { - /** - When `true`, all `NSManagedObject` attribute and relationship access will raise an assertion when executed on the wrong transaction/datastack queue. Defaults to `false` if not implemented. - */ - // TODO: test before release (rolled back) -// var enableObjectConcurrencyDebugging: Bool { get set } - /** Handles log messages sent by the `CoreStore` framework. @@ -100,13 +94,6 @@ public protocol CoreStoreLogger { extension CoreStoreLogger { - // TODO: test before release (rolled back) -// public var enableObjectConcurrencyDebugging: Bool { -// -// get { return false } -// set {} -// } - public func abort(_ message: String, fileName: StaticString, lineNumber: Int, functionName: StaticString) { Swift.fatalError(message, file: fileName, line: UInt(lineNumber)) diff --git a/Sources/CoreStoreObject.swift b/Sources/CoreStoreObject.swift index 2ec6c4c..ec2aa7b 100644 --- a/Sources/CoreStoreObject.swift +++ b/Sources/CoreStoreObject.swift @@ -33,13 +33,13 @@ import Foundation The `CoreStoreObject` is an abstract class for creating CoreStore-managed objects that are more type-safe and more convenient than `NSManagedObject` subclasses. The model entities for `CoreStoreObject` subclasses are inferred from the Swift declaration themselves; no .xcdatamodeld files are needed. To declare persisted attributes and relationships for the `CoreStoreObject` subclass, declare properties of type `Value.Required`, `Value.Optional` for values, or `Relationship.ToOne`, `Relationship.ToManyOrdered`, `Relationship.ToManyUnordered` for relationships. ``` class Animal: CoreStoreObject { - let species = Value.Required("species") + let species = Value.Required("species", initial: "") let nickname = Value.Optional("nickname") let master = Relationship.ToOne("master") } class Person: CoreStoreObject { - let name = Value.Required("name") + let name = Value.Required("name", initial: "") let pet = Relationship.ToOne("pet", inverse: { $0.master }) } ``` @@ -141,6 +141,9 @@ open /*abstract*/ class CoreStoreObject: DynamicObject, Hashable { public extension DynamicObject where Self: CoreStoreObject { + /** + Returns the `PartialObject` instance for the object, which acts as a fast, type-safe KVC interface for `CoreStoreObject`. + */ public func partialObject() -> PartialObject { CoreStore.assert( diff --git a/Sources/CoreStoreSchema.swift b/Sources/CoreStoreSchema.swift index c685d6b..82a0713 100644 --- a/Sources/CoreStoreSchema.swift +++ b/Sources/CoreStoreSchema.swift @@ -33,13 +33,13 @@ import Foundation The `CoreStoreSchema` describes models written for `CoreStoreObject` Swift class declarations for a particular model version. `CoreStoreObject` entities for a model version should be added to `CoreStoreSchema` instance. ``` class Animal: CoreStoreObject { - let species = Value.Required("species") + let species = Value.Required("species", initial: "") let nickname = Value.Optional("nickname") let master = Relationship.ToOne("master") } class Person: CoreStoreObject { - let name = Value.Required("name") + let name = Value.Required("name", initial: "") let pet = Relationship.ToOne("pet", inverse: { $0.master }) } @@ -66,13 +66,13 @@ public final class CoreStoreSchema: DynamicSchema { Initializes a `CoreStoreSchema`. Using this initializer only if the entities don't need to be assigned to particular "Configurations". To use multiple configurations (for example, to separate entities in different `StorageInterface`s), use the `init(modelVersion:entitiesByConfiguration:versionLock:)` initializer. ``` class Animal: CoreStoreObject { - let species = Value.Required("species") + let species = Value.Required("species", initial: "") let nickname = Value.Optional("nickname") let master = Relationship.ToOne("master") } class Person: CoreStoreObject { - let name = Value.Required("name") + let name = Value.Required("name", initial: "") let pet = Relationship.ToOne("pet", inverse: { $0.master }) } @@ -112,12 +112,12 @@ public final class CoreStoreSchema: DynamicSchema { Initializes a `CoreStoreSchema`. Using this initializer if multiple "Configurations" (for example, to separate entities in different `StorageInterface`s) are needed. To add an entity only to the default configuration, assign an empty set to its configurations list. Note that regardless of the set configurations, all entities will be added to the default configuration. ``` class Animal: CoreStoreObject { - let species = Value.Required("species") + let species = Value.Required("species", initial: "") let nickname = Value.Optional("nickname") } class Person: CoreStoreObject { - let name = Value.Required("name") + let name = Value.Required("name", initial: "") } CoreStore.defaultStack = DataStack( diff --git a/Sources/DataStack.swift b/Sources/DataStack.swift index 726ffb6..7187306 100644 --- a/Sources/DataStack.swift +++ b/Sources/DataStack.swift @@ -105,9 +105,6 @@ public final class DataStack: Equatable { */ public required init(schemaHistory: SchemaHistory) { - // TODO: test before release (rolled back) -// _ = DataStack.isGloballyInitialized - self.coordinator = NSPersistentStoreCoordinator(managedObjectModel: schemaHistory.rawModel) self.rootSavingContext = NSManagedObjectContext.rootSavingContextForCoordinator(self.coordinator) self.mainContext = NSManagedObjectContext.mainContextForRootContext(self.rootSavingContext) @@ -585,13 +582,6 @@ public final class DataStack: Equatable { // MARK: Private - // TODO: test before release (rolled back) -// private static let isGloballyInitialized: Bool = { -// -// NSManagedObject.cs_swizzleMethodsForLogging() -// return true -// }() - private var persistentStoresByFinalConfiguration = [String: NSPersistentStore]() private var finalConfigurationsByEntityIdentifier = [EntityIdentifier: Set]() diff --git a/Sources/DefaultLogger.swift b/Sources/DefaultLogger.swift index 380fdf5..fcfc486 100644 --- a/Sources/DefaultLogger.swift +++ b/Sources/DefaultLogger.swift @@ -33,12 +33,6 @@ import Foundation */ public final class DefaultLogger: CoreStoreLogger { - /** - When `true`, all `NSManagedObject` attribute and relationship access will raise an assertion when executed on the wrong transaction/datastack queue. Defaults to `false`. - */ - // TODO: test before release (rolled back) -// public var enableObjectConcurrencyDebugging: Bool = false - /** Creates a `DefaultLogger`. */ diff --git a/Sources/Entity.swift b/Sources/Entity.swift index bb6fc26..33da35f 100644 --- a/Sources/Entity.swift +++ b/Sources/Entity.swift @@ -34,13 +34,13 @@ import ObjectiveC The `Entity` contains `NSEntityDescription` metadata for `CoreStoreObject` subclasses. Pass the `Entity` instances to `CoreStoreSchema` initializer. ``` class Animal: CoreStoreObject { - let species = Value.Required("species") + let species = Value.Required("species", initial: "") let nickname = Value.Optional("nickname") let master = Relationship.ToOne("master") } class Person: CoreStoreObject { - let name = Value.Required("name") + let name = Value.Required("name", initial: "") let pet = Relationship.ToOne("pet", inverse: { $0.master }) } diff --git a/Sources/ICloudStore.swift b/Sources/ICloudStore.swift index ba96b6b..edf42f5 100644 --- a/Sources/ICloudStore.swift +++ b/Sources/ICloudStore.swift @@ -425,8 +425,6 @@ public final class ICloudStore: CloudStorage { */ public func cs_eraseStorageAndWait(soureModel: NSManagedObjectModel) throws { - // TODO: check if attached to persistent store - let cacheFileURL = self.cacheFileURL try autoreleasepool { diff --git a/Sources/NSManagedObject+Logging.swift b/Sources/NSManagedObject+Logging.swift index 2db850f..47847dd 100644 --- a/Sources/NSManagedObject+Logging.swift +++ b/Sources/NSManagedObject+Logging.swift @@ -66,212 +66,4 @@ internal extension NSManagedObject { } return nil } - - // TODO: test before release (rolled back) -// @nonobjc -// internal static func cs_swizzleMethodsForLogging() { -// -// struct Static { -// -// static let isSwizzled = Static.swizzle() -// -// private static func swizzle() -> Bool { -// -// NSManagedObject.cs_swizzle( -// original: #selector(NSManagedObject.willAccessValue(forKey:)), -// proxy: #selector(NSManagedObject.cs_willAccessValue(forKey:)) -// ) -// NSManagedObject.cs_swizzle( -// original: #selector(NSManagedObject.willChangeValue(forKey:)), -// proxy: #selector(NSManagedObject.cs_willChangeValue(forKey:)) -// ) -// NSManagedObject.cs_swizzle( -// original: #selector(NSManagedObject.willChangeValue(forKey:withSetMutation:using:)), -// proxy: #selector(NSManagedObject.cs_willChangeValue(forKey:withSetMutation:using:)) -// ) -// return true -// } -// } -// assert(Static.isSwizzled) -// } -// -// @nonobjc -// private static func cs_swizzle(original originalSelector: Selector, proxy swizzledSelector: Selector) { -// -// let originalMethod = class_getInstanceMethod(NSManagedObject.self, originalSelector) -// let swizzledMethod = class_getInstanceMethod(NSManagedObject.self, swizzledSelector) -// let didAddMethod = class_addMethod( -// NSManagedObject.self, -// originalSelector, -// method_getImplementation(swizzledMethod), -// method_getTypeEncoding(swizzledMethod) -// ) -// if didAddMethod { -// -// class_replaceMethod( -// NSManagedObject.self, -// swizzledSelector, -// method_getImplementation(originalMethod), -// method_getTypeEncoding(originalMethod) -// ) -// } -// else { -// -// method_exchangeImplementations(originalMethod, swizzledMethod) -// } -// } -// -// private dynamic func cs_willAccessValue(forKey key: String?) { -// -// self.cs_willAccessValue(forKey: key) -// -// guard CoreStore.logger.enableObjectConcurrencyDebugging else { -// -// return -// } -// -// guard let context = self.managedObjectContext else { -// -// CoreStore.log( -// .warning, -// message: "Attempted to access the \"\(key ?? "")\" key of an object of type \(cs_typeName(self)) after has been deleted from its \(cs_typeName(NSManagedObjectContext.self))." -// ) -// return -// } -// if context.isTransactionContext { -// -// guard let transaction = context.parentTransaction else { -// -// CoreStore.log( -// .warning, -// message: "Attempted to access the \"\(key ?? "")\" key of an object of type \(cs_typeName(self)) after has been deleted from its transaction." -// ) -// return -// } -// CoreStore.assert( -// transaction.isRunningInAllowedQueue(), -// "Attempted to access the \"\(key ?? "")\" key of an object of type \(cs_typeName(self)) outside its transaction's designated queue." -// ) -// return -// } -// if context.isDataStackContext { -// -// guard context.parentStack != nil else { -// -// CoreStore.log( -// .warning, -// message: "Attempted to access the \"\(key ?? "")\" key of an object of type \(cs_typeName(self)) after has been deleted from its \(cs_typeName(DataStack.self)).") -// return -// } -// CoreStore.assert( -// Thread.isMainThread, -// "Attempted to access the \"\(key ?? "")\" key of an object of type \(cs_typeName(self)) outside the main thread." -// ) -// return -// } -// } -// -// private dynamic func cs_willChangeValue(forKey key: String?) { -// -// self.cs_willChangeValue(forKey: key) -// -// guard CoreStore.logger.enableObjectConcurrencyDebugging else { -// -// return -// } -// -// guard let context = self.managedObjectContext else { -// -// CoreStore.log( -// .warning, -// message: "Attempted to change the \"\(key ?? "")\" of an object of type \(cs_typeName(self)) after has been deleted from its \(cs_typeName(NSManagedObjectContext.self))." -// ) -// return -// } -// if context.isTransactionContext { -// -// guard let transaction = context.parentTransaction else { -// -// CoreStore.log( -// .warning, -// message: "Attempted to change the \"\(key ?? "")\" of an object of type \(cs_typeName(self)) after has been deleted from its transaction." -// ) -// return -// } -// CoreStore.assert( -// transaction.isRunningInAllowedQueue(), -// "Attempted to change the \"\(key ?? "")\" of an object of type \(cs_typeName(self)) outside its transaction's designated queue." -// ) -// return -// } -// if context.isDataStackContext { -// -// guard context.parentStack != nil else { -// -// CoreStore.log( -// .warning, -// message: "Attempted to change the \"\(key ?? "")\" of an object of type \(cs_typeName(self)) after has been deleted from its \(cs_typeName(DataStack.self)).") -// return -// } -// CoreStore.assert( -// Thread.isMainThread, -// "Attempted to change the \"\(key ?? "")\" of an object of type \(cs_typeName(self)) outside the main thread." -// ) -// return -// } -// } -// -// private dynamic func cs_willChangeValue(forKey inKey: String, withSetMutation inMutationKind: NSKeyValueSetMutationKind, using inObjects: Set) { -// -// self.cs_willChangeValue( -// forKey: inKey, -// withSetMutation: inMutationKind, -// using: inObjects -// ) -// -// guard CoreStore.logger.enableObjectConcurrencyDebugging else { -// -// return -// } -// -// guard let context = self.managedObjectContext else { -// -// CoreStore.log( -// .warning, -// message: "Attempted to mutate the \"\(inKey)\" of an object of type \(cs_typeName(self)) after has been deleted from its \(cs_typeName(NSManagedObjectContext.self))." -// ) -// return -// } -// if context.isTransactionContext { -// -// guard let transaction = context.parentTransaction else { -// -// CoreStore.log( -// .warning, -// message: "Attempted to mutate the \"\(inKey)\" of an object of type \(cs_typeName(self)) after has been deleted from its transaction." -// ) -// return -// } -// CoreStore.assert( -// transaction.isRunningInAllowedQueue(), -// "Attempted to mutate the \"\(inKey)\" of an object of type \(cs_typeName(self)) outside its transaction's designated queue." -// ) -// return -// } -// if context.isDataStackContext { -// -// guard context.parentStack != nil else { -// -// CoreStore.log( -// .warning, -// message: "Attempted to mutate the \"\(inKey)\" of an object of type \(cs_typeName(self)) after has been deleted from its \(cs_typeName(DataStack.self)).") -// return -// } -// CoreStore.assert( -// Thread.isMainThread, -// "Attempted to mutate the \"\(inKey)\" of an object of type \(cs_typeName(self)) outside the main thread." -// ) -// return -// } -// } } diff --git a/Sources/PartialObject.swift b/Sources/PartialObject.swift index 54c1742..eaabcca 100644 --- a/Sources/PartialObject.swift +++ b/Sources/PartialObject.swift @@ -34,6 +34,9 @@ import Foundation */ public struct PartialObject { + /** + Returns a the actual `CoreStoreObject` instance for the receiver. + */ public func completeObject() -> O { return O.cs_fromRaw(object: self.rawObject) @@ -42,6 +45,9 @@ public struct PartialObject { // MARK: Value.Required accessors/mutators + /** + Returns the value for the property identified by a given key. + */ public func value(for property: (O) -> ValueContainer.Required) -> V { return V.cs_fromQueryableNativeType( @@ -49,6 +55,9 @@ public struct PartialObject { )! } + /** + Sets the property of the receiver specified by a given key to a given value. + */ public func setValue(_ value: V, for property: (O) -> ValueContainer.Required) { self.rawObject.setValue( @@ -57,6 +66,11 @@ public struct PartialObject { ) } + /** + Returns the value for the specified property from the managed object’s private internal storage. + + This method does not invoke the access notification methods (`willAccessValue(forKey:)` and `didAccessValue(forKey:)`). This method is used primarily by subclasses that implement custom accessor methods that need direct access to the receiver’s private storage. + */ public func primitiveValue(for property: (O) -> ValueContainer.Required) -> V { return V.cs_fromQueryableNativeType( @@ -64,6 +78,11 @@ public struct PartialObject { )! } + /** + Sets in the object's private internal storage the value of a given property. + + Sets in the receiver’s private internal storage the value of the property specified by key to value. + */ public func setPrimitiveValue(_ value: V, for property: (O) -> ValueContainer.Required) { self.rawObject.setPrimitiveValue( @@ -75,12 +94,18 @@ public struct PartialObject { // MARK: Value.Optional utilities + /** + Returns the value for the property identified by a given key. + */ public func value(for property: (O) -> ValueContainer.Optional) -> V? { return (self.rawObject.value(forKey: property(O.meta).keyPath) as! V.QueryableNativeType?) .flatMap(V.cs_fromQueryableNativeType) } + /** + Sets the property of the receiver specified by a given key to a given value. + */ public func setValue(_ value: V?, for property: (O) -> ValueContainer.Optional) { self.rawObject.setValue( @@ -89,12 +114,22 @@ public struct PartialObject { ) } + /** + Returns the value for the specified property from the managed object’s private internal storage. + + This method does not invoke the access notification methods (`willAccessValue(forKey:)` and `didAccessValue(forKey:)`). This method is used primarily by subclasses that implement custom accessor methods that need direct access to the receiver’s private storage. + */ public func primitiveValue(for property: (O) -> ValueContainer.Optional) -> V? { return (self.rawObject.primitiveValue(forKey: property(O.meta).keyPath) as! V.QueryableNativeType?) .flatMap(V.cs_fromQueryableNativeType) } + /** + Sets in the object's private internal storage the value of a given property. + + Sets in the receiver’s private internal storage the value of the property specified by key to value. + */ public func setPrimitiveValue(_ value: V?, for property: (O) -> ValueContainer.Optional) { self.rawObject.setPrimitiveValue( @@ -106,11 +141,17 @@ public struct PartialObject { // MARK: Transformable.Required utilities + /** + Returns the value for the property identified by a given key. + */ public func value(for property: (O) -> TransformableContainer.Required) -> V { return self.rawObject.value(forKey: property(O.meta).keyPath)! as! V } + /** + Sets the property of the receiver specified by a given key to a given value. + */ public func setValue(_ value: V, for property: (O) -> TransformableContainer.Required) { self.rawObject.setValue( @@ -119,11 +160,21 @@ public struct PartialObject { ) } + /** + Returns the value for the specified property from the managed object’s private internal storage. + + This method does not invoke the access notification methods (`willAccessValue(forKey:)` and `didAccessValue(forKey:)`). This method is used primarily by subclasses that implement custom accessor methods that need direct access to the receiver’s private storage. + */ public func primitiveValue(for property: (O) -> TransformableContainer.Required) -> V { return self.rawObject.primitiveValue(forKey: property(O.meta).keyPath)! as! V } + /** + Sets in the object's private internal storage the value of a given property. + + Sets in the receiver’s private internal storage the value of the property specified by key to value. + */ public func setPrimitiveValue(_ value: V, for property: (O) -> TransformableContainer.Required) { self.rawObject.setPrimitiveValue( @@ -135,11 +186,17 @@ public struct PartialObject { // MARK: Transformable.Optional utilities + /** + Returns the value for the property identified by a given key. + */ public func value(for property: (O) -> TransformableContainer.Optional) -> V? { return self.rawObject.value(forKey: property(O.meta).keyPath) as! V? } + /** + Sets the property of the receiver specified by a given key to a given value. + */ public func setValue(_ value: V?, for property: (O) -> TransformableContainer.Optional) { self.rawObject.setValue( @@ -148,11 +205,21 @@ public struct PartialObject { ) } + /** + Returns the value for the specified property from the managed object’s private internal storage. + + This method does not invoke the access notification methods (`willAccessValue(forKey:)` and `didAccessValue(forKey:)`). This method is used primarily by subclasses that implement custom accessor methods that need direct access to the receiver’s private storage. + */ public func primitiveValue(for property: (O) -> TransformableContainer.Optional) -> V? { return self.rawObject.primitiveValue(forKey: property(O.meta).keyPath) as! V? } + /** + Sets in the object's private internal storage the value of a given property. + + Sets in the receiver’s private internal storage the value of the property specified by key to value. + */ public func setPrimitiveValue(_ value: V?, for property: (O) -> TransformableContainer.Optional) { self.rawObject.setPrimitiveValue( diff --git a/Sources/SQLiteStore.swift b/Sources/SQLiteStore.swift index b50c29f..be95dfb 100644 --- a/Sources/SQLiteStore.swift +++ b/Sources/SQLiteStore.swift @@ -226,8 +226,6 @@ public final class SQLiteStore: LocalStorage { */ public func cs_eraseStorageAndWait(metadata: [String: Any], soureModelHint: NSManagedObjectModel?) throws { - // TODO: check if attached to persistent store - func deleteFiles(storeURL: URL, extraFiles: [String] = []) throws { let fileManager = FileManager.default diff --git a/Sources/Transformable.swift b/Sources/Transformable.swift index 167cc78..a771331 100644 --- a/Sources/Transformable.swift +++ b/Sources/Transformable.swift @@ -35,9 +35,9 @@ public extension DynamicObject where Self: CoreStoreObject { The containing type for transformable properties. `Transformable` properties support types that conforms to `NSCoding & NSCopying`. ``` class Animal: CoreStoreObject { - let species = Value.Required("species") - let nickname = Value.Optional("nickname") - let color = Transformable.Optional("color") + let species = Value.Required("species", initial: "") + let nickname = Value.Optional("nickname") + let color = Transformable.Optional("color") } ``` - Important: `Transformable` properties are required to be stored properties. Computed properties will be ignored, including `lazy` and `weak` properties. @@ -52,7 +52,7 @@ public extension DynamicObject where Self: CoreStoreObject { The containing type for transformable properties. Use the `DynamicObject.Transformable` typealias instead for shorter syntax. ``` class Animal: CoreStoreObject { - let species = Value.Required("species") + let species = Value.Required("species", initial: "") let nickname = Value.Optional("nickname") let color = Transformable.Optional("color") } @@ -66,7 +66,7 @@ public enum TransformableContainer { The containing type for transformable properties. Any type that conforms to `NSCoding & NSCopying` are supported. ``` class Animal: CoreStoreObject { - let species = Value.Required("species") + let species = Value.Required("species", initial: "") let nickname = Value.Optional("nickname") let color = Transformable.Optional("color") } @@ -79,7 +79,7 @@ public enum TransformableContainer { Initializes the metadata for the property. ``` class Animal: CoreStoreObject { - let species = Value.Required("species") + let species = Value.Required("species", initial: "") let color = Transformable.Required( "color", initial: UIColor.clear, @@ -292,7 +292,7 @@ public enum TransformableContainer { The containing type for optional transformable properties. Any type that conforms to `NSCoding & NSCopying` are supported. ``` class Animal: CoreStoreObject { - let species = Value.Required("species") + let species = Value.Required("species", initial: "") let nickname = Value.Optional("nickname") let color = Transformable.Optional("color") } @@ -305,7 +305,7 @@ public enum TransformableContainer { Initializes the metadata for the property. ``` class Animal: CoreStoreObject { - let species = Value.Required("species") + let species = Value.Required("species", initial: "") let color = Transformable.Optional( "color", isTransient: true, diff --git a/Sources/Value.swift b/Sources/Value.swift index 223ef45..e3d725b 100644 --- a/Sources/Value.swift +++ b/Sources/Value.swift @@ -35,7 +35,7 @@ public extension DynamicObject where Self: CoreStoreObject { The containing type for value propertiess. `Value` properties support any type that conforms to `ImportableAttributeType`. ``` class Animal: CoreStoreObject { - let species = Value.Required("species") + let species = Value.Required("species", initial: "") let nickname = Value.Optional("nickname") let color = Transformable.Optional("color") } @@ -52,7 +52,7 @@ public extension DynamicObject where Self: CoreStoreObject { The containing type for value properties. Use the `DynamicObject.Value` typealias instead for shorter syntax. ``` class Animal: CoreStoreObject { - let species = Value.Required("species") + let species = Value.Required("species", initial: "") let nickname = Value.Optional("nickname") let color = Transformable.Optional("color") } @@ -66,7 +66,7 @@ public enum ValueContainer { The containing type for required value properties. Any type that conforms to `ImportableAttributeType` are supported. ``` class Animal: CoreStoreObject { - let species = Value.Required("species") + let species = Value.Required("species", initial: "") let nickname = Value.Optional("nickname") let color = Transformable.Optional("color") } @@ -80,9 +80,10 @@ public enum ValueContainer { ``` class Person: CoreStoreObject { let title = Value.Required("title", initial: "Mr.") - let name = Value.Required("name") + let name = Value.Required("name", initial: "") let displayName = Value.Required( "displayName", + initial: "", isTransient: true, customGetter: Person.getName(_:) ) @@ -289,7 +290,7 @@ public enum ValueContainer { The containing type for optional value properties. Any type that conforms to `ImportableAttributeType` are supported. ``` class Animal: CoreStoreObject { - let species = Value.Required("species") + let species = Value.Required("species", initial: "") let nickname = Value.Optional("nickname") let color = Transformable.Optional("color") } diff --git a/Sources/VersionLock.swift b/Sources/VersionLock.swift index 38fff5c..4316004 100644 --- a/Sources/VersionLock.swift +++ b/Sources/VersionLock.swift @@ -32,12 +32,12 @@ import Foundation The `VersionLock` contains the version hashes for entities. This is then passed to the `CoreStoreSchema`, which contains all entities for the store. An assertion will be raised if any `Entity` doesn't match the version hash. ``` class Animal: CoreStoreObject { - let species = Value.Required("species") + let species = Value.Required("species", initial: "") let nickname = Value.Optional("nickname") let master = Relationship.ToOne("master") } class Person: CoreStoreObject { - let name = Value.Required("name") + let name = Value.Required("name", initial: "") let pet = Relationship.ToOne("pet", inverse: { $0.master }) } From d2ddf2002f62a4919df8981f312b47b70c20ae81 Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Wed, 11 Oct 2017 22:40:27 +0900 Subject: [PATCH 2/5] version update --- .travis.yml | 35 +++++++++++++++++++---------------- CoreStore.podspec | 2 +- Sources/Info.plist | 2 +- 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5a30d91..aaa35e4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ language: objective-c -osx_image: xcode8.3 +osx_image: xcode9 sudo: false git: submodules: false @@ -10,22 +10,25 @@ env: - LC_CTYPE=en_US.UTF-8 - LANG=en_US.UTF-8 matrix: - - DESTINATION="OS=10.3,name=iPhone 7" SCHEME="CoreStore iOS" SDK=iphonesimulator10.3 RUN_TESTS="YES" POD_LINT="NO" - - DESTINATION="OS=10.1,name=iPhone 7" SCHEME="CoreStore iOS" SDK=iphonesimulator10.3 RUN_TESTS="YES" POD_LINT="NO" - - DESTINATION="OS=9.0,name=iPhone 6 Plus" SCHEME="CoreStore iOS" SDK=iphonesimulator10.3 RUN_TESTS="YES" POD_LINT="NO" - - DESTINATION="OS=8.4,name=iPhone 6" SCHEME="CoreStore iOS" SDK=iphonesimulator10.3 RUN_TESTS="YES" POD_LINT="NO" - - DESTINATION="OS=8.3,name=iPhone 5S" SCHEME="CoreStore iOS" SDK=iphonesimulator10.3 RUN_TESTS="YES" POD_LINT="NO" - - DESTINATION="OS=8.3,name=iPhone 5" SCHEME="CoreStore iOS" SDK=iphonesimulator10.3 RUN_TESTS="YES" POD_LINT="NO" - - DESTINATION="OS=8.3,name=iPhone 4S" SCHEME="CoreStore iOS" SDK=iphonesimulator10.3 RUN_TESTS="YES" POD_LINT="NO" - - DESTINATION="arch=x86_64" SCHEME="CoreStore OSX" SDK=macosx10.12 RUN_TESTS="YES" POD_LINT="NO" - - DESTINATION="OS=3.2,name=Apple Watch - 42mm" SCHEME="CoreStore watchOS" SDK=watchsimulator3.2 RUN_TESTS="NO" POD_LINT="NO" - - DESTINATION="OS=2.2,name=Apple Watch - 42mm" SCHEME="CoreStore watchOS" SDK=watchsimulator3.2 RUN_TESTS="NO" POD_LINT="NO" - - DESTINATION="OS=10.2,name=Apple TV 1080p" SCHEME="CoreStore tvOS" SDK=appletvsimulator10.2 RUN_TESTS="YES" POD_LINT="NO" - - DESTINATION="OS=9.2,name=Apple TV 1080p" SCHEME="CoreStore tvOS" SDK=appletvsimulator10.2 RUN_TESTS="YES" POD_LINT="NO" + - DESTINATION="OS=11.0,name=iPhone 8" SCHEME="CoreStore iOS" SDK=iphonesimulator11.0 RUN_TESTS="YES" POD_LINT="NO" + - DESTINATION="OS=10.3,name=iPhone 7" SCHEME="CoreStore iOS" SDK=iphonesimulator11.0 RUN_TESTS="YES" POD_LINT="NO" + - DESTINATION="OS=10.1,name=iPhone 7" SCHEME="CoreStore iOS" SDK=iphonesimulator11.0 RUN_TESTS="YES" POD_LINT="NO" + - DESTINATION="OS=9.0,name=iPhone 6 Plus" SCHEME="CoreStore iOS" SDK=iphonesimulator11.0 RUN_TESTS="YES" POD_LINT="NO" + - DESTINATION="OS=8.4,name=iPhone 6" SCHEME="CoreStore iOS" SDK=iphonesimulator11.0 RUN_TESTS="YES" POD_LINT="NO" + - DESTINATION="OS=8.3,name=iPhone 5S" SCHEME="CoreStore iOS" SDK=iphonesimulator11.0 RUN_TESTS="YES" POD_LINT="NO" + - DESTINATION="OS=8.3,name=iPhone 5" SCHEME="CoreStore iOS" SDK=iphonesimulator11.0 RUN_TESTS="YES" POD_LINT="NO" + - DESTINATION="OS=8.3,name=iPhone 4S" SCHEME="CoreStore iOS" SDK=iphonesimulator11.0 RUN_TESTS="YES" POD_LINT="NO" + - DESTINATION="arch=x86_64" SCHEME="CoreStore OSX" SDK=macosx10.13 RUN_TESTS="YES" POD_LINT="NO" + - DESTINATION="OS=4.0,name=Apple Watch - 42mm" SCHEME="CoreStore watchOS" SDK=watchsimulator4.0 RUN_TESTS="NO" POD_LINT="NO" + - DESTINATION="OS=3.2,name=Apple Watch - 42mm" SCHEME="CoreStore watchOS" SDK=watchsimulator4.0 RUN_TESTS="NO" POD_LINT="NO" + - DESTINATION="OS=2.2,name=Apple Watch - 42mm" SCHEME="CoreStore watchOS" SDK=watchsimulator4.0 RUN_TESTS="NO" POD_LINT="NO" + - DESTINATION="OS=11.0,name=Apple TV 1080p" SCHEME="CoreStore tvOS" SDK=appletvsimulator11.0 RUN_TESTS="YES" POD_LINT="NO" + - DESTINATION="OS=10.2,name=Apple TV 1080p" SCHEME="CoreStore tvOS" SDK=appletvsimulator11.0 RUN_TESTS="YES" POD_LINT="NO" + - DESTINATION="OS=9.2,name=Apple TV 1080p" SCHEME="CoreStore tvOS" SDK=appletvsimulator11.0 RUN_TESTS="YES" POD_LINT="NO" before_install: - gem install cocoapods --no-rdoc --no-ri --no-document --quiet - gem install xcpretty --no-rdoc --no-ri --no-document --quiet - - curl -OlL "https://github.com/Carthage/Carthage/releases/download/0.23.0/Carthage.pkg" + - curl -OlL "https://github.com/Carthage/Carthage/releases/download/0.26.0/Carthage.pkg" - sudo installer -pkg "Carthage.pkg" -target / - rm "Carthage.pkg" before_script: @@ -38,8 +41,8 @@ script: xcodebuild -workspace CoreStore.xcworkspace -scheme "$SCHEME" -sdk "$SDK" -destination "$DESTINATION" -configuration Debug ONLY_ACTIVE_ARCH=NO clean test | xcpretty -c; xcodebuild -workspace CoreStore.xcworkspace -scheme "$SCHEME" -sdk "$SDK" -destination "$DESTINATION" -configuration Release ONLY_ACTIVE_ARCH=NO clean test | xcpretty -c; fi - - xcodebuild -workspace "CoreStore.xcworkspace" -scheme "CoreStore iOS" -sdk "iphonesimulator10.3" -destination "OS=10.3,name=iPhone 7" -configuration Debug ONLY_ACTIVE_ARCH=NO clean test | xcpretty -c; - - xcodebuild -workspace "CoreStore.xcworkspace" -scheme "CoreStore iOS" -sdk "iphonesimulator10.3" -destination "OS=10.3,name=iPhone 7" -configuration Release ONLY_ACTIVE_ARCH=NO clean test | xcpretty -c; + - xcodebuild -workspace "CoreStore.xcworkspace" -scheme "CoreStore iOS" -sdk "iphonesimulator11.0" -destination "OS=11.0,name=iPhone 8" -configuration Debug ONLY_ACTIVE_ARCH=NO clean test | xcpretty -c; + - xcodebuild -workspace "CoreStore.xcworkspace" -scheme "CoreStore iOS" -sdk "iphonesimulator11.0" -destination "OS=11.0,name=iPhone 8" -configuration Release ONLY_ACTIVE_ARCH=NO clean test | xcpretty -c; - if [ $POD_LINT == "YES" ]; then pod lib lint --quick; fi diff --git a/CoreStore.podspec b/CoreStore.podspec index fef69a2..5c3660e 100644 --- a/CoreStore.podspec +++ b/CoreStore.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "CoreStore" - s.version = "4.1.3" + s.version = "4.2.0" s.license = "MIT" s.summary = "Unleashing the real power of Core Data with the elegance and safety of Swift" s.homepage = "https://github.com/JohnEstropia/CoreStore" diff --git a/Sources/Info.plist b/Sources/Info.plist index a19ea3e..b6ba904 100644 --- a/Sources/Info.plist +++ b/Sources/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 4.1.3 + 4.2.0 CFBundleSignature ???? CFBundleVersion From 50bc3ace069e18f98ad460521fad4b5e085c59ab Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Thu, 12 Oct 2017 00:02:39 +0900 Subject: [PATCH 3/5] travis --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index aaa35e4..af7dac8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,6 +31,8 @@ before_install: - curl -OlL "https://github.com/Carthage/Carthage/releases/download/0.26.0/Carthage.pkg" - sudo installer -pkg "Carthage.pkg" -target / - rm "Carthage.pkg" + - npm install ios-sim -g + - ios-sim start --devicetypeid "com.apple.CoreSimulator.SimDeviceType.iPhone-8, 11.0" before_script: - carthage update --use-submodules script: From f460a0b30f4e2cb6863863c53ac157a000439b85 Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Thu, 12 Oct 2017 01:10:08 +0900 Subject: [PATCH 4/5] CI: test demo app --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index af7dac8..174671a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -43,8 +43,8 @@ script: xcodebuild -workspace CoreStore.xcworkspace -scheme "$SCHEME" -sdk "$SDK" -destination "$DESTINATION" -configuration Debug ONLY_ACTIVE_ARCH=NO clean test | xcpretty -c; xcodebuild -workspace CoreStore.xcworkspace -scheme "$SCHEME" -sdk "$SDK" -destination "$DESTINATION" -configuration Release ONLY_ACTIVE_ARCH=NO clean test | xcpretty -c; fi - - xcodebuild -workspace "CoreStore.xcworkspace" -scheme "CoreStore iOS" -sdk "iphonesimulator11.0" -destination "OS=11.0,name=iPhone 8" -configuration Debug ONLY_ACTIVE_ARCH=NO clean test | xcpretty -c; - - xcodebuild -workspace "CoreStore.xcworkspace" -scheme "CoreStore iOS" -sdk "iphonesimulator11.0" -destination "OS=11.0,name=iPhone 8" -configuration Release ONLY_ACTIVE_ARCH=NO clean test | xcpretty -c; + - xcodebuild -workspace "CoreStore.xcworkspace" -scheme "CoreStoreDemo" -sdk "iphonesimulator11.0" -destination "OS=11.0,name=iPhone 8" -configuration Debug ONLY_ACTIVE_ARCH=NO build | xcpretty -c; + - xcodebuild -workspace "CoreStore.xcworkspace" -scheme "CoreStoreDemo" -sdk "iphonesimulator11.0" -destination "OS=11.0,name=iPhone 8" -configuration Release ONLY_ACTIVE_ARCH=NO build | xcpretty -c; - if [ $POD_LINT == "YES" ]; then pod lib lint --quick; fi From ab2eac8f6ccbadd56ee6db9c683b4129511f4acf Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Fri, 13 Oct 2017 07:58:37 +0900 Subject: [PATCH 5/5] Swift 3.2 README update --- README.md | 28 +++++----------------------- 1 file changed, 5 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 38c4ab5..8c91861 100644 --- a/README.md +++ b/README.md @@ -18,10 +18,10 @@ Unleashing the real power of Core Data with the elegance and safety of Swift

-* **Swift 3.1:** iOS 8+ / macOS 10.10+ / watchOS 2.0+ / tvOS 9.0+ -* Beta support: [Swift 3.2](https://github.com/JohnEstropia/CoreStore/tree/prototype/Swift_3_2), [Swift 4.0](https://github.com/JohnEstropia/CoreStore/tree/prototype/Swift_4_0) +* **Swift 3.2:** iOS 8+ / macOS 10.10+ / watchOS 2.0+ / tvOS 9.0+ +* Other Swift versions: [Swift 3.1(version 4.1.4)](https://github.com/JohnEstropia/CoreStore/tree/4.1.4), [Swift 4.0](https://github.com/JohnEstropia/CoreStore/tree/5.0.0) -Upgrading from CoreStore 3.x to 4.x? Check out the [new features](#features) and make sure to read the [Migration guide](#upgrading-from-3xx-to-4xx). +Upgrading from CoreStore 4.1 (Swift 3.1) to 4.2 (Swift 3.2)? Check out the [new features](#features) and make sure to read the [Change logs](https://github.com/JohnEstropia/CoreStore/releases). CoreStore is now part of the [Swift Source Compatibility projects](https://swift.org/source-compatibility/#current-list-of-projects). @@ -38,9 +38,9 @@ CoreStore was (and is) heavily shaped by real-world needs of developing data-dep - **Clean fetching and querying API.** Fetching objects is easy, but querying for raw aggregates (`min`, `max`, etc.) and raw property values is now just as convenient. *(See [Fetching and querying](#fetching-and-querying))* - **Type-safe, easy to configure observers.** You don't have to deal with the burden of setting up `NSFetchedResultsController`s and KVO. As an added bonus, `ListMonitor`s and `ObjectMonitor`s can have multiple observers. This means you can have multiple view controllers efficiently share a single resource! *(See [Observing changes and notifications](#observing-changes-and-notifications))* - **Efficient importing utilities.** Map your entities once with their corresponding import source (JSON for example), and importing from *transactions* becomes elegant. Uniquing is also done with an efficient find-and-replace algorithm. *(See [Importing data](#importing-data))* -- ⭐️ **New in 4.0: Say goodbye to *.xcdatamodeld* files!** The new `CoreStoreObject` is *the* replacement to `NSManagedObject`. `CoreStoreObject` subclasses can declare type-safe properties all in Swift code, no need to maintain separate resource files for the models. As bonus, these special properties support custom types, and can be used to create type-safe keypaths and queries. *(See [Type-safe `CoreStoreObject`s](#type-safe-corestoreobjects))* +- **Say goodbye to *.xcdatamodeld* files!** The new `CoreStoreObject` is *the* replacement to `NSManagedObject`. `CoreStoreObject` subclasses can declare type-safe properties all in Swift code, no need to maintain separate resource files for the models. As bonus, these special properties support custom types, and can be used to create type-safe keypaths and queries. *(See [Type-safe `CoreStoreObject`s](#type-safe-corestoreobjects))* - **Progressive migrations.** No need to think how to migrate from all previous model versions to your latest model. Just tell the `DataStack` the sequence of version strings (`MigrationChain`s) and CoreStore will automatically use progressive migrations when needed. *(See [Migrations](#migrations))* -- ⭐️ **New in 4.0: Easier custom migrations.** Say goodbye to *.xcmappingmodel* files; CoreStore can now infer entity mappings when possible, while still allowing an easy way to write custom mappings. *(See [Migrations](#migrations))* +- **Easier custom migrations.** Say goodbye to *.xcmappingmodel* files; CoreStore can now infer entity mappings when possible, while still allowing an easy way to write custom mappings. *(See [Migrations](#migrations))* - **Plug-in your own logging framework.** Although a default logger is built-in, all logging, asserting, and error reporting can be funneled to `CoreStoreLogger` protocol implementations. *(See [Logging and error reporting](#logging-and-error-reporting))* - **Heavy support for multiple persistent stores per data stack.** CoreStore lets you manage separate stores in a single `DataStack`, just the way *.xcdatamodeld* configurations are designed to. CoreStore will also manage one stack by default, but you can create and manage as many as you need. *(See [Setting up](#setting-up))* - **Free to name entities and their class names independently.** CoreStore gets around a restriction with other Core Data wrappers where the entity name should be the same as the `NSManagedObject` subclass name. CoreStore loads entity-to-class mappings from the managed object model file, so you can assign different names for the entities and their class names. @@ -95,8 +95,6 @@ CoreStore was (and is) heavily shaped by real-world needs of developing data-dep - [Roadmap](#roadmap) - [Installation](#installation) - [Changesets](#changesets) - - [Upgrading from 3.x.x to 4.x.x](#upgrading-from-3xx-to-4xx) - - [Other Releases](#other-releases) - [Contact](#contact) - [Who uses CoreStore?](#who-uses-corestore) - [License](#license) @@ -1832,22 +1830,6 @@ Add all *.swift* files to your project. To use the Objective-C syntax sugars, import *CoreStoreBridge.h* in your *.m* source files. # Changesets -### Upgrading from 3.x.x to 4.x.x -**Obsoleted** -- `LegacySQLiteStore` is now finally obsoleted in favor of `SQLiteStore`. For sqlite files that were created previously with `LegacySQLiteStore`, make sure to use the `SQLiteStore.legacy(...)` factory method to create an `SQLiteStore` that can load the file from the legacy file path. -- `SQLiteStore.init(...)`'s `mappingModelBundles` argument is now obsolete. The new initializer accepts a `migrationMappingProviders` optional argument where explicit mapping sources are declared. For reference on how to do this, read on [Custom migrations](#custom-migrations). - -**Deprecated** -- `DataStack.beginAsynchronous(...)`, `DataStack.beginSynchronous(...)`, `AsynchronousDataTransaction.commit(...)`, and `SynchronousDataTransaction.commit(...)` are now deprecated in favor of `DataStack.perform(asynchronous:...)` and `DataStack.perform(synchronous:...)` family of methods. These new `perform(...)` methods are auto-commit, meaning the transaction automatically calls `commit()` internally after the transction closure completes. To roll-back and cancel a transaction, call `try transaction.cancel()`. Read [Saving and processing transactions](#saving-and-processing-transactions) for more details. - -**Other Changes** -- `ListMonitor.refetch(...)` now works by recreating its internal `NSFetchedResultsController`. Previously `refetch(...)` would only apply new `FetchClause`s on top of previous fetches. Now all `FetchClauses` are required to be passed to `refetch(...)` each time it is called. -- New important concepts on "Dynamic Models", "Schema", and "Schema Histories". - - **Dynamic Models** (`DynamicObject` protocol): These are Core Data object types that any `NSManagedObject` or `CoreStoreObject`s conform to. *(See [Type-safe `CoreStoreObject`s](#type-safe-corestoreobjects))* - - **Version Schema** (`DynamicSchema` protocol): These types contain info for a single model version, as well as entities that belong to it. Currently supports `XcodeDataModelSchema` (*.xcdatamodeld* file), `CoreStoreSchema`, or `UnsafeDataModelSchema`. *(See [Migrations](#migrations))* - - **Schema History** (`SchemaHistory` class): This is now the preferred way to express all models to the `DataStack`. This class contains info to all the `DynamicSchema` across multiple model versions. *(See [Migrations](#migrations))* - -### Other Releases For the full Changelog, refer to the [Releases](https://github.com/JohnEstropia/CoreStore/releases) page.