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 } -}