From a11915db121a558ec12365dbc766621b6aa456b8 Mon Sep 17 00:00:00 2001 From: John Estropia Date: Tue, 4 Jul 2017 12:21:46 +0900 Subject: [PATCH 1/6] minor cleanup --- Sources/Where.swift | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/Sources/Where.swift b/Sources/Where.swift index 06aff22..bee5b6e 100644 --- a/Sources/Where.swift +++ b/Sources/Where.swift @@ -50,13 +50,11 @@ public struct Where: FetchClause, QueryClause, DeleteClause, Hashable { */ public static func && (left: Where, right: Where?) -> Where { - if right != nil { - return left && right! - } - else { + if let right = right { - return left + return left && right } + return left } /** @@ -67,13 +65,11 @@ public struct Where: FetchClause, QueryClause, DeleteClause, Hashable { */ public static func && (left: Where?, right: Where) -> Where { - if left != nil { + if let left = left { + return left && right } - else { - - return right - } + return right } /** @@ -92,13 +88,11 @@ public struct Where: FetchClause, QueryClause, DeleteClause, Hashable { */ public static func || (left: Where, right: Where?) -> Where { - if right != nil { - return left || right! - } - else { + if let right = right { - return left + return left || right } + return left } /** @@ -109,13 +103,11 @@ public struct Where: FetchClause, QueryClause, DeleteClause, Hashable { */ public static func || (left: Where?, right: Where) -> Where { - if left != nil { + if let left = left { + return left || right } - else { - - return right - } + return right } /** From 28b43f33fa1f9e010d01cacfe47c745239cedeed Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Sat, 5 Aug 2017 00:03:17 +0900 Subject: [PATCH 2/6] Value.Required now requires an "initial:" parameter. For previous CoreStoreObject users, use the appropriate empty value for your existing properties (0 for numeric types, false for Bool, "" for String) --- CoreStore.xcodeproj/project.pbxproj | 42 +- CoreStoreTests/DynamicModelTests.swift | 7 +- Sources/AttributeProtocol.swift | 47 ++ Sources/CoreStoreSchema.swift | 8 +- Sources/DynamicSchema+Convenience.swift | 51 +- Sources/ImportableAttributeType.swift | 172 +------ Sources/Info.plist | 2 +- Sources/Relationship.swift | 84 ++-- Sources/RelationshipProtocol.swift | 45 ++ Sources/Transformable.swift | 597 ++++++++++++++++++++++ Sources/Value.swift | 633 +++--------------------- 11 files changed, 868 insertions(+), 820 deletions(-) create mode 100644 Sources/AttributeProtocol.swift create mode 100644 Sources/RelationshipProtocol.swift create mode 100644 Sources/Transformable.swift diff --git a/CoreStore.xcodeproj/project.pbxproj b/CoreStore.xcodeproj/project.pbxproj index c34ff5f..a390071 100644 --- a/CoreStore.xcodeproj/project.pbxproj +++ b/CoreStore.xcodeproj/project.pbxproj @@ -449,6 +449,18 @@ B580857A1CDF808C004C2EEB /* SetupTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B58085741CDF7F00004C2EEB /* SetupTests.swift */; }; B580857B1CDF808D004C2EEB /* SetupTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B58085741CDF7F00004C2EEB /* SetupTests.swift */; }; B580857C1CDF808F004C2EEB /* SetupTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B58085741CDF7F00004C2EEB /* SetupTests.swift */; }; + B5831B701F34AC3400A9F647 /* AttributeProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5831B6F1F34AC3400A9F647 /* AttributeProtocol.swift */; }; + B5831B711F34AC3400A9F647 /* AttributeProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5831B6F1F34AC3400A9F647 /* AttributeProtocol.swift */; }; + B5831B721F34AC3400A9F647 /* AttributeProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5831B6F1F34AC3400A9F647 /* AttributeProtocol.swift */; }; + B5831B731F34AC3400A9F647 /* AttributeProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5831B6F1F34AC3400A9F647 /* AttributeProtocol.swift */; }; + B5831B751F34AC7A00A9F647 /* RelationshipProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5831B741F34AC7A00A9F647 /* RelationshipProtocol.swift */; }; + B5831B761F34AC7A00A9F647 /* RelationshipProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5831B741F34AC7A00A9F647 /* RelationshipProtocol.swift */; }; + B5831B771F34AC7A00A9F647 /* RelationshipProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5831B741F34AC7A00A9F647 /* RelationshipProtocol.swift */; }; + B5831B781F34AC7A00A9F647 /* RelationshipProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5831B741F34AC7A00A9F647 /* RelationshipProtocol.swift */; }; + B5831B7A1F34ACBA00A9F647 /* Transformable.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5831B791F34ACBA00A9F647 /* Transformable.swift */; }; + B5831B7B1F34ACBA00A9F647 /* Transformable.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5831B791F34ACBA00A9F647 /* Transformable.swift */; }; + B5831B7C1F34ACBA00A9F647 /* Transformable.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5831B791F34ACBA00A9F647 /* Transformable.swift */; }; + B5831B7D1F34ACBA00A9F647 /* Transformable.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5831B791F34ACBA00A9F647 /* Transformable.swift */; }; B58B22F51C93C1BA00521925 /* CoreStore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2F03A53019C5C6DA005002A5 /* CoreStore.framework */; }; B58D0C631EAA0C7E003EDD87 /* NSManagedObject+DynamicModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B58D0C621EAA0C7E003EDD87 /* NSManagedObject+DynamicModel.swift */; }; B58D0C641EAA0C7E003EDD87 /* NSManagedObject+DynamicModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B58D0C621EAA0C7E003EDD87 /* NSManagedObject+DynamicModel.swift */; }; @@ -816,6 +828,9 @@ B57D27BD1D0BBE8200539C58 /* BaseTestDataTestCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseTestDataTestCase.swift; sourceTree = ""; }; B57D27C11D0BC20100539C58 /* QueryTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QueryTests.swift; sourceTree = ""; }; B58085741CDF7F00004C2EEB /* SetupTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SetupTests.swift; sourceTree = ""; }; + B5831B6F1F34AC3400A9F647 /* AttributeProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributeProtocol.swift; sourceTree = ""; }; + B5831B741F34AC7A00A9F647 /* RelationshipProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelationshipProtocol.swift; sourceTree = ""; }; + B5831B791F34ACBA00A9F647 /* Transformable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Transformable.swift; sourceTree = ""; }; B58D0C621EAA0C7E003EDD87 /* NSManagedObject+DynamicModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSManagedObject+DynamicModel.swift"; sourceTree = ""; }; B596BBAD1DD59FDB001DCDD9 /* ConvenienceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConvenienceTests.swift; sourceTree = ""; }; B596BBB51DD5BC67001DCDD9 /* FetchableSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FetchableSource.swift; sourceTree = ""; }; @@ -1235,17 +1250,26 @@ children = ( B5D339D71E9489AB00C880DE /* CoreStoreObject.swift */, B53CA9A11EF1EF1600E0F440 /* PartialObject.swift */, + B5831B6E1F3355C300A9F647 /* Properties */, B52F74391E9B8724005F3DAC /* Dynamic Schema */, B5D339DC1E9489C700C880DE /* DynamicObject.swift */, B52F742E1E9B50D0005F3DAC /* SchemaHistory.swift */, B5D339E61E9493A500C880DE /* Entity.swift */, - B5D33A001E96012400C880DE /* Relationship.swift */, - B5D339E11E948C3600C880DE /* Value.swift */, B5A991EB1E9DC2CE0091A2E3 /* VersionLock.swift */, ); name = "Dynamic Models"; sourceTree = ""; }; + B5831B6E1F3355C300A9F647 /* Properties */ = { + isa = PBXGroup; + children = ( + B5D33A001E96012400C880DE /* Relationship.swift */, + B5D339E11E948C3600C880DE /* Value.swift */, + B5831B791F34ACBA00A9F647 /* Transformable.swift */, + ); + name = Properties; + sourceTree = ""; + }; B5A5F26B1CAFF8D0004AB9AF /* Swift */ = { isa = PBXGroup; children = ( @@ -1434,6 +1458,8 @@ B5E84F291AFF849C0064E85B /* Internal */ = { isa = PBXGroup; children = ( + B5831B6F1F34AC3400A9F647 /* AttributeProtocol.swift */, + B5831B741F34AC7A00A9F647 /* RelationshipProtocol.swift */, B5C976E61C6E3A5900B1AF90 /* CoreStoreFetchedResultsController.swift */, B526613F1CADD585007B85D9 /* CoreStoreFetchRequest+CoreStore.swift */, B53B275E1EE3B92E00E9B352 /* CoreStoreManagedObject.swift */, @@ -1814,6 +1840,7 @@ B5ECDC1D1CA81A2100C7F112 /* CSDataStack+Querying.swift in Sources */, B5C976E31C6C9F6A00B1AF90 /* UnsafeDataTransaction+Observing.swift in Sources */, B53FBA121CAB63CB00F0D40A /* Progress+ObjectiveC.swift in Sources */, + B5831B751F34AC7A00A9F647 /* RelationshipProtocol.swift in Sources */, B5E1B5A81CAA49E2007FD580 /* CSDataStack+Migrating.swift in Sources */, B5D339F11E94AF5800C880DE /* CoreStoreStrings.swift in Sources */, B56007161B4018AB00A9A8F9 /* MigrationChain.swift in Sources */, @@ -1906,6 +1933,7 @@ B59FA0AE1CCBAC95007C9BCA /* ICloudStore.swift in Sources */, B5E84EF81AFF846E0064E85B /* CoreStore+Transaction.swift in Sources */, B5E84F301AFF849C0064E85B /* NSManagedObjectContext+CoreStore.swift in Sources */, + B5831B7A1F34ACBA00A9F647 /* Transformable.swift in Sources */, B546F9691C9AF26D00D5AC55 /* CSInMemoryStore.swift in Sources */, B53FBA1E1CAB63FA00F0D40A /* NSFetchedResultsController+ObjectiveC.swift in Sources */, B549F65E1E569C7400FBAB2D /* QueryableAttributeType.swift in Sources */, @@ -1921,6 +1949,7 @@ B5E84EE61AFF84610064E85B /* DefaultLogger.swift in Sources */, B53FBA041CAB300C00F0D40A /* CSMigrationType.swift in Sources */, B5E84EF41AFF846E0064E85B /* AsynchronousDataTransaction.swift in Sources */, + B5831B701F34AC3400A9F647 /* AttributeProtocol.swift in Sources */, B5DBE2CD1C9914A900B5CEFA /* CSCoreStore.swift in Sources */, B546F95D1C9A12B800D5AC55 /* CSSQliteStore.swift in Sources */, B5D339E71E9493A500C880DE /* Entity.swift in Sources */, @@ -2000,6 +2029,7 @@ B5ECDC1F1CA81A2100C7F112 /* CSDataStack+Querying.swift in Sources */, B5C976E41C6C9F9A00B1AF90 /* UnsafeDataTransaction+Observing.swift in Sources */, B53FBA141CAB63CB00F0D40A /* Progress+ObjectiveC.swift in Sources */, + B5831B761F34AC7A00A9F647 /* RelationshipProtocol.swift in Sources */, B5E1B5AA1CAA49E2007FD580 /* CSDataStack+Migrating.swift in Sources */, B5D339F21E94AF5800C880DE /* CoreStoreStrings.swift in Sources */, B5D3F6461C887C0A00C7492A /* LegacySQLiteStore.swift in Sources */, @@ -2092,6 +2122,7 @@ 82BA18D31C4BBD7100A0916E /* NSManagedObjectContext+CoreStore.swift in Sources */, 82BA18AD1C4BBD3100A0916E /* UnsafeDataTransaction.swift in Sources */, B546F96A1C9AF26D00D5AC55 /* CSInMemoryStore.swift in Sources */, + B5831B7B1F34ACBA00A9F647 /* Transformable.swift in Sources */, B53FBA201CAB63FA00F0D40A /* NSFetchedResultsController+ObjectiveC.swift in Sources */, 82BA18A81C4BBD2900A0916E /* CoreStoreLogger.swift in Sources */, B549F65F1E569C7400FBAB2D /* QueryableAttributeType.swift in Sources */, @@ -2107,6 +2138,7 @@ B53FBA061CAB300C00F0D40A /* CSMigrationType.swift in Sources */, 82BA18BE1C4BBD4A00A0916E /* Tweak.swift in Sources */, B5DBE2CE1C9914A900B5CEFA /* CSCoreStore.swift in Sources */, + B5831B711F34AC3400A9F647 /* AttributeProtocol.swift in Sources */, B546F95E1C9A12B800D5AC55 /* CSSQliteStore.swift in Sources */, B5ECDC0D1CA8161B00C7F112 /* CSGroupBy.swift in Sources */, B5D339E81E9493A500C880DE /* Entity.swift in Sources */, @@ -2186,6 +2218,7 @@ B5D3F6481C887C0A00C7492A /* LegacySQLiteStore.swift in Sources */, B5220E1C1D130801009BC71E /* FetchedResultsControllerDelegate.swift in Sources */, B52DD19E1BE1F92C00949AFE /* AsynchronousDataTransaction.swift in Sources */, + B5831B781F34AC7A00A9F647 /* RelationshipProtocol.swift in Sources */, B52DD1981BE1F92500949AFE /* CoreStore+Setup.swift in Sources */, B5D339F41E94AF5800C880DE /* CoreStoreStrings.swift in Sources */, B5220E241D13085E009BC71E /* NSFetchedResultsController+Convenience.swift in Sources */, @@ -2278,6 +2311,7 @@ B52DD1A01BE1F92C00949AFE /* UnsafeDataTransaction.swift in Sources */, B5ECDC331CA81CDC00C7F112 /* CSCoreStore+Transaction.swift in Sources */, B52DD1BB1BE1F94000949AFE /* MigrationType.swift in Sources */, + B5831B7D1F34ACBA00A9F647 /* Transformable.swift in Sources */, B52DD1C91BE1F94600949AFE /* NSManagedObjectContext+Transaction.swift in Sources */, B5220E151D130663009BC71E /* CoreStore+Observing.swift in Sources */, B549F6611E569C7400FBAB2D /* QueryableAttributeType.swift in Sources */, @@ -2293,6 +2327,7 @@ B52DD1B91BE1F94000949AFE /* CoreStore+Migration.swift in Sources */, B5519A5C1CA2008C002BEF78 /* CSBaseDataTransaction.swift in Sources */, B5DBE2D51C991B3E00B5CEFA /* CSDataStack.swift in Sources */, + B5831B731F34AC3400A9F647 /* AttributeProtocol.swift in Sources */, B5AEFAB81C9962AE00AD137F /* CoreStoreBridge.swift in Sources */, B598514B1C90289F00C99590 /* NSPersistentStoreCoordinator+Setup.swift in Sources */, B5D339EA1E9493A500C880DE /* Entity.swift in Sources */, @@ -2372,6 +2407,7 @@ B5C976E51C6C9F9B00B1AF90 /* UnsafeDataTransaction+Observing.swift in Sources */, B53FBA151CAB63CB00F0D40A /* Progress+ObjectiveC.swift in Sources */, B5E1B5AB1CAA49E2007FD580 /* CSDataStack+Migrating.swift in Sources */, + B5831B771F34AC7A00A9F647 /* RelationshipProtocol.swift in Sources */, B5D3F6471C887C0A00C7492A /* LegacySQLiteStore.swift in Sources */, B5D339F31E94AF5800C880DE /* CoreStoreStrings.swift in Sources */, B5E1B5A01CAA2568007FD580 /* CSDataStack+Observing.swift in Sources */, @@ -2464,6 +2500,7 @@ B56321B11BD6521C006C9394 /* NSManagedObjectContext+CoreStore.swift in Sources */, B563218D1BD65216006C9394 /* CoreStore+Transaction.swift in Sources */, B546F96B1C9AF26D00D5AC55 /* CSInMemoryStore.swift in Sources */, + B5831B7C1F34ACBA00A9F647 /* Transformable.swift in Sources */, B53FBA211CAB63FA00F0D40A /* NSFetchedResultsController+ObjectiveC.swift in Sources */, B563218B1BD65216006C9394 /* UnsafeDataTransaction.swift in Sources */, B549F6601E569C7400FBAB2D /* QueryableAttributeType.swift in Sources */, @@ -2479,6 +2516,7 @@ B53FBA071CAB300C00F0D40A /* CSMigrationType.swift in Sources */, B56321841BD65216006C9394 /* DefaultLogger.swift in Sources */, B5DBE2CF1C9914A900B5CEFA /* CSCoreStore.swift in Sources */, + B5831B721F34AC3400A9F647 /* AttributeProtocol.swift in Sources */, B546F95F1C9A12B800D5AC55 /* CSSQliteStore.swift in Sources */, B5ECDC0E1CA8161B00C7F112 /* CSGroupBy.swift in Sources */, B5D339E91E9493A500C880DE /* Entity.swift in Sources */, diff --git a/CoreStoreTests/DynamicModelTests.swift b/CoreStoreTests/DynamicModelTests.swift index 20081bf..20686f4 100644 --- a/CoreStoreTests/DynamicModelTests.swift +++ b/CoreStoreTests/DynamicModelTests.swift @@ -37,7 +37,7 @@ import CoreStore class Animal: CoreStoreObject { - let species = Value.Required("species", default: "Swift") + let species = Value.Required("species", initial: "Swift") let master = Relationship.ToOne("master") let color = Transformable.Optional("color") } @@ -45,7 +45,7 @@ class Animal: CoreStoreObject { class Dog: Animal { let nickname = Value.Optional("nickname") - let age = Value.Required("age", default: 1) + let age = Value.Required("age", initial: 1) let friends = Relationship.ToManyOrdered("friends") let friendedBy = Relationship.ToManyUnordered("friendedBy", inverse: { $0.friends }) } @@ -54,12 +54,13 @@ class Person: CoreStoreObject { let title = Value.Required( "title", - default: "Mr.", + initial: "Mr.", customSetter: Person.setTitle ) let name = Value.Required( "name", + initial: "", customSetter: Person.setName ) diff --git a/Sources/AttributeProtocol.swift b/Sources/AttributeProtocol.swift new file mode 100644 index 0000000..dd51238 --- /dev/null +++ b/Sources/AttributeProtocol.swift @@ -0,0 +1,47 @@ +// +// AttributeProtocol.swift +// CoreStore +// +// Copyright © 2017 John Rommel Estropia +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +import Foundation +import CoreData + + +// MARK: - AttributeProtocol + +internal protocol AttributeProtocol: class { + + static var attributeType: NSAttributeType { get } + + var keyPath: KeyPath { get } + var isOptional: Bool { get } + var isIndexed: Bool { get } + var isTransient: Bool { get } + var versionHashModifier: () -> String? { get } + var renamingIdentifier: () -> String? { get } + var defaultValue: () -> Any? { get } + var affectedByKeyPaths: () -> Set { get } + weak var parentObject: CoreStoreObject? { get set } + var getter: CoreStoreManagedObject.CustomGetter? { get } + var setter: CoreStoreManagedObject.CustomSetter? { get } +} diff --git a/Sources/CoreStoreSchema.swift b/Sources/CoreStoreSchema.swift index 6c763b0..4afc80e 100644 --- a/Sources/CoreStoreSchema.swift +++ b/Sources/CoreStoreSchema.swift @@ -295,8 +295,8 @@ public final class CoreStoreSchema: DynamicSchema { description.isIndexed = attribute.isIndexed description.defaultValue = attribute.defaultValue() description.isTransient = attribute.isTransient - description.versionHashModifier = attribute.versionHashModifier - description.renamingIdentifier = attribute.renamingIdentifier + description.versionHashModifier = attribute.versionHashModifier() + description.renamingIdentifier = attribute.renamingIdentifier() propertyDescriptions.append(description) keyPathsByAffectedKeyPaths[attribute.keyPath] = attribute.affectedByKeyPaths() customGetterSetterByKeyPaths[attribute.keyPath] = (attribute.getter, attribute.setter) @@ -308,8 +308,8 @@ public final class CoreStoreSchema: DynamicSchema { description.maxCount = relationship.maxCount description.isOrdered = relationship.isOrdered description.deleteRule = relationship.deleteRule - description.versionHashModifier = relationship.versionHashModifier - description.renamingIdentifier = relationship.renamingIdentifier + description.versionHashModifier = relationship.versionHashModifier() + description.renamingIdentifier = relationship.renamingIdentifier() propertyDescriptions.append(description) keyPathsByAffectedKeyPaths[relationship.keyPath] = relationship.affectedByKeyPaths() diff --git a/Sources/DynamicSchema+Convenience.swift b/Sources/DynamicSchema+Convenience.swift index 45b2fbb..56fd272 100644 --- a/Sources/DynamicSchema+Convenience.swift +++ b/Sources/DynamicSchema+Convenience.swift @@ -96,71 +96,62 @@ public extension DynamicSchema { case .integer16AttributeType: valueType = Int16.self - if let defaultValue = (attribute.defaultValue as! Int16.ImportableNativeType?).flatMap(Int16.cs_fromImportableNativeType), - defaultValue != Int16.cs_emptyValue() { + if let defaultValue = (attribute.defaultValue as! Int16.ImportableNativeType?).flatMap(Int16.cs_fromImportableNativeType) { - defaultString = ", default: \(defaultValue)" + defaultString = ", initial: \(defaultValue)" } case .integer32AttributeType: valueType = Int32.self - if let defaultValue = (attribute.defaultValue as! Int32.ImportableNativeType?).flatMap(Int32.cs_fromImportableNativeType), - defaultValue != Int32.cs_emptyValue() { + if let defaultValue = (attribute.defaultValue as! Int32.ImportableNativeType?).flatMap(Int32.cs_fromImportableNativeType) { - defaultString = ", default: \(defaultValue)" + defaultString = ", initial: \(defaultValue)" } case .integer64AttributeType: valueType = Int64.self - if let defaultValue = (attribute.defaultValue as! Int64.ImportableNativeType?).flatMap(Int64.cs_fromImportableNativeType), - defaultValue != Int64.cs_emptyValue() { + if let defaultValue = (attribute.defaultValue as! Int64.ImportableNativeType?).flatMap(Int64.cs_fromImportableNativeType) { - defaultString = ", default: \(defaultValue)" + defaultString = ", initial: \(defaultValue)" } case .decimalAttributeType: valueType = NSDecimalNumber.self - if let defaultValue = (attribute.defaultValue as! NSDecimalNumber.ImportableNativeType?).flatMap(NSDecimalNumber.cs_fromImportableNativeType), - defaultValue != NSDecimalNumber.cs_emptyValue() { + if let defaultValue = (attribute.defaultValue as! NSDecimalNumber.ImportableNativeType?).flatMap(NSDecimalNumber.cs_fromImportableNativeType) { - defaultString = ", default: NSDecimalNumber(string: \"\(defaultValue.description(withLocale: nil))\")" + defaultString = ", initial: NSDecimalNumber(string: \"\(defaultValue.description(withLocale: nil))\")" } case .doubleAttributeType: valueType = Double.self - if let defaultValue = (attribute.defaultValue as! Double.ImportableNativeType?).flatMap(Double.cs_fromImportableNativeType), - defaultValue != Double.cs_emptyValue() { + if let defaultValue = (attribute.defaultValue as! Double.ImportableNativeType?).flatMap(Double.cs_fromImportableNativeType) { - defaultString = ", default: \(defaultValue)" + defaultString = ", initial: \(defaultValue)" } case .floatAttributeType: valueType = Float.self - if let defaultValue = (attribute.defaultValue as! Float.ImportableNativeType?).flatMap(Float.cs_fromImportableNativeType), - defaultValue != Float.cs_emptyValue() { + if let defaultValue = (attribute.defaultValue as! Float.ImportableNativeType?).flatMap(Float.cs_fromImportableNativeType) { - defaultString = ", default: \(defaultValue)" + defaultString = ", initial: \(defaultValue)" } case .stringAttributeType: valueType = String.self - if let defaultValue = (attribute.defaultValue as! String.ImportableNativeType?).flatMap(String.cs_fromImportableNativeType), - defaultValue != String.cs_emptyValue() { + if let defaultValue = (attribute.defaultValue as! String.ImportableNativeType?).flatMap(String.cs_fromImportableNativeType) { // TODO: escape strings - defaultString = ", default: \"\(defaultValue)\"" + defaultString = ", initial: \"\(defaultValue)\"" } case .booleanAttributeType: valueType = Bool.self - if let defaultValue = (attribute.defaultValue as! Bool.ImportableNativeType?).flatMap(Bool.cs_fromImportableNativeType), - defaultValue != Bool.cs_emptyValue() { + if let defaultValue = (attribute.defaultValue as! Bool.ImportableNativeType?).flatMap(Bool.cs_fromImportableNativeType) { - defaultString = ", default: \(defaultValue ? "true" : "false")" + defaultString = ", initial: \(defaultValue ? "true" : "false")" } case .dateAttributeType: valueType = Date.self if let defaultValue = (attribute.defaultValue as! Date.ImportableNativeType?).flatMap(Date.cs_fromImportableNativeType) { - defaultString = ", default: Date(timeIntervalSinceReferenceDate: \(defaultValue.timeIntervalSinceReferenceDate))" + defaultString = ", initial: Date(timeIntervalSinceReferenceDate: \(defaultValue.timeIntervalSinceReferenceDate))" } case .binaryDataAttributeType: valueType = Data.self - if let defaultValue = (attribute.defaultValue as! Data.ImportableNativeType?).flatMap(Data.cs_fromImportableNativeType), - defaultValue != Data.cs_emptyValue() { + if let defaultValue = (attribute.defaultValue as! Data.ImportableNativeType?).flatMap(Data.cs_fromImportableNativeType) { let count = defaultValue.count let bytes = defaultValue.withUnsafeBytes { (pointer: UnsafePointer) in @@ -168,7 +159,7 @@ public extension DynamicSchema { return (0 ..< (count / MemoryLayout.size)) .map({ "\("0x\(String(pointer[$0], radix: 16, uppercase: false))")" }) } - defaultString = ", default: Data(bytes: [\(bytes.joined(separator: ", "))])" + defaultString = ", initial: Data(bytes: [\(bytes.joined(separator: ", "))])" } case .transformableAttributeType: if let attributeValueClassName = attribute.attributeValueClassName { @@ -181,11 +172,11 @@ public extension DynamicSchema { } if let defaultValue = attribute.defaultValue { - defaultString = ", default: /* \"\(defaultValue)\" */" + defaultString = ", initial: /* \"\(defaultValue)\" */" } else if !attribute.isOptional { - defaultString = ", default: /* required */" + defaultString = ", initial: /* required */" } default: fatalError("Unsupported attribute type: \(attribute.attributeType.rawValue)") diff --git a/Sources/ImportableAttributeType.swift b/Sources/ImportableAttributeType.swift index 3770a5d..d8d82d9 100644 --- a/Sources/ImportableAttributeType.swift +++ b/Sources/ImportableAttributeType.swift @@ -78,26 +78,9 @@ 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, EmptyableAttributeType { +extension Bool: ImportableAttributeType { // MARK: ImportableAttributeType @@ -114,21 +97,11 @@ extension Bool: ImportableAttributeType, EmptyableAttributeType { return self.cs_toQueryableNativeType() } - - - // MARK: EmptyableAttributeType - - @inline(__always) - public static func cs_emptyValue() -> Bool { - - return false - } } - // MARK: - CGFloat -extension CGFloat: ImportableAttributeType, EmptyableAttributeType { +extension CGFloat: ImportableAttributeType { // MARK: ImportableAttributeType @@ -145,21 +118,12 @@ extension CGFloat: ImportableAttributeType, EmptyableAttributeType { return self.cs_toQueryableNativeType() } - - - // MARK: EmptyableAttributeType - - @inline(__always) - public static func cs_emptyValue() -> CGFloat { - - return 0 - } } // MARK: - Data -extension Data: ImportableAttributeType, EmptyableAttributeType { +extension Data: ImportableAttributeType { // MARK: ImportableAttributeType @@ -176,15 +140,6 @@ extension Data: ImportableAttributeType, EmptyableAttributeType { return self.cs_toQueryableNativeType() } - - - // MARK: EmptyableAttributeType - - @inline(__always) - public static func cs_emptyValue() -> Data { - - return Data() - } } @@ -212,7 +167,7 @@ extension Date: ImportableAttributeType { // MARK: - Double -extension Double: ImportableAttributeType, EmptyableAttributeType { +extension Double: ImportableAttributeType { // MARK: ImportableAttributeType @@ -229,21 +184,12 @@ extension Double: ImportableAttributeType, EmptyableAttributeType { return self.cs_toQueryableNativeType() } - - - // MARK: EmptyableAttributeType - - @inline(__always) - public static func cs_emptyValue() -> Double { - - return 0 - } } // MARK: - Float -extension Float: ImportableAttributeType, EmptyableAttributeType { +extension Float: ImportableAttributeType { // MARK: ImportableAttributeType @@ -260,21 +206,12 @@ extension Float: ImportableAttributeType, EmptyableAttributeType { return self.cs_toQueryableNativeType() } - - - // MARK: EmptyableAttributeType - - @inline(__always) - public static func cs_emptyValue() -> Float { - - return 0 - } } // MARK: - Int -extension Int: ImportableAttributeType, EmptyableAttributeType { +extension Int: ImportableAttributeType { // MARK: ImportableAttributeType @@ -291,21 +228,12 @@ extension Int: ImportableAttributeType, EmptyableAttributeType { return self.cs_toQueryableNativeType() } - - - // MARK: EmptyableAttributeType - - @inline(__always) - public static func cs_emptyValue() -> Int { - - return 0 - } } // MARK: - Int8 -extension Int8: ImportableAttributeType, EmptyableAttributeType { +extension Int8: ImportableAttributeType { // MARK: ImportableAttributeType @@ -322,21 +250,12 @@ extension Int8: ImportableAttributeType, EmptyableAttributeType { return self.cs_toQueryableNativeType() } - - - // MARK: EmptyableAttributeType - - @inline(__always) - public static func cs_emptyValue() -> Int8 { - - return 0 - } } // MARK: - Int16 -extension Int16: ImportableAttributeType, EmptyableAttributeType { +extension Int16: ImportableAttributeType { // MARK: ImportableAttributeType @@ -353,21 +272,12 @@ extension Int16: ImportableAttributeType, EmptyableAttributeType { return self.cs_toQueryableNativeType() } - - - // MARK: EmptyableAttributeType - - @inline(__always) - public static func cs_emptyValue() -> Int16 { - - return 0 - } } // MARK: - Int32 -extension Int32: ImportableAttributeType, EmptyableAttributeType { +extension Int32: ImportableAttributeType { // MARK: ImportableAttributeType @@ -384,21 +294,12 @@ extension Int32: ImportableAttributeType, EmptyableAttributeType { return self.cs_toQueryableNativeType() } - - - // MARK: EmptyableAttributeType - - @inline(__always) - public static func cs_emptyValue() -> Int32 { - - return 0 - } } // MARK: - Int64 -extension Int64: ImportableAttributeType, EmptyableAttributeType { +extension Int64: ImportableAttributeType { // MARK: ImportableAttributeType @@ -415,21 +316,12 @@ extension Int64: ImportableAttributeType, EmptyableAttributeType { return self.cs_toQueryableNativeType() } - - - // MARK: EmptyableAttributeType - - @inline(__always) - public static func cs_emptyValue() -> Int64 { - - return 0 - } } // MARK: - NSData -extension NSData: ImportableAttributeType, EmptyableAttributeType { +extension NSData: ImportableAttributeType { // MARK: ImportableAttributeType @@ -446,15 +338,6 @@ extension NSData: ImportableAttributeType, EmptyableAttributeType { return self.cs_toQueryableNativeType() } - - - // MARK: EmptyableAttributeType - - @nonobjc @inline(__always) - public class func cs_emptyValue() -> Self { - - return self.init() - } } @@ -482,7 +365,7 @@ extension NSDate: ImportableAttributeType { // MARK: - NSNumber -extension NSNumber: ImportableAttributeType, EmptyableAttributeType { +extension NSNumber: ImportableAttributeType { // MARK: ImportableAttributeType @@ -499,21 +382,12 @@ extension NSNumber: ImportableAttributeType, EmptyableAttributeType { return self.cs_toQueryableNativeType() } - - - // MARK: EmptyableAttributeType - - @nonobjc @inline(__always) - public class func cs_emptyValue() -> Self { - - return self.init() - } } // MARK: - NSString -extension NSString: ImportableAttributeType, EmptyableAttributeType { +extension NSString: ImportableAttributeType { // MARK: ImportableAttributeType @@ -530,15 +404,6 @@ extension NSString: ImportableAttributeType, EmptyableAttributeType { return self.cs_toQueryableNativeType() } - - - // MARK: EmptyableAttributeType - - @nonobjc @inline(__always) - public class func cs_emptyValue() -> Self { - - return self.init() - } } @@ -588,7 +453,7 @@ extension NSUUID: ImportableAttributeType { // MARK: - String -extension String: ImportableAttributeType, EmptyableAttributeType { +extension String: ImportableAttributeType { // MARK: ImportableAttributeType @@ -605,15 +470,6 @@ extension String: ImportableAttributeType, EmptyableAttributeType { return self.cs_toQueryableNativeType() } - - - // MARK: EmptyableAttributeType - - @inline(__always) - public static func cs_emptyValue() -> String { - - return "" - } } diff --git a/Sources/Info.plist b/Sources/Info.plist index 5d7e8c4..8492747 100644 --- a/Sources/Info.plist +++ b/Sources/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 4.0.5 + 4.1 CFBundleSignature ???? CFBundleVersion diff --git a/Sources/Relationship.swift b/Sources/Relationship.swift index 848ae8f..240b14b 100644 --- a/Sources/Relationship.swift +++ b/Sources/Relationship.swift @@ -97,8 +97,8 @@ public enum RelationshipContainer { public convenience init( _ keyPath: KeyPath, deleteRule: DeleteRule = .nullify, - versionHashModifier: String? = nil, - renamingIdentifier: String? = nil, + versionHashModifier: @autoclosure @escaping () -> String? = nil, + renamingIdentifier: @autoclosure @escaping () -> String? = nil, affectedByKeyPaths: @autoclosure @escaping () -> Set = []) { self.init( @@ -132,8 +132,8 @@ public enum RelationshipContainer { _ keyPath: KeyPath, inverse: @escaping (D) -> RelationshipContainer.ToOne, deleteRule: DeleteRule = .nullify, - versionHashModifier: String? = nil, - renamingIdentifier: String? = nil, + versionHashModifier: @autoclosure @escaping () -> String? = nil, + renamingIdentifier: @autoclosure @escaping () -> String? = nil, affectedByKeyPaths: @autoclosure @escaping () -> Set = []) { self.init( @@ -167,8 +167,8 @@ public enum RelationshipContainer { _ keyPath: KeyPath, inverse: @escaping (D) -> RelationshipContainer.ToManyOrdered, deleteRule: DeleteRule = .nullify, - versionHashModifier: String? = nil, - renamingIdentifier: String? = nil, + versionHashModifier: @autoclosure @escaping () -> String? = nil, + renamingIdentifier: @autoclosure @escaping () -> String? = nil, affectedByKeyPaths: @autoclosure @escaping () -> Set = []) { self.init( @@ -202,8 +202,8 @@ public enum RelationshipContainer { _ keyPath: KeyPath, inverse: @escaping (D) -> RelationshipContainer.ToManyUnordered, deleteRule: DeleteRule = .nullify, - versionHashModifier: String? = nil, - renamingIdentifier: String? = nil, + versionHashModifier: @autoclosure @escaping () -> String? = nil, + renamingIdentifier: @autoclosure @escaping () -> String? = nil, affectedByKeyPaths: @autoclosure @escaping () -> Set = []) { self.init( @@ -242,8 +242,8 @@ public enum RelationshipContainer { internal let minCount: Int = 0 internal let maxCount: Int = 1 internal let inverse: (type: CoreStoreObject.Type, keyPath: () -> KeyPath?) - internal let versionHashModifier: String? - internal let renamingIdentifier: String? + internal let versionHashModifier: () -> String? + internal let renamingIdentifier: () -> String? internal let affectedByKeyPaths: () -> Set internal weak var parentObject: CoreStoreObject? @@ -294,7 +294,7 @@ public enum RelationshipContainer { // MARK: Private - private init(keyPath: KeyPath, inverseKeyPath: @escaping () -> KeyPath?, deleteRule: DeleteRule, versionHashModifier: String?, renamingIdentifier: String?, affectedByKeyPaths: @autoclosure @escaping () -> Set) { + private init(keyPath: KeyPath, inverseKeyPath: @escaping () -> KeyPath?, deleteRule: DeleteRule, versionHashModifier: @autoclosure @escaping () -> String?, renamingIdentifier: @autoclosure @escaping () -> String?, affectedByKeyPaths: @autoclosure @escaping () -> Set) { self.keyPath = keyPath self.deleteRule = deleteRule.nativeValue @@ -345,8 +345,8 @@ public enum RelationshipContainer { minCount: Int = 0, maxCount: Int = 0, deleteRule: DeleteRule = .nullify, - versionHashModifier: String? = nil, - renamingIdentifier: String? = nil, + versionHashModifier: @autoclosure @escaping () -> String? = nil, + renamingIdentifier: @autoclosure @escaping () -> String? = nil, affectedByKeyPaths: @autoclosure @escaping () -> Set = []) { self.init( @@ -386,8 +386,8 @@ public enum RelationshipContainer { maxCount: Int = 0, inverse: @escaping (D) -> RelationshipContainer.ToOne, deleteRule: DeleteRule = .nullify, - versionHashModifier: String? = nil, - renamingIdentifier: String? = nil, + versionHashModifier: @autoclosure @escaping () -> String? = nil, + renamingIdentifier: @autoclosure @escaping () -> String? = nil, affectedByKeyPaths: @autoclosure @escaping () -> Set = []) { self.init( @@ -427,8 +427,8 @@ public enum RelationshipContainer { maxCount: Int = 0, inverse: @escaping (D) -> RelationshipContainer.ToManyOrdered, deleteRule: DeleteRule = .nullify, - versionHashModifier: String? = nil, - renamingIdentifier: String? = nil, + versionHashModifier: @autoclosure @escaping () -> String? = nil, + renamingIdentifier: @autoclosure @escaping () -> String? = nil, affectedByKeyPaths: @autoclosure @escaping () -> Set = []) { self.init( @@ -468,8 +468,8 @@ public enum RelationshipContainer { maxCount: Int = 0, inverse: @escaping (D) -> RelationshipContainer.ToManyUnordered, deleteRule: DeleteRule = .nullify, - versionHashModifier: String? = nil, - renamingIdentifier: String? = nil, + versionHashModifier: @autoclosure @escaping () -> String? = nil, + renamingIdentifier: @autoclosure @escaping () -> String? = nil, affectedByKeyPaths: @autoclosure @escaping () -> Set = []) { self.init( @@ -511,8 +511,8 @@ public enum RelationshipContainer { internal let minCount: Int internal let maxCount: Int internal let inverse: (type: CoreStoreObject.Type, keyPath: () -> KeyPath?) - internal let versionHashModifier: String? - internal let renamingIdentifier: String? + internal let versionHashModifier: () -> String? + internal let renamingIdentifier: () -> String? internal let affectedByKeyPaths: () -> Set internal weak var parentObject: CoreStoreObject? @@ -563,7 +563,7 @@ public enum RelationshipContainer { // MARK: Private - private init(keyPath: String, minCount: Int, maxCount: Int, inverseKeyPath: @escaping () -> String?, deleteRule: DeleteRule, versionHashModifier: String?, renamingIdentifier: String?, affectedByKeyPaths: @autoclosure @escaping () -> Set) { + private init(keyPath: String, minCount: Int, maxCount: Int, inverseKeyPath: @escaping () -> String?, deleteRule: DeleteRule, versionHashModifier: @autoclosure @escaping () -> String?, renamingIdentifier: @autoclosure @escaping () -> String?, affectedByKeyPaths: @autoclosure @escaping () -> Set) { self.keyPath = keyPath self.deleteRule = deleteRule.nativeValue @@ -619,8 +619,8 @@ public enum RelationshipContainer { deleteRule: DeleteRule = .nullify, minCount: Int = 0, maxCount: Int = 0, - versionHashModifier: String? = nil, - renamingIdentifier: String? = nil, + versionHashModifier: @autoclosure @escaping () -> String? = nil, + renamingIdentifier: @autoclosure @escaping () -> String? = nil, affectedByKeyPaths: @autoclosure @escaping () -> Set = []) { self.init( @@ -660,8 +660,8 @@ public enum RelationshipContainer { deleteRule: DeleteRule = .nullify, minCount: Int = 0, maxCount: Int = 0, - versionHashModifier: String? = nil, - renamingIdentifier: String? = nil, + versionHashModifier: @autoclosure @escaping () -> String? = nil, + renamingIdentifier: @autoclosure @escaping () -> String? = nil, affectedByKeyPaths: @autoclosure @escaping () -> Set = []) { self.init( @@ -701,8 +701,8 @@ public enum RelationshipContainer { deleteRule: DeleteRule = .nullify, minCount: Int = 0, maxCount: Int = 0, - versionHashModifier: String? = nil, - renamingIdentifier: String? = nil, + versionHashModifier: @autoclosure @escaping () -> String? = nil, + renamingIdentifier: @autoclosure @escaping () -> String? = nil, affectedByKeyPaths: @autoclosure @escaping () -> Set = []) { self.init( @@ -742,8 +742,8 @@ public enum RelationshipContainer { deleteRule: DeleteRule = .nullify, minCount: Int = 0, maxCount: Int = 0, - versionHashModifier: String? = nil, - renamingIdentifier: String? = nil, + versionHashModifier: @autoclosure @escaping () -> String? = nil, + renamingIdentifier: @autoclosure @escaping () -> String? = nil, affectedByKeyPaths: @autoclosure @escaping () -> Set = []) { self.init( @@ -785,8 +785,8 @@ public enum RelationshipContainer { internal let minCount: Int internal let maxCount: Int internal let inverse: (type: CoreStoreObject.Type, keyPath: () -> KeyPath?) - internal let versionHashModifier: String? - internal let renamingIdentifier: String? + internal let versionHashModifier: () -> String? + internal let renamingIdentifier: () -> String? internal let affectedByKeyPaths: () -> Set internal weak var parentObject: CoreStoreObject? @@ -837,7 +837,7 @@ public enum RelationshipContainer { // MARK: Private - private init(keyPath: KeyPath, inverseKeyPath: @escaping () -> KeyPath?, deleteRule: DeleteRule, minCount: Int, maxCount: Int, versionHashModifier: String?, renamingIdentifier: String?, affectedByKeyPaths: @autoclosure @escaping () -> Set) { + private init(keyPath: KeyPath, inverseKeyPath: @escaping () -> KeyPath?, deleteRule: DeleteRule, minCount: Int, maxCount: Int, versionHashModifier: @autoclosure @escaping () -> String?, renamingIdentifier: @autoclosure @escaping () -> String?, affectedByKeyPaths: @autoclosure @escaping () -> Set) { self.keyPath = keyPath self.deleteRule = deleteRule.nativeValue @@ -1204,21 +1204,3 @@ extension RelationshipContainer.ToManyUnordered { return relationship.nativeValue.isEqual(relationship2.nativeValue) } } - - -// MARK: - RelationshipProtocol - -internal protocol RelationshipProtocol: class { - - var keyPath: KeyPath { get } - var isToMany: Bool { get } - var isOrdered: Bool { get } - var deleteRule: NSDeleteRule { get } - var inverse: (type: CoreStoreObject.Type, keyPath: () -> KeyPath?) { get } - var affectedByKeyPaths: () -> Set { get } - weak var parentObject: CoreStoreObject? { get set } - var versionHashModifier: String? { get } - var renamingIdentifier: String? { get } - var minCount: Int { get } - var maxCount: Int { get } -} diff --git a/Sources/RelationshipProtocol.swift b/Sources/RelationshipProtocol.swift new file mode 100644 index 0000000..0b608f9 --- /dev/null +++ b/Sources/RelationshipProtocol.swift @@ -0,0 +1,45 @@ +// +// RelationshipProtocol.swift +// CoreStore +// +// Copyright © 2017 John Rommel Estropia +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +import Foundation +import CoreData + + +// MARK: - RelationshipProtocol + +internal protocol RelationshipProtocol: class { + + var keyPath: KeyPath { get } + var isToMany: Bool { get } + var isOrdered: Bool { get } + var deleteRule: NSDeleteRule { get } + var inverse: (type: CoreStoreObject.Type, keyPath: () -> KeyPath?) { get } + var affectedByKeyPaths: () -> Set { get } + weak var parentObject: CoreStoreObject? { get set } + var versionHashModifier: () -> String? { get } + var renamingIdentifier: () -> String? { get } + var minCount: Int { get } + var maxCount: Int { get } +} diff --git a/Sources/Transformable.swift b/Sources/Transformable.swift new file mode 100644 index 0000000..0f18bae --- /dev/null +++ b/Sources/Transformable.swift @@ -0,0 +1,597 @@ +// +// Transformable.swift +// CoreStore +// +// Copyright © 2017 John Rommel Estropia +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +import CoreData +import Foundation + + +// MARK: - DynamicObject + +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") + } + ``` + - Important: `Transformable` properties are required to be stored properties. Computed properties will be ignored, including `lazy` and `weak` properties. + */ + public typealias Transformable = TransformableContainer +} + + +// MARK: - TransformableContainer + +/** + The containing type for transformable properties. Use the `DynamicObject.Transformable` typealias instead for shorter syntax. + ``` + class Animal: CoreStoreObject { + let species = Value.Required("species") + let nickname = Value.Optional("nickname") + let color = Transformable.Optional("color") + } + ``` + */ +public enum TransformableContainer { + + // MARK: - Required + + /** + The containing type for transformable properties. Any type that conforms to `NSCoding & NSCopying` are supported. + ``` + class Animal: CoreStoreObject { + let species = Value.Required("species") + let nickname = Value.Optional("nickname") + let color = Transformable.Optional("color") + } + ``` + - Important: `Transformable.Required` properties are required to be stored properties. Computed properties will be ignored, including `lazy` and `weak` properties. + */ + public final class Required: AttributeProtocol { + + /** + Initializes the metadata for the property. + ``` + class Animal: CoreStoreObject { + let species = Value.Required("species") + let color = Transformable.Required( + "color", + initial: UIColor.clear, + isTransient: true, + customGetter: Animal.getColor(_:) + ) + } + + private static func getColor(_ partialObject: PartialObject) -> UIColor { + let cachedColor = partialObject.primitiveValue(for: { $0.color }) + if cachedColor != UIColor.clear { + + return cachedColor + } + let color: UIColor + switch partialObject.value(for: { $0.species }) { + + case "Swift": color = UIColor.orange + case "Bulbasaur": color = UIColor.green + default: color = UIColor.black + } + partialObject.setPrimitiveValue(color, for: { $0.color }) + return color + } + ``` + - parameter keyPath: the permanent attribute name for this property. + - parameter initial: the initial value for the property when the object is first created. Defaults to the `ImportableAttributeType`'s empty value if not specified. + - 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 as an "override" for the default property getter. The closure receives a `PartialObject`, which acts as a fast, type-safe KVC interface for `CoreStoreObject`. The reason a `CoreStoreObject` instance is not passed directly is because the Core Data runtime is not aware of `CoreStoreObject` properties' static typing, and so loading those info everytime KVO invokes this accessor method incurs a cumulative performance hit (especially in KVO-heavy operations such as `ListMonitor` observing.) When accessing the property value from `PartialObject`, make sure to use `PartialObject.primitiveValue(for:)` instead of `PartialObject.value(for:)`, which would unintentionally execute the same closure again recursively. + - parameter customSetter: use this closure as an "override" for the default property setter. The closure receives a `PartialObject`, which acts as a fast, type-safe KVC interface for `CoreStoreObject`. The reason a `CoreStoreObject` instance is not passed directly is because the Core Data runtime is not aware of `CoreStoreObject` properties' static typing, and so loading those info everytime KVO invokes this accessor method incurs a cumulative performance hit (especially in KVO-heavy operations such as `ListMonitor` observing.) When accessing the property value from `PartialObject`, make sure to use `PartialObject.setPrimitiveValue(_:for:)` instead of `PartialObject.setValue(_:for:)`, which would unintentionally execute the same closure again recursively. + - parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`. + */ + public init( + _ keyPath: KeyPath, + initial: @autoclosure @escaping () -> V, + isIndexed: Bool = false, + isTransient: Bool = false, + versionHashModifier: @autoclosure @escaping () -> String? = nil, + renamingIdentifier: @autoclosure @escaping () -> String? = nil, + customGetter: ((_ partialObject: PartialObject) -> V)? = nil, + customSetter: ((_ partialObject: PartialObject, _ newValue: V) -> Void)? = nil, + affectedByKeyPaths: @autoclosure @escaping () -> Set = []) { + + self.keyPath = keyPath + self.defaultValue = initial + self.isIndexed = isIndexed + self.isTransient = isTransient + self.versionHashModifier = versionHashModifier + self.renamingIdentifier = renamingIdentifier + self.customGetter = customGetter + self.customSetter = customSetter + self.affectedByKeyPaths = affectedByKeyPaths + } + + /** + The property value. + */ + public var value: V { + + get { + + CoreStore.assert( + self.parentObject != nil, + "Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types." + ) + return withExtendedLifetime(self.parentObject! as! O) { (object: O) in + + CoreStore.assert( + object.rawObject!.isRunningInAllowedQueue() == true, + "Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue." + ) + if let customGetter = self.customGetter { + + return customGetter(PartialObject(object.rawObject!)) + } + return object.rawObject!.value(forKey: self.keyPath)! as! V + } + } + set { + + CoreStore.assert( + self.parentObject != nil, + "Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types." + ) + return withExtendedLifetime(self.parentObject! as! O) { (object: O) in + + CoreStore.assert( + object.rawObject!.isRunningInAllowedQueue() == true, + "Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue." + ) + CoreStore.assert( + object.rawObject!.isEditableInContext() == true, + "Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction." + ) + if let customSetter = self.customSetter { + + return customSetter(PartialObject(object.rawObject!), newValue) + } + object.rawObject!.setValue( + newValue, + forKey: self.keyPath + ) + } + } + } + + + // MARK: AttributeProtocol + + internal static var attributeType: NSAttributeType { + + return .transformableAttributeType + } + + public let keyPath: KeyPath + + internal let isOptional = false + internal let isIndexed: Bool + internal let isTransient: Bool + internal let versionHashModifier: () -> String? + internal let renamingIdentifier: () -> String? + internal let defaultValue: () -> Any? + internal let affectedByKeyPaths: () -> Set + internal weak var parentObject: CoreStoreObject? + + internal private(set) lazy var getter: CoreStoreManagedObject.CustomGetter? = cs_lazy { [unowned self] in + + guard let customGetter = self.customGetter else { + + return nil + } + let keyPath = self.keyPath + return { (_ id: Any) -> Any? in + + let rawObject = id as! CoreStoreManagedObject + rawObject.willAccessValue(forKey: keyPath) + defer { + + rawObject.didAccessValue(forKey: keyPath) + } + let value = customGetter(PartialObject(rawObject)) + return value + } + } + + internal private(set) lazy var setter: CoreStoreManagedObject.CustomSetter? = cs_lazy { [unowned self] in + + guard let customSetter = self.customSetter else { + + return nil + } + let keyPath = self.keyPath + return { (_ id: Any, _ newValue: Any?) -> Void in + + let rawObject = id as! CoreStoreManagedObject + rawObject.willChangeValue(forKey: keyPath) + defer { + + rawObject.didChangeValue(forKey: keyPath) + } + customSetter( + PartialObject(rawObject), + newValue as! V + ) + } + } + + + // MARK: Private + + private let customGetter: ((_ partialObject: PartialObject) -> V)? + private let customSetter: ((_ partialObject: PartialObject, _ newValue: V) -> Void)? + + + // MARK: Deprecated + + @available(*, deprecated: 3.1, renamed: "init(_:initial:isIndexed:isTransient:versionHashModifier:renamingIdentifier:customGetter:customSetter:affectedByKeyPaths:)") + public convenience init( + _ keyPath: KeyPath, + `default`: @autoclosure @escaping () -> V, + isIndexed: Bool = false, + isTransient: Bool = false, + versionHashModifier: @autoclosure @escaping () -> String? = nil, + renamingIdentifier: @autoclosure @escaping () -> String? = nil, + customGetter: ((_ partialObject: PartialObject) -> V)? = nil, + customSetter: ((_ partialObject: PartialObject, _ newValue: V) -> Void)? = nil, + affectedByKeyPaths: @autoclosure @escaping () -> Set = []) { + + self.init( + keyPath, + initial: `default`, + isIndexed: isIndexed, + isTransient: isTransient, + versionHashModifier: versionHashModifier, + renamingIdentifier: renamingIdentifier, + customGetter: customGetter, + customSetter: customSetter, + affectedByKeyPaths: affectedByKeyPaths + ) + } + } + + + // MARK: - Optional + + /** + 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 nickname = Value.Optional("nickname") + let color = Transformable.Optional("color") + } + ``` + - Important: `Transformable.Optional` properties are required to be stored properties. Computed properties will be ignored, including `lazy` and `weak` properties. + */ + public final class Optional: AttributeProtocol { + + /** + Initializes the metadata for the property. + ``` + class Animal: CoreStoreObject { + let species = Value.Required("species") + let color = Transformable.Optional( + "color", + isTransient: true, + customGetter: Animal.getColor(_:) + ) + } + + private static func getColor(_ partialObject: PartialObject) -> UIColor? { + if let cachedColor = partialObject.primitiveValue(for: { $0.color }) { + return cachedColor + } + let color: UIColor? + switch partialObject.value(for: { $0.species }) { + + case "Swift": color = UIColor.orange + case "Bulbasaur": color = UIColor.green + default: return nil + } + partialObject.setPrimitiveValue(color, for: { $0.color }) + return color + } + ``` + - parameter keyPath: the permanent attribute name for this property. + - parameter initial: the initial value for the property when the object is first created. Defaults to the `ImportableAttributeType`'s empty value if not specified. + - 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 as an "override" for the default property getter. The closure receives a `PartialObject`, which acts as a fast, type-safe KVC interface for `CoreStoreObject`. The reason a `CoreStoreObject` instance is not passed directly is because the Core Data runtime is not aware of `CoreStoreObject` properties' static typing, and so loading those info everytime KVO invokes this accessor method incurs a cumulative performance hit (especially in KVO-heavy operations such as `ListMonitor` observing.) When accessing the property value from `PartialObject`, make sure to use `PartialObject.primitiveValue(for:)` instead of `PartialObject.value(for:)`, which would unintentionally execute the same closure again recursively. + - parameter customSetter: use this closure as an "override" for the default property setter. The closure receives a `PartialObject`, which acts as a fast, type-safe KVC interface for `CoreStoreObject`. The reason a `CoreStoreObject` instance is not passed directly is because the Core Data runtime is not aware of `CoreStoreObject` properties' static typing, and so loading those info everytime KVO invokes this accessor method incurs a cumulative performance hit (especially in KVO-heavy operations such as `ListMonitor` observing.) When accessing the property value from `PartialObject`, make sure to use `PartialObject.setPrimitiveValue(_:for:)` instead of `PartialObject.setValue(_:for:)`, which would unintentionally execute the same closure again recursively. + - parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`. + */ + public init( + _ keyPath: KeyPath, + initial: @autoclosure @escaping () -> V? = nil, + isIndexed: Bool = false, + isTransient: Bool = false, + versionHashModifier: @autoclosure @escaping () -> String? = nil, + renamingIdentifier: @autoclosure @escaping () -> String? = nil, + customGetter: ((_ partialObject: PartialObject) -> V?)? = nil, + customSetter: ((_ partialObject: PartialObject, _ newValue: V?) -> Void)? = nil, + affectedByKeyPaths: @autoclosure @escaping () -> Set = []) { + + self.keyPath = keyPath + self.defaultValue = initial + self.isIndexed = isIndexed + self.isTransient = isTransient + self.versionHashModifier = versionHashModifier + self.renamingIdentifier = renamingIdentifier + self.customGetter = customGetter + self.customSetter = customSetter + self.affectedByKeyPaths = affectedByKeyPaths + } + + /** + The property value. + */ + public var value: V? { + + get { + + CoreStore.assert( + self.parentObject != nil, + "Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types." + ) + return withExtendedLifetime(self.parentObject! as! O) { (object: O) in + + CoreStore.assert( + object.rawObject!.isRunningInAllowedQueue() == true, + "Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue." + ) + if let customGetter = self.customGetter { + + return customGetter(PartialObject(object.rawObject!)) + } + return object.rawObject!.value(forKey: self.keyPath) as! V? + } + } + set { + + CoreStore.assert( + self.parentObject != nil, + "Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types." + ) + return withExtendedLifetime(self.parentObject! as! O) { (object: O) in + + CoreStore.assert( + object.rawObject!.isRunningInAllowedQueue() == true, + "Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue." + ) + CoreStore.assert( + object.rawObject!.isEditableInContext() == true, + "Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction." + ) + if let customSetter = self.customSetter { + + return customSetter(PartialObject(object.rawObject!), newValue) + } + object.rawObject!.setValue( + newValue, + forKey: self.keyPath + ) + } + } + } + + + // MARK: AttributeProtocol + + internal static var attributeType: NSAttributeType { + + return .transformableAttributeType + } + + public let keyPath: KeyPath + + internal let isOptional = true + internal let isIndexed: Bool + internal let isTransient: Bool + internal let versionHashModifier: () -> String? + internal let renamingIdentifier: () -> String? + internal let defaultValue: () -> Any? + internal let affectedByKeyPaths: () -> Set + internal weak var parentObject: CoreStoreObject? + + internal private(set) lazy var getter: CoreStoreManagedObject.CustomGetter? = cs_lazy { [unowned self] in + + guard let customGetter = self.customGetter else { + + return nil + } + let keyPath = self.keyPath + return { (_ id: Any) -> Any? in + + let rawObject = id as! CoreStoreManagedObject + rawObject.willAccessValue(forKey: keyPath) + defer { + + rawObject.didAccessValue(forKey: keyPath) + } + let value = customGetter(PartialObject(rawObject)) + return value + } + } + + internal private(set) lazy var setter: CoreStoreManagedObject.CustomSetter? = cs_lazy { [unowned self] in + + guard let customSetter = self.customSetter else { + + return nil + } + let keyPath = self.keyPath + return { (_ id: Any, _ newValue: Any?) -> Void in + + let rawObject = id as! CoreStoreManagedObject + rawObject.willChangeValue(forKey: keyPath) + defer { + + rawObject.didChangeValue(forKey: keyPath) + } + customSetter( + PartialObject(rawObject), + newValue as! V? + ) + } + } + + + // MARK: Private + + private let customGetter: ((_ partialObject: PartialObject) -> V?)? + private let customSetter: ((_ partialObject: PartialObject, _ newValue: V?) -> Void)? + + + // MARK: Deprecated + + @available(*, deprecated: 3.1, renamed: "init(_:initial:isIndexed:isTransient:versionHashModifier:renamingIdentifier:customGetter:customSetter:affectedByKeyPaths:)") + public convenience init( + _ keyPath: KeyPath, + `default`: @autoclosure @escaping () -> V?, + isIndexed: Bool = false, + isTransient: Bool = false, + versionHashModifier: @autoclosure @escaping () -> String? = nil, + renamingIdentifier: @autoclosure @escaping () -> String? = nil, + customGetter: ((_ partialObject: PartialObject) -> V?)? = nil, + customSetter: ((_ partialObject: PartialObject, _ newValue: V?) -> Void)? = nil, + affectedByKeyPaths: @autoclosure @escaping () -> Set = []) { + + self.init( + keyPath, + initial: `default`, + isIndexed: isIndexed, + isTransient: isTransient, + versionHashModifier: versionHashModifier, + renamingIdentifier: renamingIdentifier, + customGetter: customGetter, + customSetter: customSetter, + affectedByKeyPaths: affectedByKeyPaths + ) + } + } +} + + +// MARK: - Operations + +infix operator .= : AssignmentPrecedence +infix operator .== : ComparisonPrecedence + +extension TransformableContainer.Required { + + /** + Assigns a transformable value to the property. The operation + ``` + animal.color .= UIColor.red + ``` + is equivalent to + ``` + animal.color.value = UIColor.red + ``` + */ + public static func .= (_ property: TransformableContainer.Required, _ newValue: V) { + + property.value = newValue + } + + /** + Assigns a transformable value from another property. The operation + ``` + animal.nickname .= anotherAnimal.species + ``` + is equivalent to + ``` + animal.nickname.value = anotherAnimal.species.value + ``` + */ + public static func .= (_ property: TransformableContainer.Required, _ property2: TransformableContainer.Required) { + + property.value = property2.value + } +} + +extension TransformableContainer.Optional { + + /** + Assigns an optional transformable value to the property. The operation + ``` + animal.color .= UIColor.red + ``` + is equivalent to + ``` + animal.color.value = UIColor.red + ``` + */ + public static func .= (_ property: TransformableContainer.Optional, _ newValue: V?) { + + property.value = newValue + } + + /** + Assigns an optional transformable value from another property. The operation + ``` + animal.color .= anotherAnimal.color + ``` + is equivalent to + ``` + animal.color.value = anotherAnimal.color.value + ``` + */ + public static func .= (_ property: TransformableContainer.Optional, _ property2: TransformableContainer.Optional) { + + property.value = property2.value + } + + /** + Assigns a transformable value from another property. The operation + ``` + animal.color .= anotherAnimal.color + ``` + is equivalent to + ``` + animal.color.value = anotherAnimal.color.value + ``` + */ + public static func .= (_ property: TransformableContainer.Optional, _ property2: TransformableContainer.Required) { + + property.value = property2.value + } +} diff --git a/Sources/Value.swift b/Sources/Value.swift index c329ea4..f6c2c7e 100644 --- a/Sources/Value.swift +++ b/Sources/Value.swift @@ -43,19 +43,6 @@ public extension DynamicObject where Self: CoreStoreObject { - Important: `Value` properties are required to be stored properties. Computed properties will be ignored, including `lazy` and `weak` properties. */ public typealias Value = ValueContainer - - /** - 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") - } - ``` - - Important: `Transformable` properties are required to be stored properties. Computed properties will be ignored, including `lazy` and `weak` properties. - */ - public typealias Transformable = TransformableContainer } @@ -92,7 +79,7 @@ public enum ValueContainer { Initializes the metadata for the property. ``` class Person: CoreStoreObject { - let title = Value.Required("title", default: "Mr.") + let title = Value.Required("title", initial: "Mr.") let name = Value.Required("name") let displayName = Value.Required( "displayName", @@ -114,7 +101,7 @@ public enum ValueContainer { } ``` - parameter keyPath: the permanent attribute name for this property. - - 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 initial: the initial value for the property when the object is first created - 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.) @@ -125,11 +112,11 @@ public enum ValueContainer { */ public init( _ keyPath: KeyPath, - `default`: @autoclosure @escaping () -> V, + initial: @autoclosure @escaping () -> V, isIndexed: Bool = false, isTransient: Bool = false, - versionHashModifier: String? = nil, - renamingIdentifier: String? = nil, + versionHashModifier: @autoclosure @escaping () -> String? = nil, + renamingIdentifier: @autoclosure @escaping () -> String? = nil, customGetter: ((_ partialObject: PartialObject) -> V)? = nil, customSetter: ((_ partialObject: PartialObject, _ newValue: V) -> Void)? = nil, affectedByKeyPaths: @autoclosure @escaping () -> Set = []) { @@ -137,7 +124,7 @@ public enum ValueContainer { self.keyPath = keyPath self.isIndexed = isIndexed self.isTransient = isTransient - self.defaultValue = { `default`().cs_toImportableNativeType() } + self.defaultValue = { initial().cs_toImportableNativeType() } self.versionHashModifier = versionHashModifier self.renamingIdentifier = renamingIdentifier self.customGetter = customGetter @@ -212,8 +199,8 @@ public enum ValueContainer { internal let isOptional = false internal let isIndexed: Bool internal let isTransient: Bool - internal let versionHashModifier: String? - internal let renamingIdentifier: String? + internal let versionHashModifier: () -> String? + internal let renamingIdentifier: () -> String? internal let defaultValue: () -> Any? internal let affectedByKeyPaths: () -> Set internal weak var parentObject: CoreStoreObject? @@ -265,6 +252,34 @@ public enum ValueContainer { private let customGetter: ((_ partialObject: PartialObject) -> V)? private let customSetter: ((_ partialObject: PartialObject, _ newValue: V) -> Void)? + + + // MARK: Deprecated + + @available(*, deprecated: 3.1, renamed: "init(_:initial:isIndexed:isTransient:versionHashModifier:renamingIdentifier:customGetter:customSetter:affectedByKeyPaths:)") + public convenience init( + _ keyPath: KeyPath, + `default`: @autoclosure @escaping () -> V, + isIndexed: Bool = false, + isTransient: Bool = false, + versionHashModifier: @autoclosure @escaping () -> String? = nil, + renamingIdentifier: @autoclosure @escaping () -> String? = nil, + customGetter: ((_ partialObject: PartialObject) -> V)? = nil, + customSetter: ((_ partialObject: PartialObject, _ newValue: V) -> Void)? = nil, + affectedByKeyPaths: @autoclosure @escaping () -> Set = []) { + + self.init( + keyPath, + initial: `default`, + isIndexed: isIndexed, + isTransient: isTransient, + versionHashModifier: versionHashModifier, + renamingIdentifier: renamingIdentifier, + customGetter: customGetter, + customSetter: customSetter, + affectedByKeyPaths: affectedByKeyPaths + ) + } } @@ -287,7 +302,7 @@ public enum ValueContainer { Initializes the metadata for the property. ``` class Person: CoreStoreObject { - let title = Value.Optional("title", default: "Mr.") + let title = Value.Optional("title", initial: "Mr.") let name = Value.Optional("name") let displayName = Value.Optional( "displayName", @@ -308,7 +323,7 @@ public enum ValueContainer { } ``` - 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 `nil` if not specified. + - parameter initial: the initial value for the property when the object is first created. Defaults to `nil` if not specified. - 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.) @@ -324,11 +339,11 @@ public enum ValueContainer { */ public init( _ keyPath: KeyPath, - `default`: @autoclosure @escaping () -> V? = nil, + initial: @autoclosure @escaping () -> V? = nil, isIndexed: Bool = false, isTransient: Bool = false, - versionHashModifier: String? = nil, - renamingIdentifier: String? = nil, + versionHashModifier: @autoclosure @escaping () -> String? = nil, + renamingIdentifier: @autoclosure @escaping () -> String? = nil, customGetter: ((_ partialObject: PartialObject) -> V?)? = nil, customSetter: ((_ partialObject: PartialObject, _ newValue: V?) -> Void)? = nil, affectedByKeyPaths: @autoclosure @escaping () -> Set = []) { @@ -336,7 +351,7 @@ public enum ValueContainer { self.keyPath = keyPath self.isIndexed = isIndexed self.isTransient = isTransient - self.defaultValue = { `default`()?.cs_toImportableNativeType() } + self.defaultValue = { initial()?.cs_toImportableNativeType() } self.versionHashModifier = versionHashModifier self.renamingIdentifier = renamingIdentifier self.customGetter = customGetter @@ -409,8 +424,8 @@ public enum ValueContainer { internal let isOptional = true internal let isIndexed: Bool internal let isTransient: Bool - internal let versionHashModifier: String? - internal let renamingIdentifier: String? + internal let versionHashModifier: () -> String? + internal let renamingIdentifier: () -> String? internal let defaultValue: () -> Any? internal let affectedByKeyPaths: () -> Set internal weak var parentObject: CoreStoreObject? @@ -462,457 +477,34 @@ public enum ValueContainer { private let customGetter: ((_ partialObject: PartialObject) -> V?)? private let customSetter: ((_ partialObject: PartialObject, _ newValue: V?) -> Void)? - } -} - -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("title", default: "Mr.") // explicit default value - let name = Value.Required("name") // 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 as an "override" for the default property getter. The closure receives a `PartialObject`, which acts as a fast, type-safe KVC interface for `CoreStoreObject`. The reason a `CoreStoreObject` instance is not passed directly is because the Core Data runtime is not aware of `CoreStoreObject` properties' static typing, and so loading those info everytime KVO invokes this accessor method incurs a cumulative performance hit (especially in KVO-heavy operations such as `ListMonitor` observing.) When accessing the property value from `PartialObject`, make sure to use `PartialObject.primitiveValue(for:)` instead of `PartialObject.value(for:)`, which would unintentionally execute the same closure again recursively. - - parameter customSetter: use this closure as an "override" for the default property setter. The closure receives a `PartialObject`, which acts as a fast, type-safe KVC interface for `CoreStoreObject`. The reason a `CoreStoreObject` instance is not passed directly is because the Core Data runtime is not aware of `CoreStoreObject` properties' static typing, and so loading those info everytime KVO invokes this accessor method incurs a cumulative performance hit (especially in KVO-heavy operations such as `ListMonitor` observing.) When accessing the property value from `PartialObject`, make sure to use `PartialObject.setPrimitiveValue(_:for:)` instead of `PartialObject.setValue(_:for:)`, which would unintentionally execute the same closure again recursively. - - parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`. - */ - public convenience init( - _ keyPath: KeyPath, - isIndexed: Bool = false, - isTransient: Bool = false, - versionHashModifier: String? = nil, - renamingIdentifier: String? = nil, - customGetter: ((_ partialObject: PartialObject) -> V)? = nil, - customSetter: ((_ partialObject: PartialObject, _ newValue: V) -> Void)? = nil, - affectedByKeyPaths: @autoclosure @escaping () -> Set = []) { - self.init( - keyPath, - default: V.cs_emptyValue(), - isIndexed: isIndexed, - isTransient: isTransient, - versionHashModifier: versionHashModifier, - renamingIdentifier: renamingIdentifier, - customGetter: customGetter, - customSetter: customSetter, - affectedByKeyPaths: affectedByKeyPaths - ) - } -} - - -// MARK: - TransformableContainer - -/** - The containing type for transformable properties. Use the `DynamicObject.Transformable` typealias instead for shorter syntax. - ``` - class Animal: CoreStoreObject { - let species = Value.Required("species") - let nickname = Value.Optional("nickname") - let color = Transformable.Optional("color") - } - ``` - */ -public enum TransformableContainer { - - // MARK: - Required - - /** - The containing type for transformable properties. Any type that conforms to `NSCoding & NSCopying` are supported. - ``` - class Animal: CoreStoreObject { - let species = Value.Required("species") - let nickname = Value.Optional("nickname") - let color = Transformable.Optional("color") - } - ``` - - Important: `Transformable.Required` properties are required to be stored properties. Computed properties will be ignored, including `lazy` and `weak` properties. - */ - public final class Required: AttributeProtocol { - /** - Initializes the metadata for the property. - ``` - class Animal: CoreStoreObject { - let species = Value.Required("species") - let color = Transformable.Required( - "color", - default: UIColor.clear, - isTransient: true, - customGetter: Animal.getColor(_:) - ) - } - - private static func getColor(_ partialObject: PartialObject) -> UIColor { - let cachedColor = partialObject.primitiveValue(for: { $0.color }) - if cachedColor != UIColor.clear { - - return cachedColor - } - let color: UIColor - switch partialObject.value(for: { $0.species }) { - - case "Swift": color = UIColor.orange - case "Bulbasaur": color = UIColor.green - default: color = UIColor.black - } - partialObject.setPrimitiveValue(color, for: { $0.color }) - return color - } - ``` - - 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 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 as an "override" for the default property getter. The closure receives a `PartialObject`, which acts as a fast, type-safe KVC interface for `CoreStoreObject`. The reason a `CoreStoreObject` instance is not passed directly is because the Core Data runtime is not aware of `CoreStoreObject` properties' static typing, and so loading those info everytime KVO invokes this accessor method incurs a cumulative performance hit (especially in KVO-heavy operations such as `ListMonitor` observing.) When accessing the property value from `PartialObject`, make sure to use `PartialObject.primitiveValue(for:)` instead of `PartialObject.value(for:)`, which would unintentionally execute the same closure again recursively. - - parameter customSetter: use this closure as an "override" for the default property setter. The closure receives a `PartialObject`, which acts as a fast, type-safe KVC interface for `CoreStoreObject`. The reason a `CoreStoreObject` instance is not passed directly is because the Core Data runtime is not aware of `CoreStoreObject` properties' static typing, and so loading those info everytime KVO invokes this accessor method incurs a cumulative performance hit (especially in KVO-heavy operations such as `ListMonitor` observing.) When accessing the property value from `PartialObject`, make sure to use `PartialObject.setPrimitiveValue(_:for:)` instead of `PartialObject.setValue(_:for:)`, which would unintentionally execute the same closure again recursively. - - parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`. - */ - public init( + // MARK: Deprecated + + @available(*, deprecated: 3.1, renamed: "init(_:initial:isIndexed:isTransient:versionHashModifier:renamingIdentifier:customGetter:customSetter:affectedByKeyPaths:)") + public convenience init( _ keyPath: KeyPath, - `default`: @autoclosure @escaping () -> V, + `default`: @autoclosure @escaping () -> V?, isIndexed: Bool = false, isTransient: Bool = false, - versionHashModifier: String? = nil, - renamingIdentifier: String? = nil, - customGetter: ((_ partialObject: PartialObject) -> V)? = nil, - customSetter: ((_ partialObject: PartialObject, _ newValue: V) -> Void)? = nil, - affectedByKeyPaths: @autoclosure @escaping () -> Set = []) { - - self.keyPath = keyPath - self.defaultValue = `default` - self.isIndexed = isIndexed - self.isTransient = isTransient - self.versionHashModifier = versionHashModifier - self.renamingIdentifier = renamingIdentifier - self.customGetter = customGetter - self.customSetter = customSetter - self.affectedByKeyPaths = affectedByKeyPaths - } - - /** - The property value. - */ - public var value: V { - - get { - - CoreStore.assert( - self.parentObject != nil, - "Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types." - ) - return withExtendedLifetime(self.parentObject! as! O) { (object: O) in - - CoreStore.assert( - object.rawObject!.isRunningInAllowedQueue() == true, - "Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue." - ) - if let customGetter = self.customGetter { - - return customGetter(PartialObject(object.rawObject!)) - } - return object.rawObject!.value(forKey: self.keyPath)! as! V - } - } - set { - - CoreStore.assert( - self.parentObject != nil, - "Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types." - ) - return withExtendedLifetime(self.parentObject! as! O) { (object: O) in - - CoreStore.assert( - object.rawObject!.isRunningInAllowedQueue() == true, - "Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue." - ) - CoreStore.assert( - object.rawObject!.isEditableInContext() == true, - "Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction." - ) - if let customSetter = self.customSetter { - - return customSetter(PartialObject(object.rawObject!), newValue) - } - object.rawObject!.setValue( - newValue, - forKey: self.keyPath - ) - } - } - } - - - // MARK: AttributeProtocol - - internal static var attributeType: NSAttributeType { - - return .transformableAttributeType - } - - public let keyPath: KeyPath - - internal let isOptional = false - internal let isIndexed: Bool - internal let isTransient: Bool - internal let versionHashModifier: String? - internal let renamingIdentifier: String? - internal let defaultValue: () -> Any? - internal let affectedByKeyPaths: () -> Set - internal weak var parentObject: CoreStoreObject? - - internal private(set) lazy var getter: CoreStoreManagedObject.CustomGetter? = cs_lazy { [unowned self] in - - guard let customGetter = self.customGetter else { - - return nil - } - let keyPath = self.keyPath - return { (_ id: Any) -> Any? in - - let rawObject = id as! CoreStoreManagedObject - rawObject.willAccessValue(forKey: keyPath) - defer { - - rawObject.didAccessValue(forKey: keyPath) - } - let value = customGetter(PartialObject(rawObject)) - return value - } - } - - internal private(set) lazy var setter: CoreStoreManagedObject.CustomSetter? = cs_lazy { [unowned self] in - - guard let customSetter = self.customSetter else { - - return nil - } - let keyPath = self.keyPath - return { (_ id: Any, _ newValue: Any?) -> Void in - - let rawObject = id as! CoreStoreManagedObject - rawObject.willChangeValue(forKey: keyPath) - defer { - - rawObject.didChangeValue(forKey: keyPath) - } - customSetter( - PartialObject(rawObject), - newValue as! V - ) - } - } - - - // MARK: Private - - private let customGetter: ((_ partialObject: PartialObject) -> V)? - private let customSetter: ((_ partialObject: PartialObject, _ newValue: V) -> Void)? - } - - - // MARK: - Optional - - /** - 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 nickname = Value.Optional("nickname") - let color = Transformable.Optional("color") - } - ``` - - Important: `Transformable.Optional` properties are required to be stored properties. Computed properties will be ignored, including `lazy` and `weak` properties. - */ - public final class Optional: AttributeProtocol { - - /** - Initializes the metadata for the property. - ``` - class Animal: CoreStoreObject { - let species = Value.Required("species") - let color = Transformable.Optional( - "color", - isTransient: true, - customGetter: Animal.getColor(_:) - ) - } - - private static func getColor(_ partialObject: PartialObject) -> UIColor? { - if let cachedColor = partialObject.primitiveValue(for: { $0.color }) { - return cachedColor - } - let color: UIColor? - switch partialObject.value(for: { $0.species }) { - - case "Swift": color = UIColor.orange - case "Bulbasaur": color = UIColor.green - default: return nil - } - partialObject.setPrimitiveValue(color, for: { $0.color }) - return color - } - ``` - - 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 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 as an "override" for the default property getter. The closure receives a `PartialObject`, which acts as a fast, type-safe KVC interface for `CoreStoreObject`. The reason a `CoreStoreObject` instance is not passed directly is because the Core Data runtime is not aware of `CoreStoreObject` properties' static typing, and so loading those info everytime KVO invokes this accessor method incurs a cumulative performance hit (especially in KVO-heavy operations such as `ListMonitor` observing.) When accessing the property value from `PartialObject`, make sure to use `PartialObject.primitiveValue(for:)` instead of `PartialObject.value(for:)`, which would unintentionally execute the same closure again recursively. - - parameter customSetter: use this closure as an "override" for the default property setter. The closure receives a `PartialObject`, which acts as a fast, type-safe KVC interface for `CoreStoreObject`. The reason a `CoreStoreObject` instance is not passed directly is because the Core Data runtime is not aware of `CoreStoreObject` properties' static typing, and so loading those info everytime KVO invokes this accessor method incurs a cumulative performance hit (especially in KVO-heavy operations such as `ListMonitor` observing.) When accessing the property value from `PartialObject`, make sure to use `PartialObject.setPrimitiveValue(_:for:)` instead of `PartialObject.setValue(_:for:)`, which would unintentionally execute the same closure again recursively. - - parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`. - */ - public init( - _ keyPath: KeyPath, - `default`: @autoclosure @escaping () -> V? = nil, - isIndexed: Bool = false, - isTransient: Bool = false, - versionHashModifier: String? = nil, - renamingIdentifier: String? = nil, + versionHashModifier: @autoclosure @escaping () -> String? = nil, + renamingIdentifier: @autoclosure @escaping () -> String? = nil, customGetter: ((_ partialObject: PartialObject) -> V?)? = nil, customSetter: ((_ partialObject: PartialObject, _ newValue: V?) -> Void)? = nil, affectedByKeyPaths: @autoclosure @escaping () -> Set = []) { - self.keyPath = keyPath - self.defaultValue = `default` - self.isIndexed = isIndexed - self.isTransient = isTransient - self.versionHashModifier = versionHashModifier - self.renamingIdentifier = renamingIdentifier - self.customGetter = customGetter - self.customSetter = customSetter - self.affectedByKeyPaths = affectedByKeyPaths + self.init( + keyPath, + initial: `default`, + isIndexed: isIndexed, + isTransient: isTransient, + versionHashModifier: versionHashModifier, + renamingIdentifier: renamingIdentifier, + customGetter: customGetter, + customSetter: customSetter, + affectedByKeyPaths: affectedByKeyPaths + ) } - - /** - The property value. - */ - public var value: V? { - - get { - - CoreStore.assert( - self.parentObject != nil, - "Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types." - ) - return withExtendedLifetime(self.parentObject! as! O) { (object: O) in - - CoreStore.assert( - object.rawObject!.isRunningInAllowedQueue() == true, - "Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue." - ) - if let customGetter = self.customGetter { - - return customGetter(PartialObject(object.rawObject!)) - } - return object.rawObject!.value(forKey: self.keyPath) as! V? - } - } - set { - - CoreStore.assert( - self.parentObject != nil, - "Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types." - ) - return withExtendedLifetime(self.parentObject! as! O) { (object: O) in - - CoreStore.assert( - object.rawObject!.isRunningInAllowedQueue() == true, - "Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue." - ) - CoreStore.assert( - object.rawObject!.isEditableInContext() == true, - "Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction." - ) - if let customSetter = self.customSetter { - - return customSetter(PartialObject(object.rawObject!), newValue) - } - object.rawObject!.setValue( - newValue, - forKey: self.keyPath - ) - } - } - } - - - // MARK: AttributeProtocol - - internal static var attributeType: NSAttributeType { - - return .transformableAttributeType - } - - public let keyPath: KeyPath - - internal let isOptional = true - internal let isIndexed: Bool - internal let isTransient: Bool - internal let versionHashModifier: String? - internal let renamingIdentifier: String? - internal let defaultValue: () -> Any? - internal let affectedByKeyPaths: () -> Set - internal weak var parentObject: CoreStoreObject? - - internal private(set) lazy var getter: CoreStoreManagedObject.CustomGetter? = cs_lazy { [unowned self] in - - guard let customGetter = self.customGetter else { - - return nil - } - let keyPath = self.keyPath - return { (_ id: Any) -> Any? in - - let rawObject = id as! CoreStoreManagedObject - rawObject.willAccessValue(forKey: keyPath) - defer { - - rawObject.didAccessValue(forKey: keyPath) - } - let value = customGetter(PartialObject(rawObject)) - return value - } - } - - internal private(set) lazy var setter: CoreStoreManagedObject.CustomSetter? = cs_lazy { [unowned self] in - - guard let customSetter = self.customSetter else { - - return nil - } - let keyPath = self.keyPath - return { (_ id: Any, _ newValue: Any?) -> Void in - - let rawObject = id as! CoreStoreManagedObject - rawObject.willChangeValue(forKey: keyPath) - defer { - - rawObject.didChangeValue(forKey: keyPath) - } - customSetter( - PartialObject(rawObject), - newValue as! V? - ) - } - } - - - // MARK: Private - - private let customGetter: ((_ partialObject: PartialObject) -> V?)? - private let customSetter: ((_ partialObject: PartialObject, _ newValue: V?) -> Void)? } } @@ -1122,104 +714,3 @@ extension ValueContainer.Optional { return property.value == property2.value } } - -extension TransformableContainer.Required { - - /** - Assigns a transformable value to the property. The operation - ``` - animal.color .= UIColor.red - ``` - is equivalent to - ``` - animal.color.value = UIColor.red - ``` - */ - public static func .= (_ property: TransformableContainer.Required, _ newValue: V) { - - property.value = newValue - } - - /** - Assigns a transformable value from another property. The operation - ``` - animal.nickname .= anotherAnimal.species - ``` - is equivalent to - ``` - animal.nickname.value = anotherAnimal.species.value - ``` - */ - public static func .= (_ property: TransformableContainer.Required, _ property2: TransformableContainer.Required) { - - property.value = property2.value - } -} - -extension TransformableContainer.Optional { - - /** - Assigns an optional transformable value to the property. The operation - ``` - animal.color .= UIColor.red - ``` - is equivalent to - ``` - animal.color.value = UIColor.red - ``` - */ - public static func .= (_ property: TransformableContainer.Optional, _ newValue: V?) { - - property.value = newValue - } - - /** - Assigns an optional transformable value from another property. The operation - ``` - animal.color .= anotherAnimal.color - ``` - is equivalent to - ``` - animal.color.value = anotherAnimal.color.value - ``` - */ - public static func .= (_ property: TransformableContainer.Optional, _ property2: TransformableContainer.Optional) { - - property.value = property2.value - } - - /** - Assigns a transformable value from another property. The operation - ``` - animal.color .= anotherAnimal.color - ``` - is equivalent to - ``` - animal.color.value = anotherAnimal.color.value - ``` - */ - public static func .= (_ property: TransformableContainer.Optional, _ property2: TransformableContainer.Required) { - - property.value = property2.value - } -} - - -// MARK: - AttributeProtocol - -internal protocol AttributeProtocol: class { - - static var attributeType: NSAttributeType { get } - - var keyPath: KeyPath { get } - var isOptional: Bool { get } - var isIndexed: Bool { get } - var isTransient: Bool { get } - var versionHashModifier: String? { get } - var renamingIdentifier: String? { get } - var defaultValue: () -> Any? { get } - var affectedByKeyPaths: () -> Set { get } - weak var parentObject: CoreStoreObject? { get set } - var getter: CoreStoreManagedObject.CustomGetter? { get } - var setter: CoreStoreManagedObject.CustomSetter? { get } -} From 3bd459bb1a17622a1a959b7e093f9593ca5b4fa1 Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Wed, 16 Aug 2017 20:09:09 +0900 Subject: [PATCH 3/6] updated docs --- Sources/CoreStore+Transaction.swift | 2 +- Sources/DataStack+Transaction.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/CoreStore+Transaction.swift b/Sources/CoreStore+Transaction.swift index de8386a..60e36e8 100644 --- a/Sources/CoreStore+Transaction.swift +++ b/Sources/CoreStore+Transaction.swift @@ -54,7 +54,7 @@ public extension CoreStore { } /** - Using the `defaultStack`, performs a transaction synchronously where `NSManagedObject` or `CoreStoreObject` creates, updates, and deletes can be made. The changes are commited automatically after the `task` closure returns. On success, the value returned from closure will be the return value of `perform(synchronous:)`. Any errors thrown from inside the `task` will be rethrown from `perform(synchronous:)`. To cancel/rollback changes, call `try transaction.cancel()`, which throws a `CoreStoreError.userCancelled`. + Using the `defaultStack`, performs a transaction synchronously where `NSManagedObject` or `CoreStoreObject` creates, updates, and deletes can be made. The changes are commited automatically after the `task` closure returns. On success, the value returned from closure will be the return value of `perform(synchronous:)`. Any errors thrown from inside the `task` will be thrown from `perform(synchronous:)`. To cancel/rollback changes, call `try transaction.cancel()`, which throws a `CoreStoreError.userCancelled`. - parameter task: the synchronous non-escaping closure where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent `NSManagedObjectContext`. - parameter waitForAllObservers: When `true`, this method waits for all observers to be notified of the changes before returning. This results in more predictable data update order, but may risk triggering deadlocks. When `false`, this method does not wait for observers to be notified of the changes before returning. This results in lower risk for deadlocks, but the updated data may not have been propagated to the `DataStack` after returning. Defaults to `true`. diff --git a/Sources/DataStack+Transaction.swift b/Sources/DataStack+Transaction.swift index 52a09c7..8b970e0 100644 --- a/Sources/DataStack+Transaction.swift +++ b/Sources/DataStack+Transaction.swift @@ -95,7 +95,7 @@ public extension DataStack { } /** - Performs a transaction synchronously where `NSManagedObject` or `CoreStoreObject` creates, updates, and deletes can be made. The changes are commited automatically after the `task` closure returns. On success, the value returned from closure will be the return value of `perform(synchronous:)`. Any errors thrown from inside the `task` will be rethrown from `perform(synchronous:)`. To cancel/rollback changes, call `try transaction.cancel()`, which throws a `CoreStoreError.userCancelled`. + Performs a transaction synchronously where `NSManagedObject` or `CoreStoreObject` creates, updates, and deletes can be made. The changes are commited automatically after the `task` closure returns. On success, the value returned from closure will be the return value of `perform(synchronous:)`. Any errors thrown from inside the `task` will be thrown from `perform(synchronous:)`. To cancel/rollback changes, call `try transaction.cancel()`, which throws a `CoreStoreError.userCancelled`. - parameter task: the synchronous non-escaping closure where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent `NSManagedObjectContext`. - parameter waitForAllObservers: When `true`, this method waits for all observers to be notified of the changes before returning. This results in more predictable data update order, but may risk triggering deadlocks. When `false`, this method does not wait for observers to be notified of the changes before returning. This results in lower risk for deadlocks, but the updated data may not have been propagated to the `DataStack` after returning. Defaults to `true`. From 9685f0aef2e7553b12a034c184c0ca25d4020e0a Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Wed, 16 Aug 2017 20:21:26 +0900 Subject: [PATCH 4/6] #191 --- Sources/NSManagedObjectContext+Querying.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/NSManagedObjectContext+Querying.swift b/Sources/NSManagedObjectContext+Querying.swift index 084e331..8d9156b 100644 --- a/Sources/NSManagedObjectContext+Querying.swift +++ b/Sources/NSManagedObjectContext+Querying.swift @@ -62,7 +62,7 @@ extension NSManagedObjectContext: FetchableSource, QueryableSource { return object } - return T.cs_fromRaw(object: existingRawObject) + return type(of: object).cs_fromRaw(object: existingRawObject) } catch { From 0f10bc33490f3bd4aa56e062319a86962cb4ecf6 Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Wed, 16 Aug 2017 20:26:22 +0900 Subject: [PATCH 5/6] version bump --- Sources/Info.plist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Info.plist b/Sources/Info.plist index 8492747..5284837 100644 --- a/Sources/Info.plist +++ b/Sources/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 4.1 + 4.1.0 CFBundleSignature ???? CFBundleVersion From 86be046c9fff88e2a5d999c2e8332f81b0013239 Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Wed, 16 Aug 2017 20:43:10 +0900 Subject: [PATCH 6/6] updated podspec --- CoreStore.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CoreStore.podspec b/CoreStore.podspec index e01a6fd..55a119c 100644 --- a/CoreStore.podspec +++ b/CoreStore.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "CoreStore" - s.version = "4.0.5" + s.version = "4.1.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"