diff --git a/HardcoreData.xcodeproj/project.pbxproj b/HardcoreData.xcodeproj/project.pbxproj index acb2263..69f338e 100644 --- a/HardcoreData.xcodeproj/project.pbxproj +++ b/HardcoreData.xcodeproj/project.pbxproj @@ -14,6 +14,9 @@ B57078B01A50392D007E33F2 /* FetchClause.swift in Sources */ = {isa = PBXBuildFile; fileRef = B57078AF1A50392D007E33F2 /* FetchClause.swift */; }; B582DF821A98B0E7003F09C6 /* HardcoreData+Querying.swift in Sources */ = {isa = PBXBuildFile; fileRef = B582DF811A98B0E7003F09C6 /* HardcoreData+Querying.swift */; }; B582DF861A98B11B003F09C6 /* HardcoreData+Transaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B582DF851A98B11B003F09C6 /* HardcoreData+Transaction.swift */; }; + B595CAC41A9A11C1009A397F /* NSManagedObjectContext+Setup.swift in Sources */ = {isa = PBXBuildFile; fileRef = B595CAC31A9A11C1009A397F /* NSManagedObjectContext+Setup.swift */; }; + B595CAC61A9A1260009A397F /* NSManagedObjectContext+Transaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B595CAC51A9A1260009A397F /* NSManagedObjectContext+Transaction.swift */; }; + B595CAC81A9A161B009A397F /* WeakObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B595CAC71A9A161B009A397F /* WeakObject.swift */; }; B5CFD36E1A0775F000B7885F /* SaveResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5CFD36D1A0775F000B7885F /* SaveResult.swift */; }; B5CFF23E19FD1D1C00D6DFC4 /* NSManagedObjectContext+HardcoreData.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5CFF23D19FD1D1C00D6DFC4 /* NSManagedObjectContext+HardcoreData.swift */; }; B5CFF24019FD383100D6DFC4 /* DataTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5CFF23F19FD383100D6DFC4 /* DataTransaction.swift */; }; @@ -32,7 +35,7 @@ B5D8081A1A3495BD00A44484 /* NSObject+HardcoreData.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D808191A3495BD00A44484 /* NSObject+HardcoreData.swift */; }; B5E126551A7DCE1400AD8B39 /* Where.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E126541A7DCE1400AD8B39 /* Where.swift */; }; B5E126571A7DCE5900AD8B39 /* SortedBy.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E126561A7DCE5900AD8B39 /* SortedBy.swift */; }; - B5E209E01A0726460089C9D4 /* NSManagedObject+HardcoreData.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E209DF1A0726460089C9D4 /* NSManagedObject+HardcoreData.swift */; }; + B5E209E01A0726460089C9D4 /* NSManagedObject+Transaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E209DF1A0726460089C9D4 /* NSManagedObject+Transaction.swift */; }; B5F409E91A8B11CE00A228EA /* HardcoreDataLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F409E81A8B11CE00A228EA /* HardcoreDataLogger.swift */; }; B5F409EB1A8B199600A228EA /* DefaultLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F409EA1A8B199600A228EA /* DefaultLogger.swift */; }; B5F409ED1A8B200700A228EA /* NSManagedObjectContext+Querying.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F409EC1A8B200700A228EA /* NSManagedObjectContext+Querying.swift */; }; @@ -76,6 +79,9 @@ B57078AF1A50392D007E33F2 /* FetchClause.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FetchClause.swift; sourceTree = ""; }; B582DF811A98B0E7003F09C6 /* HardcoreData+Querying.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "HardcoreData+Querying.swift"; sourceTree = ""; }; B582DF851A98B11B003F09C6 /* HardcoreData+Transaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "HardcoreData+Transaction.swift"; sourceTree = ""; }; + B595CAC31A9A11C1009A397F /* NSManagedObjectContext+Setup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSManagedObjectContext+Setup.swift"; sourceTree = ""; }; + B595CAC51A9A1260009A397F /* NSManagedObjectContext+Transaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSManagedObjectContext+Transaction.swift"; sourceTree = ""; }; + B595CAC71A9A161B009A397F /* WeakObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WeakObject.swift; sourceTree = ""; }; B5CFD36D1A0775F000B7885F /* SaveResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SaveResult.swift; sourceTree = ""; }; B5CFF23D19FD1D1C00D6DFC4 /* NSManagedObjectContext+HardcoreData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSManagedObjectContext+HardcoreData.swift"; sourceTree = ""; }; B5CFF23F19FD383100D6DFC4 /* DataTransaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataTransaction.swift; sourceTree = ""; }; @@ -94,7 +100,7 @@ B5D808191A3495BD00A44484 /* NSObject+HardcoreData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+HardcoreData.swift"; sourceTree = ""; }; B5E126541A7DCE1400AD8B39 /* Where.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Where.swift; sourceTree = ""; }; B5E126561A7DCE5900AD8B39 /* SortedBy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SortedBy.swift; sourceTree = ""; }; - B5E209DF1A0726460089C9D4 /* NSManagedObject+HardcoreData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSManagedObject+HardcoreData.swift"; sourceTree = ""; }; + B5E209DF1A0726460089C9D4 /* NSManagedObject+Transaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSManagedObject+Transaction.swift"; sourceTree = ""; }; B5F3D98419F3EB8E009690A6 /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; B5F409E81A8B11CE00A228EA /* HardcoreDataLogger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HardcoreDataLogger.swift; sourceTree = ""; }; B5F409EA1A8B199600A228EA /* DefaultLogger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DefaultLogger.swift; sourceTree = ""; }; @@ -148,14 +154,10 @@ 2F03A53219C5C6DA005002A5 /* HardcoreData */ = { isa = PBXGroup; children = ( - B5D399F019FC818E000E91BB /* DataStack.swift */, 2F03A53519C5C6DA005002A5 /* HardcoreData.h */, 2F291E2619C6D3CF007AF63F /* HardcoreData.swift */, B5D1E22B19FA9FBC003B2874 /* NSError+HardcoreData.swift */, - B5E209DF1A0726460089C9D4 /* NSManagedObject+HardcoreData.swift */, - B5CFF23D19FD1D1C00D6DFC4 /* NSManagedObjectContext+HardcoreData.swift */, - B5D399F419FCF4E0000E91BB /* NSPersistentStoreCoordinator+HardcoreData.swift */, - B5D1E22919FA9E63003B2874 /* PersistentStoreResult.swift */, + B595CAC01A9A0AC4009A397F /* Setting Up */, B5D022621A90BCC60070CA63 /* Saving and Processing */, B5F409E51A8B11B600A228EA /* Logging */, B5E126531A7DCCE400AD8B39 /* Fetching and Querying */, @@ -204,13 +206,41 @@ name = Frameworks; sourceTree = ""; }; + B595CAC01A9A0AC4009A397F /* Setting Up */ = { + isa = PBXGroup; + children = ( + B5D399F019FC818E000E91BB /* DataStack.swift */, + B5D1E22919FA9E63003B2874 /* PersistentStoreResult.swift */, + B595CAC91A9A1802009A397F /* Internal */, + ); + name = "Setting Up"; + sourceTree = ""; + }; + B595CAC91A9A1802009A397F /* Internal */ = { + isa = PBXGroup; + children = ( + B595CAC31A9A11C1009A397F /* NSManagedObjectContext+Setup.swift */, + ); + name = Internal; + sourceTree = ""; + }; + B595CACA1A9A1818009A397F /* Internal */ = { + isa = PBXGroup; + children = ( + B5E209DF1A0726460089C9D4 /* NSManagedObject+Transaction.swift */, + B595CAC51A9A1260009A397F /* NSManagedObjectContext+Transaction.swift */, + ); + name = Internal; + sourceTree = ""; + }; B5D022621A90BCC60070CA63 /* Saving and Processing */ = { isa = PBXGroup; children = ( B5CFF23F19FD383100D6DFC4 /* DataTransaction.swift */, + B5CFD36D1A0775F000B7885F /* SaveResult.swift */, B582DF851A98B11B003F09C6 /* HardcoreData+Transaction.swift */, B5D022651A90CD340070CA63 /* DataStack+Transaction.swift */, - B5CFD36D1A0775F000B7885F /* SaveResult.swift */, + B595CACA1A9A1818009A397F /* Internal */, ); name = "Saving and Processing"; sourceTree = ""; @@ -244,7 +274,10 @@ isa = PBXGroup; children = ( B5D808151A34947300A44484 /* NotificationObserver.swift */, + B595CAC71A9A161B009A397F /* WeakObject.swift */, B5D808191A3495BD00A44484 /* NSObject+HardcoreData.swift */, + B5CFF23D19FD1D1C00D6DFC4 /* NSManagedObjectContext+HardcoreData.swift */, + B5D399F419FCF4E0000E91BB /* NSPersistentStoreCoordinator+HardcoreData.swift */, ); name = Internal; sourceTree = ""; @@ -409,14 +442,17 @@ B5D022661A90CD340070CA63 /* DataStack+Transaction.swift in Sources */, B582DF861A98B11B003F09C6 /* HardcoreData+Transaction.swift in Sources */, B5D1E22C19FA9FBC003B2874 /* NSError+HardcoreData.swift in Sources */, + B595CAC61A9A1260009A397F /* NSManagedObjectContext+Transaction.swift in Sources */, B5CFF23E19FD1D1C00D6DFC4 /* NSManagedObjectContext+HardcoreData.swift in Sources */, B5E126571A7DCE5900AD8B39 /* SortedBy.swift in Sources */, B5F409EF1A8B243D00A228EA /* DataTransaction+Querying.swift in Sources */, 2F291E2719C6D3CF007AF63F /* HardcoreData.swift in Sources */, B5F409E91A8B11CE00A228EA /* HardcoreDataLogger.swift in Sources */, B5D8081A1A3495BD00A44484 /* NSObject+HardcoreData.swift in Sources */, - B5E209E01A0726460089C9D4 /* NSManagedObject+HardcoreData.swift in Sources */, + B595CAC41A9A11C1009A397F /* NSManagedObjectContext+Setup.swift in Sources */, + B5E209E01A0726460089C9D4 /* NSManagedObject+Transaction.swift in Sources */, B582DF821A98B0E7003F09C6 /* HardcoreData+Querying.swift in Sources */, + B595CAC81A9A161B009A397F /* WeakObject.swift in Sources */, B5D1E22A19FA9E63003B2874 /* PersistentStoreResult.swift in Sources */, B5F409F11A8B27A600A228EA /* CustomizeQuery.swift in Sources */, B5F409EB1A8B199600A228EA /* DefaultLogger.swift in Sources */, diff --git a/HardcoreData/DataStack+Transaction.swift b/HardcoreData/DataStack+Transaction.swift index b9bf253..258d54c 100644 --- a/HardcoreData/DataStack+Transaction.swift +++ b/HardcoreData/DataStack+Transaction.swift @@ -2,8 +2,25 @@ // DataStack+Transaction.swift // HardcoreData // -// Created by John Rommel Estropia on 2015/02/15. -// Copyright (c) 2015 John Rommel Estropia. All rights reserved. +// Copyright (c) 2015 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 @@ -12,7 +29,7 @@ import CoreData // MARK: - DataStack -extension DataStack { +public extension DataStack { // MARK: Public diff --git a/HardcoreData/DataTransaction+Querying.swift b/HardcoreData/DataTransaction+Querying.swift index f8c2d3a..c9f2fb5 100644 --- a/HardcoreData/DataTransaction+Querying.swift +++ b/HardcoreData/DataTransaction+Querying.swift @@ -29,7 +29,7 @@ import CoreData // MARK: - DataTransaction -extension DataTransaction { +public extension DataTransaction { // MARK: Public diff --git a/HardcoreData/DataTransaction.swift b/HardcoreData/DataTransaction.swift index a7bf6ba..19dcca5 100644 --- a/HardcoreData/DataTransaction.swift +++ b/HardcoreData/DataTransaction.swift @@ -134,15 +134,13 @@ public final class DataTransaction { internal init(mainContext: NSManagedObjectContext, queue: GCDQueue, closure: (transaction: DataTransaction) -> Void) { - self.mainContext = mainContext self.transactionQueue = queue - - let context = mainContext.temporaryContext() - context.retainsRegisteredObjects = true - self.context = context - self.closure = closure + let context = mainContext.temporaryContextInTransaction(nil) + self.context = context + + context.retainsRegisteredObjects = true context.parentTransaction = self } @@ -168,7 +166,6 @@ public final class DataTransaction { private var isCommitted = false private var result: SaveResult? - private let mainContext: NSManagedObjectContext private let transactionQueue: GCDQueue private let closure: (transaction: DataTransaction) -> Void } diff --git a/HardcoreData/HardcoreData+Querying.swift b/HardcoreData/HardcoreData+Querying.swift index e4bcdc7..475f7af 100644 --- a/HardcoreData/HardcoreData+Querying.swift +++ b/HardcoreData/HardcoreData+Querying.swift @@ -27,7 +27,7 @@ import Foundation // MARK: - HardcoreData -extension HardcoreData { +public extension HardcoreData { // MARK: Public diff --git a/HardcoreData/HardcoreData+Transaction.swift b/HardcoreData/HardcoreData+Transaction.swift index 0ba0ad2..12f91e5 100644 --- a/HardcoreData/HardcoreData+Transaction.swift +++ b/HardcoreData/HardcoreData+Transaction.swift @@ -28,7 +28,7 @@ import Foundation // MARK: - HardcoreData -extension HardcoreData { +public extension HardcoreData { // MARK: Public diff --git a/HardcoreData/NSManagedObject+HardcoreData.swift b/HardcoreData/NSManagedObject+Transaction.swift similarity index 96% rename from HardcoreData/NSManagedObject+HardcoreData.swift rename to HardcoreData/NSManagedObject+Transaction.swift index 9722dc5..ff0a131 100644 --- a/HardcoreData/NSManagedObject+HardcoreData.swift +++ b/HardcoreData/NSManagedObject+Transaction.swift @@ -1,5 +1,5 @@ // -// NSManagedObject+HardcoreData.swift +// NSManagedObject+Transaction.swift // HardcoreData // // Copyright (c) 2014 John Rommel Estropia @@ -27,9 +27,9 @@ import Foundation import CoreData -// MARK: - NSManagedObject+HardcoreData +// MARK: - NSManagedObject -extension NSManagedObject { +internal extension NSManagedObject { // MARK: - Internal diff --git a/HardcoreData/NSManagedObjectContext+HardcoreData.swift b/HardcoreData/NSManagedObjectContext+HardcoreData.swift index a2c11f1..0d5acdb 100644 --- a/HardcoreData/NSManagedObjectContext+HardcoreData.swift +++ b/HardcoreData/NSManagedObjectContext+HardcoreData.swift @@ -30,61 +30,22 @@ import GCDKit // MARK: - NSManagedObjectContext -public extension NSManagedObjectContext { - - // MARK: NSObject - - // MARK: Transactions - - public func temporaryContext() -> NSManagedObjectContext { - - let context = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType) - context.parentContext = self - context.parentStack = self.parentStack - context.parentTransaction = self.parentTransaction - context.setupForHardcoreDataWithContextName("com.hardcoredata.temporarycontext") - context.shouldCascadeSavesToParent = true - - return context - } - +internal extension NSManagedObjectContext { // MARK: - Internal - internal weak var parentStack: DataStack? { + internal var shouldCascadeSavesToParent: Bool { get { - if let parentContext = self.parentContext { - - return parentContext.parentStack - } - return self.getAssociatedObjectForKey(&PropertyKeys.parentStack) + let number: NSNumber? = self.getAssociatedObjectForKey(&PropertyKeys.shouldCascadeSavesToParent) + return number?.boolValue ?? false } set { - if let parentContext = self.parentContext { - - return - } - - self.setAssociatedWeakObject( - newValue, - forKey: &PropertyKeys.parentStack) - } - } - - internal weak var parentTransaction: DataTransaction? { - - get { - - return self.getAssociatedObjectForKey(&PropertyKeys.parentTransaction) - } - set { - - self.setAssociatedWeakObject( - newValue, - forKey: &PropertyKeys.parentTransaction) + self.setAssociatedCopiedObject( + NSNumber(bool: newValue), + forKey: &PropertyKeys.shouldCascadeSavesToParent) } } @@ -100,215 +61,7 @@ public extension NSManagedObjectContext { return nil } - internal func temporaryContextInTransaction(transaction: DataTransaction?) -> NSManagedObjectContext { - - let context = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType) - context.parentContext = self - context.parentStack = self.parentStack - context.setupForHardcoreDataWithContextName("com.hardcoredata.temporarycontext") - context.shouldCascadeSavesToParent = true - - return context - } - - internal func saveSynchronously() -> SaveResult { - - var result = SaveResult(hasChanges: false) - self.performBlockAndWait { - [unowned self] () -> Void in - - if !self.hasChanges { - - self.reset() - return - } - - var saveError: NSError? - if self.save(&saveError) { - - if self.shouldCascadeSavesToParent { - - if let parentContext = self.parentContext { - - switch parentContext.saveSynchronously() { - - case .Success(let hasChanges): - result = SaveResult(hasChanges: true) - case .Failure(let error): - result = SaveResult(error) - } - return - } - } - - result = SaveResult(hasChanges: true) - } - else if let error = saveError { - - HardcoreData.handleError( - error, - "Failed to save NSManagedObjectContext.") - result = SaveResult(error) - } - else { - - result = SaveResult(hasChanges: false) - } - } - - return result - } - - internal func saveAsynchronouslyWithCompletion(completion: ((result: SaveResult) -> Void)?) { - - self.performBlock { () -> Void in - - if !self.hasChanges { - - if let completion = completion { - - GCDQueue.Main.async { - - completion(result: SaveResult(hasChanges: false)) - } - } - return - } - - var saveError: NSError? - if self.save(&saveError) { - - if self.shouldCascadeSavesToParent { - - if let parentContext = self.parentContext { - - let result = parentContext.saveSynchronously() - if let completion = completion { - - GCDQueue.Main.async { - - completion(result: result) - } - } - return - } - } - - if let completion = completion { - - GCDQueue.Main.async { - - completion(result: SaveResult(hasChanges: true)) - } - } - } - else if let error = saveError { - - HardcoreData.handleError( - error, - "Failed to save NSManagedObjectContext.") - if let completion = completion { - - GCDQueue.Main.async { - - completion(result: SaveResult(error)) - } - } - } - else if let completion = completion { - - GCDQueue.Main.async { - - completion(result: SaveResult(hasChanges: false)) - } - } - } - } - - internal class func rootSavingContextForCoordinator(coordinator: NSPersistentStoreCoordinator) -> NSManagedObjectContext { - - let context = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType) - context.persistentStoreCoordinator = coordinator - context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy - context.setupForHardcoreDataWithContextName("com.hardcoredata.rootcontext") - - return context - } - - internal class func mainContextForRootContext(rootContext: NSManagedObjectContext) -> NSManagedObjectContext { - - let context = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType) - context.parentContext = rootContext - context.setupForHardcoreDataWithContextName("com.hardcoredata.maincontext") - context.shouldCascadeSavesToParent = true - context.observerForDidSaveNotification = NotificationObserver( - notificationName: NSManagedObjectContextDidSaveNotification, - object: rootContext, - closure: { [weak context] (note) -> Void in - - context?.mergeChangesFromContextDidSaveNotification(note) - return - }) - - return context - } - - - // MARK: - Private - - private struct PropertyKeys { - - static var observerForWillSaveNotification: Void? - static var observerForDidSaveNotification: Void? - static var shouldCascadeSavesToParent: Void? - static var parentStack: Void? - static var parentTransaction: Void? - } - - private var observerForWillSaveNotification: NotificationObserver? { - - get { - - return self.getAssociatedObjectForKey(&PropertyKeys.observerForWillSaveNotification) - } - set { - - self.setAssociatedRetainedObject( - newValue, - forKey: &PropertyKeys.observerForWillSaveNotification) - } - } - - private var observerForDidSaveNotification: NotificationObserver? { - - get { - - return self.getAssociatedObjectForKey(&PropertyKeys.observerForDidSaveNotification) - } - set { - - self.setAssociatedRetainedObject( - newValue, - forKey: &PropertyKeys.observerForDidSaveNotification) - } - } - - private var shouldCascadeSavesToParent: Bool { - - get { - - let number: NSNumber? = self.getAssociatedObjectForKey(&PropertyKeys.observerForDidSaveNotification) - return number?.boolValue ?? false - } - set { - - self.setAssociatedCopiedObject( - NSNumber(bool: newValue), - forKey: &PropertyKeys.shouldCascadeSavesToParent) - } - } - - private func setupForHardcoreDataWithContextName(contextName: String) { + internal func setupForHardcoreDataWithContextName(contextName: String) { if self.respondsToSelector("setName:") { @@ -341,5 +94,28 @@ public extension NSManagedObjectContext { } }) } + + + // MARK: - Private + + private struct PropertyKeys { + + static var observerForWillSaveNotification: Void? + static var shouldCascadeSavesToParent: Void? + } + + private var observerForWillSaveNotification: NotificationObserver? { + + get { + + return self.getAssociatedObjectForKey(&PropertyKeys.observerForWillSaveNotification) + } + set { + + self.setAssociatedRetainedObject( + newValue, + forKey: &PropertyKeys.observerForWillSaveNotification) + } + } } diff --git a/HardcoreData/NSManagedObjectContext+Querying.swift b/HardcoreData/NSManagedObjectContext+Querying.swift index 01cfa3d..d13b6b8 100644 --- a/HardcoreData/NSManagedObjectContext+Querying.swift +++ b/HardcoreData/NSManagedObjectContext+Querying.swift @@ -29,7 +29,7 @@ import CoreData // MARK: - NSManagedObjectContext -extension NSManagedObjectContext { +public extension NSManagedObjectContext { // MARK: Public diff --git a/HardcoreData/NSManagedObjectContext+Setup.swift b/HardcoreData/NSManagedObjectContext+Setup.swift new file mode 100644 index 0000000..538771f --- /dev/null +++ b/HardcoreData/NSManagedObjectContext+Setup.swift @@ -0,0 +1,110 @@ +// +// NSManagedObjectContext+Setup.swift +// HardcoreData +// +// Copyright (c) 2015 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: - NSManagedObjectContext + +internal extension NSManagedObjectContext { + + // MARK: - Internal + + internal weak var parentStack: DataStack? { + + get { + + if let parentContext = self.parentContext { + + return parentContext.parentStack + } + return self.getAssociatedObjectForKey(&PropertyKeys.parentStack) + } + set { + + if let parentContext = self.parentContext { + + return + } + + self.setAssociatedWeakObject( + newValue, + forKey: &PropertyKeys.parentStack) + } + } + + internal class func rootSavingContextForCoordinator(coordinator: NSPersistentStoreCoordinator) -> NSManagedObjectContext { + + let context = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType) + context.persistentStoreCoordinator = coordinator + context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy + context.setupForHardcoreDataWithContextName("com.hardcoredata.rootcontext") + + return context + } + + internal class func mainContextForRootContext(rootContext: NSManagedObjectContext) -> NSManagedObjectContext { + + let context = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType) + context.parentContext = rootContext + context.setupForHardcoreDataWithContextName("com.hardcoredata.maincontext") + context.shouldCascadeSavesToParent = true + context.observerForDidSaveNotification = NotificationObserver( + notificationName: NSManagedObjectContextDidSaveNotification, + object: rootContext, + closure: { [weak context] (note) -> Void in + + context?.mergeChangesFromContextDidSaveNotification(note) + return + } + ) + + return context + } + + + // MARK: Private + + private struct PropertyKeys { + + static var parentStack: Void? + static var observerForDidSaveNotification: Void? + } + + private var observerForDidSaveNotification: NotificationObserver? { + + get { + + return self.getAssociatedObjectForKey(&PropertyKeys.observerForDidSaveNotification) + } + set { + + self.setAssociatedRetainedObject( + newValue, + forKey: &PropertyKeys.observerForDidSaveNotification) + } + } +} diff --git a/HardcoreData/NSManagedObjectContext+Transaction.swift b/HardcoreData/NSManagedObjectContext+Transaction.swift new file mode 100644 index 0000000..e6dc253 --- /dev/null +++ b/HardcoreData/NSManagedObjectContext+Transaction.swift @@ -0,0 +1,183 @@ +// +// NSManagedObjectContext+Transaction.swift +// HardcoreData +// +// Copyright (c) 2015 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 +import GCDKit + + +// MARK: - NSManagedObjectContext + +internal extension NSManagedObjectContext { + + // MARK: - Internal + + internal weak var parentTransaction: DataTransaction? { + + get { + + return self.getAssociatedObjectForKey(&PropertyKeys.parentTransaction) + } + set { + + self.setAssociatedWeakObject( + newValue, + forKey: &PropertyKeys.parentTransaction) + } + } + + internal func temporaryContextInTransaction(transaction: DataTransaction?) -> NSManagedObjectContext { + + let context = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType) + context.parentContext = self + context.parentStack = self.parentStack + context.setupForHardcoreDataWithContextName("com.hardcoredata.temporarycontext") + context.shouldCascadeSavesToParent = true + + return context + } + + internal func saveSynchronously() -> SaveResult { + + var result = SaveResult(hasChanges: false) + self.performBlockAndWait { + [unowned self] () -> Void in + + if !self.hasChanges { + + self.reset() + return + } + + var saveError: NSError? + if self.save(&saveError) { + + if self.shouldCascadeSavesToParent { + + if let parentContext = self.parentContext { + + switch parentContext.saveSynchronously() { + + case .Success(let hasChanges): + result = SaveResult(hasChanges: true) + case .Failure(let error): + result = SaveResult(error) + } + return + } + } + + result = SaveResult(hasChanges: true) + } + else if let error = saveError { + + HardcoreData.handleError( + error, + "Failed to save NSManagedObjectContext.") + result = SaveResult(error) + } + else { + + result = SaveResult(hasChanges: false) + } + } + + return result + } + + internal func saveAsynchronouslyWithCompletion(completion: ((result: SaveResult) -> Void)?) { + + self.performBlock { () -> Void in + + if !self.hasChanges { + + if let completion = completion { + + GCDQueue.Main.async { + + completion(result: SaveResult(hasChanges: false)) + } + } + return + } + + var saveError: NSError? + if self.save(&saveError) { + + if self.shouldCascadeSavesToParent { + + if let parentContext = self.parentContext { + + let result = parentContext.saveSynchronously() + if let completion = completion { + + GCDQueue.Main.async { + + completion(result: result) + } + } + return + } + } + + if let completion = completion { + + GCDQueue.Main.async { + + completion(result: SaveResult(hasChanges: true)) + } + } + } + else if let error = saveError { + + HardcoreData.handleError( + error, + "Failed to save NSManagedObjectContext.") + if let completion = completion { + + GCDQueue.Main.async { + + completion(result: SaveResult(error)) + } + } + } + else if let completion = completion { + + GCDQueue.Main.async { + + completion(result: SaveResult(hasChanges: false)) + } + } + } + } + + + // MARK: Private + + private struct PropertyKeys { + + static var parentTransaction: Void? + } +} \ No newline at end of file diff --git a/HardcoreData/NSObject+HardcoreData.swift b/HardcoreData/NSObject+HardcoreData.swift index 2a5798d..ba20013 100644 --- a/HardcoreData/NSObject+HardcoreData.swift +++ b/HardcoreData/NSObject+HardcoreData.swift @@ -26,18 +26,7 @@ import Foundation -private class WeakObject { - - private(set) weak var object: AnyObject? - - init(_ object: AnyObject) { - - self.object = object - } -} - - -// MARK: - NSObject+HardcoreData +// MARK: - NSObject internal extension NSObject { diff --git a/HardcoreData/NSPersistentStoreCoordinator+HardcoreData.swift b/HardcoreData/NSPersistentStoreCoordinator+HardcoreData.swift index a67b9ab..fdac82d 100644 --- a/HardcoreData/NSPersistentStoreCoordinator+HardcoreData.swift +++ b/HardcoreData/NSPersistentStoreCoordinator+HardcoreData.swift @@ -27,11 +27,13 @@ import Foundation import CoreData -// MARK: - NSPersistentStoreCoordinator+HardcoreData +// MARK: - NSPersistentStoreCoordinator -public extension NSPersistentStoreCoordinator { +internal extension NSPersistentStoreCoordinator { - public func performSynchronously(closure: () -> Void) { + // MARK: Internal + + internal func performSynchronously(closure: () -> Void) { if self.respondsToSelector("performBlockAndWait:") { diff --git a/HardcoreData/WeakObject.swift b/HardcoreData/WeakObject.swift new file mode 100644 index 0000000..08a502d --- /dev/null +++ b/HardcoreData/WeakObject.swift @@ -0,0 +1,44 @@ +// +// WeakObject.swift +// HardcoreData +// +// Copyright (c) 2015 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 + + +// MARK: - WeakObject + +internal final class WeakObject { + + // MARK: Internal + + internal init(_ object: AnyObject) { + + self.object = object + } + + + // MARK: Private + + private(set) weak var object: AnyObject? +}