diff --git a/CoreStore.podspec b/CoreStore.podspec index e8e2abf..16792c5 100644 --- a/CoreStore.podspec +++ b/CoreStore.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "CoreStore" - s.version = "1.1.2" + s.version = "1.2.1" s.license = "MIT" s.summary = "Simple, elegant, and smart Core Data programming with Swift" s.homepage = "https://github.com/JohnEstropia/CoreStore" diff --git a/CoreStore/Importing Data/BaseDataTransaction+Importing.swift b/CoreStore/Importing Data/BaseDataTransaction+Importing.swift index 991628d..8bc5598 100644 --- a/CoreStore/Importing Data/BaseDataTransaction+Importing.swift +++ b/CoreStore/Importing Data/BaseDataTransaction+Importing.swift @@ -31,16 +31,14 @@ public protocol ImportableObject: class { typealias ImportSource - static func shouldImportFromSource(source: ImportSource) -> Bool + static func shouldInsertFromImportSource(source: ImportSource, inTransaction transaction: BaseDataTransaction) -> Bool - func didInsertFromImportSource(source: ImportSource) throws - - func updateFromImportSource(source: ImportSource) throws + func didInsertFromImportSource(source: ImportSource, inTransaction transaction: BaseDataTransaction) throws } public extension ImportableObject { - static func shouldImportFromSource(source: ImportSource) -> Bool { + static func shouldInsertFromImportSource(source: ImportSource, inTransaction transaction: BaseDataTransaction) -> Bool { return true } @@ -55,7 +53,25 @@ public protocol ImportableUniqueObject: ImportableObject { var uniqueIDValue: UniqueIDType { get set } - static func uniqueIDFromImportSource(source: ImportSource) throws -> UniqueIDType + static func uniqueIDFromImportSource(source: ImportSource, inTransaction transaction: BaseDataTransaction) throws -> UniqueIDType? + + static func shouldUpdateFromImportSource(source: ImportSource, inTransaction transaction: BaseDataTransaction) -> Bool + + func updateFromImportSource(source: ImportSource, inTransaction transaction: BaseDataTransaction) throws +} + + +public extension ImportableUniqueObject { + + static func shouldUpdateFromImportSource(source: ImportSource, inTransaction transaction: BaseDataTransaction) -> Bool { + + return true + } + + func didInsertFromImportSource(source: ImportSource, inTransaction transaction: BaseDataTransaction) throws { + + try self.updateFromImportSource(source, inTransaction: transaction) + } } @@ -72,13 +88,13 @@ public extension BaseDataTransaction { return try autoreleasepool { - if !T.shouldImportFromSource(source) { + guard T.shouldInsertFromImportSource(source, inTransaction: self) else { return nil } let object = self.create(into) - try object.didInsertFromImportSource(source) + try object.didInsertFromImportSource(source, inTransaction: self) return object } } @@ -96,10 +112,15 @@ public extension BaseDataTransaction { for source in sourceArray { + guard T.shouldInsertFromImportSource(source, inTransaction: self) else { + + continue + } + try autoreleasepool { let object = self.create(into) - try object.didInsertFromImportSource(source) + try object.didInsertFromImportSource(source, inTransaction: self) } } } @@ -120,10 +141,15 @@ public extension BaseDataTransaction { var objects = [T]() for source in sourceArray { + guard T.shouldInsertFromImportSource(source, inTransaction: self) else { + + continue + } + try autoreleasepool { let object = self.create(into) - try object.didInsertFromImportSource(source) + try object.didInsertFromImportSource(source, inTransaction: self) objects.append(object) } @@ -143,24 +169,27 @@ public extension BaseDataTransaction { return try autoreleasepool { - if !T.shouldImportFromSource(source) { + let uniqueIDKeyPath = T.uniqueIDKeyPath + guard let uniqueIDValue = try T.uniqueIDFromImportSource(source, inTransaction: self) else { return nil } - let uniqueIDKeyPath = T.uniqueIDKeyPath - let uniqueIDValue = try T.uniqueIDFromImportSource(source) - if let object = self.fetchOne(From(T), Where(uniqueIDKeyPath, isEqualTo: uniqueIDValue)) { - try object.updateFromImportSource(source) + try object.updateFromImportSource(source, inTransaction: self) return object } else { + guard T.shouldInsertFromImportSource(source, inTransaction: self) else { + + return nil + } + let object = self.create(into) object.uniqueIDValue = uniqueIDValue - try object.didInsertFromImportSource(source) + try object.didInsertFromImportSource(source, inTransaction: self) return object } } @@ -169,7 +198,7 @@ public extension BaseDataTransaction { func importUniqueObjects( into: Into, sourceArray: [T.ImportSource], - preProcess: ((mapping: [T.UniqueIDType: T.ImportSource]) throws -> Void)? = nil) throws { + preProcess: ((inout mapping: [T.UniqueIDType: T.ImportSource]) throws -> Void)? = nil) throws { CoreStore.assert( self.bypassesQueueing || self.transactionQueue.isCurrentExecutionContext(), @@ -183,12 +212,11 @@ public extension BaseDataTransaction { try autoreleasepool { - if !T.shouldImportFromSource(source) { + guard let uniqueIDValue = try T.uniqueIDFromImportSource(source, inTransaction: self) else { return } - let uniqueIDValue = try T.uniqueIDFromImportSource(source) mapping[uniqueIDValue] = source } } @@ -197,7 +225,7 @@ public extension BaseDataTransaction { try autoreleasepool { - try preProcess(mapping: mapping) + try preProcess(mapping: &mapping) } } @@ -206,7 +234,14 @@ public extension BaseDataTransaction { try autoreleasepool { let uniqueIDValue = object.uniqueIDValue - try object.updateFromImportSource(mapping.removeValueForKey(uniqueIDValue)!) + + guard let source = mapping.removeValueForKey(uniqueIDValue) + where T.shouldUpdateFromImportSource(source, inTransaction: self) else { + + return + } + + try object.updateFromImportSource(source, inTransaction: self) } } @@ -214,9 +249,14 @@ public extension BaseDataTransaction { try autoreleasepool { + guard T.shouldInsertFromImportSource(source, inTransaction: self) else { + + return + } + let object = self.create(into) object.uniqueIDValue = uniqueIDValue - try object.didInsertFromImportSource(source) + try object.didInsertFromImportSource(source, inTransaction: self) } } } @@ -225,7 +265,7 @@ public extension BaseDataTransaction { func importUniqueObjects( into: Into, sourceArray: [T.ImportSource], - preProcess: ((mapping: [T.UniqueIDType: T.ImportSource]) throws -> Void)? = nil, + preProcess: ((inout mapping: [T.UniqueIDType: T.ImportSource]) throws -> Void)? = nil, postProcess: (sorted: [T]) -> Void) throws { CoreStore.assert( @@ -241,12 +281,11 @@ public extension BaseDataTransaction { try autoreleasepool { - if !T.shouldImportFromSource(source) { + guard let uniqueIDValue = try T.uniqueIDFromImportSource(source, inTransaction: self) else { return } - - let uniqueIDValue = try T.uniqueIDFromImportSource(source) + mapping[uniqueIDValue] = source sortedIDs.append(uniqueIDValue) } @@ -256,7 +295,7 @@ public extension BaseDataTransaction { try autoreleasepool { - try preProcess(mapping: mapping) + try preProcess(mapping: &mapping) } } @@ -266,7 +305,14 @@ public extension BaseDataTransaction { try autoreleasepool { let uniqueIDValue = object.uniqueIDValue - try object.updateFromImportSource(mapping.removeValueForKey(uniqueIDValue)!) + + guard let source = mapping.removeValueForKey(uniqueIDValue) + where T.shouldUpdateFromImportSource(source, inTransaction: self) else { + + return + } + + try object.updateFromImportSource(source, inTransaction: self) objects[uniqueIDValue] = object } } @@ -275,9 +321,14 @@ public extension BaseDataTransaction { try autoreleasepool { + guard T.shouldInsertFromImportSource(source, inTransaction: self) else { + + return + } + let object = self.create(into) object.uniqueIDValue = uniqueIDValue - try object.didInsertFromImportSource(source) + try object.didInsertFromImportSource(source, inTransaction: self) objects[uniqueIDValue] = object } diff --git a/CoreStore/Info.plist b/CoreStore/Info.plist index 3034b22..ae2dbbb 100644 --- a/CoreStore/Info.plist +++ b/CoreStore/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 1.1.2 + 1.2.1 CFBundleSignature ???? CFBundleVersion diff --git a/CoreStore/Internal/NSManagedObjectModel+Setup.swift b/CoreStore/Internal/NSManagedObjectModel+Setup.swift index 8ddd4b5..ffd82e2 100644 --- a/CoreStore/Internal/NSManagedObjectModel+Setup.swift +++ b/CoreStore/Internal/NSManagedObjectModel+Setup.swift @@ -150,7 +150,7 @@ internal extension NSManagedObjectModel { return self.entityNameMapping[NSStringFromClass(entityClass)]! } - @nonobjc internal func entityMapping() -> [String: NSManagedObject.Type] { + @nonobjc internal func entityTypesMapping() -> [String: NSManagedObject.Type] { return self.entityNameMapping.reduce([:]) { (var mapping, pair) in diff --git a/CoreStore/Observing/ListMonitor.swift b/CoreStore/Observing/ListMonitor.swift index 3d3fef3..f6f5318 100644 --- a/CoreStore/Observing/ListMonitor.swift +++ b/CoreStore/Observing/ListMonitor.swift @@ -273,6 +273,28 @@ public final class ListMonitor { return sections[section] } + /** + Returns the index of the `NSManagedObject` if it exists in the `ListMonitor`'s fetched objects, or `nil` if not found. + + - parameter object: the `NSManagedObject` to search the index of + - returns: the index of the `NSManagedObject` if it exists in the `ListMonitor`'s fetched objects, or `nil` if not found. + */ + public func indexOf(object: T) -> Int? { + + return (self.fetchedResultsController.fetchedObjects as? [T] ?? []).indexOf(object) + } + + /** + Returns the `NSIndexPath` of the `NSManagedObject` if it exists in the `ListMonitor`'s fetched objects, or `nil` if not found. + + - parameter object: the `NSManagedObject` to search the index of + - returns: the `NSIndexPath` of the `NSManagedObject` if it exists in the `ListMonitor`'s fetched objects, or `nil` if not found. + */ + public func indexPathOf(object: T) -> NSIndexPath? { + + return self.fetchedResultsController.indexPathForObject(object) + } + /** Registers a `ListObserver` to be notified when changes to the receiver's list occur. @@ -779,7 +801,7 @@ extension ListMonitor: FetchedResultsControllerHandler { ] ) - case .Move: + case .Move where indexPath != newIndexPath: NSNotificationCenter.defaultCenter().postNotificationName( ListMonitorDidMoveObjectNotification, object: self, @@ -789,6 +811,9 @@ extension ListMonitor: FetchedResultsControllerHandler { UserInfoKeyNewIndexPath: newIndexPath! ] ) + + default: + break } } diff --git a/CoreStore/Observing/ObjectMonitor.swift b/CoreStore/Observing/ObjectMonitor.swift index f06d257..6402a28 100644 --- a/CoreStore/Observing/ObjectMonitor.swift +++ b/CoreStore/Observing/ObjectMonitor.swift @@ -129,13 +129,13 @@ public final class ObjectMonitor { let previousCommitedAttributes = strongSelf.lastCommittedAttributes let currentCommitedAttributes = object.committedValuesForKeys(nil) as! [String: NSObject] - let changedKeys = currentCommitedAttributes.keys.reduce(Set()) { (var changedKeys, key) -> Set in + var changedKeys = Set() + for key in currentCommitedAttributes.keys { if previousCommitedAttributes[key] != currentCommitedAttributes[key] { changedKeys.insert(key) } - return changedKeys } strongSelf.lastCommittedAttributes = currentCommitedAttributes @@ -179,7 +179,7 @@ public final class ObjectMonitor { let fetchRequest = NSFetchRequest() fetchRequest.entity = object.entity - fetchRequest.fetchLimit = 1 + fetchRequest.fetchLimit = 0 fetchRequest.resultType = .ManagedObjectResultType fetchRequest.sortDescriptors = [] @@ -288,7 +288,7 @@ extension ObjectMonitor: FetchedResultsControllerHandler { userInfo: [UserInfoKeyObject: anObject] ) - case .Update: + case .Update, .Move where indexPath == newIndexPath: NSNotificationCenter.defaultCenter().postNotificationName( ObjectMonitorDidUpdateObjectNotification, object: self, diff --git a/CoreStore/Setting Up/CoreStore+Setup.swift b/CoreStore/Setting Up/CoreStore+Setup.swift index 11b42cf..c54c387 100644 --- a/CoreStore/Setting Up/CoreStore+Setup.swift +++ b/CoreStore/Setting Up/CoreStore+Setup.swift @@ -43,9 +43,17 @@ public extension CoreStore { /** Returns the entity name-to-class type mapping from the `defaultStack`'s model. */ - public static var entitiesByName: [String: NSManagedObject.Type] { + public static var entityTypesByName: [String: NSManagedObject.Type] { - return self.defaultStack.entitiesByName + return self.defaultStack.entityTypesByName + } + + /** + Returns the `NSEntityDescription` for the specified `NSManagedObject` subclass from `defaultStack`'s model. + */ + public static func entityDescriptionForType(type: NSManagedObject.Type) -> NSEntityDescription? { + + return self.defaultStack.entityDescriptionForType(type) } /** diff --git a/CoreStore/Setting Up/DataStack.swift b/CoreStore/Setting Up/DataStack.swift index e435c0a..46fe6ce 100644 --- a/CoreStore/Setting Up/DataStack.swift +++ b/CoreStore/Setting Up/DataStack.swift @@ -84,9 +84,20 @@ public final class DataStack { /** Returns the entity name-to-class type mapping from the `DataStack`'s model. */ - public var entitiesByName: [String: NSManagedObject.Type] { + public var entityTypesByName: [String: NSManagedObject.Type] { - return self.model.entityMapping() + return self.model.entityTypesMapping() + } + + /** + Returns the `NSEntityDescription` for the specified `NSManagedObject` subclass. + */ + public func entityDescriptionForType(type: NSManagedObject.Type) -> NSEntityDescription? { + + return NSEntityDescription.entityForName( + self.model.entityNameForClass(type), + inManagedObjectContext: self.mainContext + ) } /** diff --git a/README.md b/README.md index ded62d4..a07ea9f 100644 --- a/README.md +++ b/README.md @@ -956,7 +956,7 @@ let person2 = self.monitor[1, 2] # Installation - Requires: - iOS 8 SDK and above - - Swift 2.0 + - Swift 2.0 (XCode 7 beta 5) - Dependencies: - [GCDKit](https://github.com/JohnEstropia/GCDKit)