diff --git a/CoreStore.xcodeproj/project.pbxproj b/CoreStore.xcodeproj/project.pbxproj index 0801a46..c34ff5f 100644 --- a/CoreStore.xcodeproj/project.pbxproj +++ b/CoreStore.xcodeproj/project.pbxproj @@ -242,6 +242,10 @@ B53B27601EE3B92E00E9B352 /* CoreStoreManagedObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B53B275E1EE3B92E00E9B352 /* CoreStoreManagedObject.swift */; }; B53B27611EE3B92E00E9B352 /* CoreStoreManagedObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B53B275E1EE3B92E00E9B352 /* CoreStoreManagedObject.swift */; }; B53B27621EE3B92E00E9B352 /* CoreStoreManagedObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B53B275E1EE3B92E00E9B352 /* CoreStoreManagedObject.swift */; }; + B53CA9A21EF1EF1600E0F440 /* PartialObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B53CA9A11EF1EF1600E0F440 /* PartialObject.swift */; }; + B53CA9A31EF1EF1600E0F440 /* PartialObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B53CA9A11EF1EF1600E0F440 /* PartialObject.swift */; }; + B53CA9A41EF1EF1600E0F440 /* PartialObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B53CA9A11EF1EF1600E0F440 /* PartialObject.swift */; }; + B53CA9A51EF1EF1600E0F440 /* PartialObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B53CA9A11EF1EF1600E0F440 /* PartialObject.swift */; }; B53FB9FE1CAB2D2F00F0D40A /* CSMigrationResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = B53FB9FD1CAB2D2F00F0D40A /* CSMigrationResult.swift */; }; B53FBA001CAB2D2F00F0D40A /* CSMigrationResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = B53FB9FD1CAB2D2F00F0D40A /* CSMigrationResult.swift */; }; B53FBA011CAB2D2F00F0D40A /* CSMigrationResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = B53FB9FD1CAB2D2F00F0D40A /* CSMigrationResult.swift */; }; @@ -757,6 +761,7 @@ B533C4DA1D7D4BFA001383CB /* DispatchQueue+CoreStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DispatchQueue+CoreStore.swift"; sourceTree = ""; }; B538BA701D15B3E30003A766 /* CoreStoreBridge.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CoreStoreBridge.m; sourceTree = ""; }; B53B275E1EE3B92E00E9B352 /* CoreStoreManagedObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreStoreManagedObject.swift; sourceTree = ""; }; + B53CA9A11EF1EF1600E0F440 /* PartialObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PartialObject.swift; sourceTree = ""; }; B53FB9FD1CAB2D2F00F0D40A /* CSMigrationResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSMigrationResult.swift; sourceTree = ""; }; B53FBA031CAB300C00F0D40A /* CSMigrationType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSMigrationType.swift; sourceTree = ""; }; B53FBA0A1CAB5E6500F0D40A /* CSCoreStore+Migrating.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CSCoreStore+Migrating.swift"; sourceTree = ""; }; @@ -1229,6 +1234,7 @@ isa = PBXGroup; children = ( B5D339D71E9489AB00C880DE /* CoreStoreObject.swift */, + B53CA9A11EF1EF1600E0F440 /* PartialObject.swift */, B52F74391E9B8724005F3DAC /* Dynamic Schema */, B5D339DC1E9489C700C880DE /* DynamicObject.swift */, B52F742E1E9B50D0005F3DAC /* SchemaHistory.swift */, @@ -1826,6 +1832,7 @@ B5FAD6AE1B518DCB00714891 /* CoreStore+Migration.swift in Sources */, B5E84EE71AFF84610064E85B /* CoreStore+Logging.swift in Sources */, B546F9731C9C553300D5AC55 /* SetupResult.swift in Sources */, + B53CA9A21EF1EF1600E0F440 /* PartialObject.swift in Sources */, B56007111B3F6BD500A9A8F9 /* Into.swift in Sources */, B5E84F111AFF847B0064E85B /* Select.swift in Sources */, B51260931E9B28F100402229 /* EntityIdentifier.swift in Sources */, @@ -2011,6 +2018,7 @@ 82BA18A31C4BBD2200A0916E /* DataStack.swift in Sources */, 82BA18C81C4BBD5900A0916E /* MigrationChain.swift in Sources */, B546F9741C9C553300D5AC55 /* SetupResult.swift in Sources */, + B53CA9A31EF1EF1600E0F440 /* PartialObject.swift in Sources */, 82BA18B11C4BBD3100A0916E /* SaveResult.swift in Sources */, 82BA18DD1C4BBE1400A0916E /* NSFetchedResultsController+Convenience.swift in Sources */, B51260941E9B28F100402229 /* EntityIdentifier.swift in Sources */, @@ -2196,6 +2204,7 @@ B52DD1961BE1F92500949AFE /* DataStack.swift in Sources */, B5ECDBFD1CA804FD00C7F112 /* NSManagedObjectContext+ObjectiveC.swift in Sources */, B52DD1BD1BE1F94300949AFE /* NSManagedObject+Convenience.swift in Sources */, + B53CA9A51EF1EF1600E0F440 /* PartialObject.swift in Sources */, B52DD1AD1BE1F93900949AFE /* Where.swift in Sources */, B53FBA1C1CAB63E200F0D40A /* NSManagedObject+ObjectiveC.swift in Sources */, B51260961E9B28F100402229 /* EntityIdentifier.swift in Sources */, @@ -2381,6 +2390,7 @@ B56321A81BD65219006C9394 /* NSManagedObject+Convenience.swift in Sources */, B546F9751C9C553300D5AC55 /* SetupResult.swift in Sources */, B56321981BD65216006C9394 /* Where.swift in Sources */, + B53CA9A41EF1EF1600E0F440 /* PartialObject.swift in Sources */, B5202CFD1C046E8400DED140 /* NSFetchedResultsController+Convenience.swift in Sources */, B5FE4DA91C84FB4400FA6A91 /* InMemoryStore.swift in Sources */, B51260951E9B28F100402229 /* EntityIdentifier.swift in Sources */, diff --git a/Sources/CoreStoreObject.swift b/Sources/CoreStoreObject.swift index 910644f..2ec6c4c 100644 --- a/Sources/CoreStoreObject.swift +++ b/Sources/CoreStoreObject.swift @@ -135,3 +135,23 @@ open /*abstract*/ class CoreStoreObject: DynamicObject, Hashable { } } } + + +// MARK: - DynamicObject where Self: CoreStoreObject + +public extension DynamicObject where Self: CoreStoreObject { + + public func partialObject() -> PartialObject { + + CoreStore.assert( + !self.isMeta, + "Attempted to create a \(cs_typeName(PartialObject.self)) from a meta object. Meta objects are only used for querying keyPaths and infering types." + ) + return PartialObject(self.rawObject!) + } + + internal static var meta: Self { + + return self.init(asMeta: ()) + } +} diff --git a/Sources/DynamicObject.swift b/Sources/DynamicObject.swift index db733a3..c48bdba 100644 --- a/Sources/DynamicObject.swift +++ b/Sources/DynamicObject.swift @@ -155,14 +155,3 @@ extension CoreStoreObject { return self.rawObject! } } - - -// MARK: - Internal - -internal extension DynamicObject where Self: CoreStoreObject { - - internal static var meta: Self { - - return self.init(asMeta: ()) - } -} diff --git a/Sources/PartialObject.swift b/Sources/PartialObject.swift new file mode 100644 index 0000000..a47cebf --- /dev/null +++ b/Sources/PartialObject.swift @@ -0,0 +1,135 @@ +// +// PartialObject.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: - PartialObject + +public struct PartialObject { + + public func completeObject() -> O { + + return O.cs_fromRaw(object: self.rawObject) + } + + public func value(for property: (O) -> ValueContainer.Required) -> V { + + return V.cs_fromImportableNativeType( + self.rawObject.value(forKey: property(O.meta).keyPath)! as! V.ImportableNativeType + )! + } + + public func value(for property: (O) -> ValueContainer.Optional) -> V? { + + return (self.rawObject.value(forKey: property(O.meta).keyPath) as! V.ImportableNativeType?) + .flatMap(V.cs_fromImportableNativeType) + } + + public func value(for property: (O) -> TransformableContainer.Required) -> V { + + return self.rawObject.value(forKey: property(O.meta).keyPath)! as! V + } + + public func value(for property: (O) -> TransformableContainer.Optional) -> V? { + + return self.rawObject.value(forKey: property(O.meta).keyPath) as! V? + } + + public func setValue(_ value: V, for property: (O) -> ValueContainer.Required) { + + self.rawObject.setValue( + value.cs_toImportableNativeType(), + forKey: property(O.meta).keyPath + ) + } + + public func setValue(_ value: V?, for property: (O) -> ValueContainer.Optional) { + + self.rawObject.setValue( + value?.cs_toImportableNativeType(), + forKey: property(O.meta).keyPath + ) + } + + public func setValue(_ value: V, for property: (O) -> TransformableContainer.Required) { + + self.rawObject.setValue( + value, + forKey: property(O.meta).keyPath + ) + } + + public func setValue(_ value: V?, for property: (O) -> TransformableContainer.Optional) { + + self.rawObject.setValue( + value, + forKey: property(O.meta).keyPath + ) + } + + public func setPrimitiveValue(_ value: V, for property: (O) -> ValueContainer.Required) { + + self.rawObject.setPrimitiveValue( + value.cs_toImportableNativeType(), + forKey: property(O.meta).keyPath + ) + } + + public func setPrimitiveValue(_ value: V?, for property: (O) -> ValueContainer.Optional) { + + self.rawObject.setPrimitiveValue( + value?.cs_toImportableNativeType(), + forKey: property(O.meta).keyPath + ) + } + + public func setPrimitiveValue(_ value: V, for property: (O) -> TransformableContainer.Required) { + + self.rawObject.setPrimitiveValue( + value, + forKey: property(O.meta).keyPath + ) + } + + public func setPrimitiveValue(_ value: V?, for property: (O) -> TransformableContainer.Optional) { + + self.rawObject.setPrimitiveValue( + value, + forKey: property(O.meta).keyPath + ) + } + + + // MARK: Internal + + internal let rawObject: NSManagedObject + + internal init(_ rawObject: NSManagedObject) { + + self.rawObject = rawObject + } +} diff --git a/Sources/Value.swift b/Sources/Value.swift index d1ef2c4..e8f1575 100644 --- a/Sources/Value.swift +++ b/Sources/Value.swift @@ -123,8 +123,8 @@ public enum ValueContainer { isTransient: Bool = false, versionHashModifier: String? = nil, renamingIdentifier: String? = nil, - customGetter: ((_ `self`: O, _ getValue: () -> V) -> V)? = nil, - customSetter: ((_ `self`: O, _ setValue: (_ finalNewValue: V) -> Void, _ originalNewValue: V) -> Void)? = nil, + customGetter: ((_ partialObject: PartialObject) -> V)? = nil, + customSetter: ((_ partialObject: PartialObject, _ newValue: V) -> Void)? = nil, affectedByKeyPaths: @autoclosure @escaping () -> Set = []) { self.keyPath = keyPath @@ -155,16 +155,13 @@ public enum ValueContainer { object.rawObject!.isRunningInAllowedQueue() == true, "Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue." ) - let customGetter = (self.customGetter ?? { $1() }) - return customGetter( - object, - { () -> V in - - return V.cs_fromImportableNativeType( - object.rawObject!.value(forKey: self.keyPath)! as! V.ImportableNativeType - )! - } - ) + if let customGetter = self.customGetter { + + return customGetter(PartialObject(object.rawObject!)) + } + return V.cs_fromImportableNativeType( + object.rawObject!.value(forKey: self.keyPath)! as! V.ImportableNativeType + )! } } set { @@ -183,17 +180,13 @@ public enum ValueContainer { object.rawObject!.isEditableInContext() == true, "Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction." ) - let customSetter = (self.customSetter ?? { $1($2) }) - customSetter( - object, - { (newValue: V) -> Void in - - object.rawObject!.setValue( - newValue.cs_toImportableNativeType(), - forKey: self.keyPath - ) - }, - newValue + if let customSetter = self.customSetter { + + return customSetter(PartialObject(object.rawObject!), newValue) + } + return object.rawObject!.setValue( + newValue.cs_toImportableNativeType(), + forKey: self.keyPath ) } } @@ -274,15 +267,12 @@ public enum ValueContainer { return { (_ id: Any) -> Any? in let rawObject = id as! CoreStoreManagedObject - let value = customGetter( - O.cs_fromRaw(object: rawObject), - { - rawObject.getValue( - forKvcKey: keyPath, - didGetValue: { V.cs_fromImportableNativeType($0 as! V.ImportableNativeType!)! } - ) - } - ) + rawObject.willAccessValue(forKey: keyPath) + defer { + + rawObject.didAccessValue(forKey: keyPath) + } + let value = customGetter(PartialObject(rawObject)) return value.cs_toImportableNativeType() } } @@ -297,16 +287,13 @@ public enum ValueContainer { return { (_ id: Any, _ newValue: Any?) -> Void in let rawObject = id as! CoreStoreManagedObject + rawObject.willChangeValue(forKey: keyPath) + defer { + + rawObject.didChangeValue(forKey: keyPath) + } customSetter( - O.cs_fromRaw(object: rawObject), - { (userValue: V) -> Void in - - rawObject.setValue( - userValue, - forKvcKey: keyPath, - willSetValue: { $0.cs_toImportableNativeType() } - ) - }, + PartialObject(rawObject), V.cs_fromImportableNativeType(newValue as! V.ImportableNativeType)! ) } @@ -315,8 +302,8 @@ public enum ValueContainer { // MARK: Private - private let customGetter: ((_ `self`: O, _ getValue: () -> V) -> V)? - private let customSetter: ((_ `self`: O, _ setValue: (V) -> Void, _ newValue: V) -> Void)? + private let customGetter: ((_ partialObject: PartialObject) -> V)? + private let customSetter: ((_ partialObject: PartialObject, _ newValue: V) -> Void)? } @@ -370,8 +357,8 @@ public enum ValueContainer { isTransient: Bool = false, versionHashModifier: String? = nil, renamingIdentifier: String? = nil, - customGetter: ((_ `self`: O, _ getValue: () -> V?) -> V?)? = nil, - customSetter: ((_ `self`: O, _ setValue: (_ finalNewValue: V?) -> Void, _ originalNewValue: V?) -> Void)? = nil, + customGetter: ((_ partialObject: PartialObject) -> V?)? = nil, + customSetter: ((_ partialObject: PartialObject, _ newValue: V?) -> Void)? = nil, affectedByKeyPaths: @autoclosure @escaping () -> Set = []) { self.keyPath = keyPath @@ -402,15 +389,12 @@ public enum ValueContainer { object.rawObject!.isRunningInAllowedQueue() == true, "Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue." ) - let customGetter = (self.customGetter ?? { $1() }) - return customGetter( - object, - { () -> V? in - - return (object.rawObject!.value(forKey: self.keyPath) as! V.ImportableNativeType?) - .flatMap(V.cs_fromImportableNativeType) - } - ) + if let customGetter = self.customGetter { + + return customGetter(PartialObject(object.rawObject!)) + } + return (object.rawObject!.value(forKey: self.keyPath) as! V.ImportableNativeType?) + .flatMap(V.cs_fromImportableNativeType) } } set { @@ -429,17 +413,13 @@ public enum ValueContainer { object.rawObject!.isEditableInContext() == true, "Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction." ) - let customSetter = (self.customSetter ?? { $1($2) }) - customSetter( - object, - { (newValue: V?) -> Void in - - object.rawObject!.setValue( - newValue?.cs_toImportableNativeType(), - forKey: self.keyPath - ) - }, - newValue + if let customSetter = self.customSetter { + + return customSetter(PartialObject(object.rawObject!), newValue) + } + object.rawObject!.setValue( + newValue?.cs_toImportableNativeType(), + forKey: self.keyPath ) } } @@ -518,15 +498,12 @@ public enum ValueContainer { return { (_ id: Any) -> Any? in let rawObject = id as! CoreStoreManagedObject - let value = customGetter( - O.cs_fromRaw(object: rawObject), - { - rawObject.getValue( - forKvcKey: keyPath, - didGetValue: { ($0 as! V.ImportableNativeType?).flatMap(V.cs_fromImportableNativeType) } - ) - } - ) + rawObject.willAccessValue(forKey: keyPath) + defer { + + rawObject.didAccessValue(forKey: keyPath) + } + let value = customGetter(PartialObject(rawObject)) return value?.cs_toImportableNativeType() } } @@ -541,16 +518,13 @@ public enum ValueContainer { return { (_ id: Any, _ newValue: Any?) -> Void in let rawObject = id as! CoreStoreManagedObject + rawObject.willChangeValue(forKey: keyPath) + defer { + + rawObject.didChangeValue(forKey: keyPath) + } customSetter( - O.cs_fromRaw(object: rawObject), - { (userValue: V?) -> Void in - - rawObject.setValue( - userValue, - forKvcKey: keyPath, - willSetValue: { $0?.cs_toImportableNativeType() } - ) - }, + PartialObject(rawObject), (newValue as! V.ImportableNativeType?).flatMap(V.cs_fromImportableNativeType) ) } @@ -559,8 +533,8 @@ public enum ValueContainer { // MARK: Private - private let customGetter: ((_ `self`: O, _ getValue: () -> V?) -> V?)? - private let customSetter: ((_ `self`: O, _ setValue: (V?) -> Void, _ newValue: V?) -> Void)? + private let customGetter: ((_ partialObject: PartialObject) -> V?)? + private let customSetter: ((_ partialObject: PartialObject, _ newValue: V?) -> Void)? } } @@ -593,7 +567,7 @@ public extension ValueContainer.Required where V: EmptyableAttributeType { isTransient: Bool = false, versionHashModifier: String? = nil, renamingIdentifier: String? = nil, - customGetter: ((_ `self`: O, _ getValue: () -> V) -> V)? = nil, + customGetter: ((_ `self`: PartialObject, _ getValue: () -> V) -> V)? = nil, customSetter: ((_ `self`: O, _ setValue: (_ finalNewValue: V) -> Void, _ originalNewValue: V) -> Void)? = nil, affectedByKeyPaths: @autoclosure @escaping () -> Set = []) { @@ -670,7 +644,7 @@ public enum TransformableContainer { isTransient: Bool = false, versionHashModifier: String? = nil, renamingIdentifier: String? = nil, - customGetter: ((_ `self`: O, _ getValue: () -> V) -> V)? = nil, + customGetter: ((_ `self`: PartialObject, _ getValue: () -> V) -> V)? = nil, customSetter: ((_ `self`: O, _ setValue: (_ finalNewValue: V) -> Void, _ originalNewValue: V) -> Void)? = nil, affectedByKeyPaths: @autoclosure @escaping () -> Set = []) { @@ -704,7 +678,7 @@ public enum TransformableContainer { ) let customGetter = (self.customGetter ?? { $1() }) return customGetter( - object, + PartialObject(object.rawObject!), { () -> V in return object.rawObject!.value(forKey: self.keyPath)! as! V @@ -818,7 +792,7 @@ public enum TransformableContainer { let rawObject = id as! CoreStoreManagedObject return customGetter( - O.cs_fromRaw(object: rawObject), + PartialObject(rawObject), { rawObject.getValue(forKvcKey: keyPath) as! V } ) } @@ -848,7 +822,7 @@ public enum TransformableContainer { // MARK: Private - private let customGetter: ((_ `self`: O, _ getValue: () -> V) -> V)? + private let customGetter: ((_ `self`: PartialObject, _ getValue: () -> V) -> V)? private let customSetter: ((_ `self`: O, _ setValue: (V) -> Void, _ newValue: V) -> Void)? } @@ -897,7 +871,7 @@ public enum TransformableContainer { isTransient: Bool = false, versionHashModifier: String? = nil, renamingIdentifier: String? = nil, - customGetter: ((_ `self`: O, _ getValue: () -> V?) -> V?)? = nil, + customGetter: ((_ `self`: PartialObject, _ getValue: () -> V?) -> V?)? = nil, customSetter: ((_ `self`: O, _ setValue: (_ finalNewValue: V?) -> Void, _ originalNewValue: V?) -> Void)? = nil, affectedByKeyPaths: @autoclosure @escaping () -> Set = []) { @@ -931,7 +905,7 @@ public enum TransformableContainer { ) let customGetter = (self.customGetter ?? { $1() }) return customGetter( - object, + PartialObject(object.rawObject!), { () -> V? in object.rawObject!.value(forKey: self.keyPath) as! V? @@ -1045,7 +1019,7 @@ public enum TransformableContainer { let rawObject = id as! CoreStoreManagedObject return customGetter( - O.cs_fromRaw(object: rawObject), + PartialObject(rawObject), { rawObject.getValue(forKvcKey: keyPath) as! V? } ) } @@ -1083,7 +1057,7 @@ public enum TransformableContainer { // MARK: Private - private let customGetter: ((_ `self`: O, _ getValue: () -> V?) -> V?)? + private let customGetter: ((_ `self`: PartialObject, _ getValue: () -> V?) -> V?)? private let customSetter: ((_ `self`: O, _ setValue: (V?) -> Void, _ newValue: V?) -> Void)? } }