mirror of
https://github.com/JohnEstropia/CoreStore.git
synced 2026-03-21 00:49:49 +01:00
restructured files, added way to transform section names in ManagedObjectListControllers
This commit is contained in:
@@ -0,0 +1,182 @@
|
||||
//
|
||||
// AsynchronousDataTransaction.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: - AsynchronousDataTransaction
|
||||
|
||||
/**
|
||||
The AsynchronousDataTransaction provides an interface for NSManagedObject creates, updates, and deletes. A transaction object should typically be only used from within a transaction block initiated from DataStack.beginAsynchronous(_:), or from HardcoreData.beginAsynchronous(_:).
|
||||
*/
|
||||
public final class AsynchronousDataTransaction: BaseDataTransaction {
|
||||
|
||||
// MARK: Public
|
||||
|
||||
/**
|
||||
Saves the transaction changes asynchronously. This method should not be used after the commit() method was already called once.
|
||||
|
||||
:param: completion the block executed after the save completes. Success or failure is reported by the SaveResult argument of the block.
|
||||
*/
|
||||
public func commit(completion: (result: SaveResult) -> Void) {
|
||||
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to commit a \(typeName(self)) outside its designated queue.")
|
||||
HardcoreData.assert(!self.isCommitted, "Attempted to commit a \(typeName(self)) more than once.")
|
||||
|
||||
self.isCommitted = true
|
||||
let semaphore = GCDSemaphore(0)
|
||||
self.context.saveAsynchronouslyWithCompletion { (result) -> Void in
|
||||
|
||||
self.result = result
|
||||
completion(result: result)
|
||||
semaphore.signal()
|
||||
}
|
||||
semaphore.wait()
|
||||
}
|
||||
|
||||
/**
|
||||
Saves the transaction changes and waits for completion synchronously. This method should not be used after the commit() method was already called once.
|
||||
|
||||
:returns: a SaveResult value indicating success or failure.
|
||||
*/
|
||||
public func commit() {
|
||||
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to commit a \(typeName(self)) outside its designated queue.")
|
||||
HardcoreData.assert(!self.isCommitted, "Attempted to commit a \(typeName(self)) more than once.")
|
||||
|
||||
self.isCommitted = true
|
||||
self.result = self.context.saveSynchronously()
|
||||
}
|
||||
|
||||
/**
|
||||
Begins a child transaction synchronously where NSManagedObject creates, updates, and deletes can be made. This method should not be used after he commit() method was already called once.
|
||||
|
||||
:param: closure the block where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent NSManagedObjectContext.
|
||||
:returns: a SaveResult value indicating success or failure, or nil if the transaction was not comitted synchronously
|
||||
*/
|
||||
public func beginSynchronous(closure: (transaction: SynchronousDataTransaction) -> Void) -> SaveResult? {
|
||||
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to begin a child transaction from a \(typeName(self)) outside its designated queue.")
|
||||
HardcoreData.assert(!self.isCommitted, "Attempted to begin a child transaction from an already committed \(typeName(self)).")
|
||||
|
||||
return SynchronousDataTransaction(
|
||||
mainContext: self.context,
|
||||
queue: self.childTransactionQueue,
|
||||
closure: closure).performAndWait()
|
||||
}
|
||||
|
||||
|
||||
// MARK: BaseDataTransaction
|
||||
|
||||
/**
|
||||
Creates a new NSManagedObject with the specified entity type. This method should not be used after the commit() method was already called once.
|
||||
|
||||
:param: entity the NSManagedObject type to be created
|
||||
:returns: a new NSManagedObject instance of the specified entity type.
|
||||
*/
|
||||
public override func create<T: NSManagedObject>(entity: T.Type) -> T {
|
||||
|
||||
HardcoreData.assert(!self.isCommitted, "Attempted to create an entity of type <\(entity)> from an already committed \(typeName(self)).")
|
||||
|
||||
return super.create(entity)
|
||||
}
|
||||
|
||||
/**
|
||||
Returns an editable proxy of a specified NSManagedObject. This method should not be used after the commit() method was already called once.
|
||||
|
||||
:param: object the NSManagedObject type to be edited
|
||||
:returns: an editable proxy for the specified NSManagedObject.
|
||||
*/
|
||||
public override func fetch<T: NSManagedObject>(object: T?) -> T? {
|
||||
|
||||
HardcoreData.assert(!self.isCommitted, "Attempted to update an entity of type \(typeName(object)) from an already committed \(typeName(self)).")
|
||||
|
||||
return super.fetch(object)
|
||||
}
|
||||
|
||||
/**
|
||||
Deletes a specified NSManagedObject. This method should not be used after the commit() method was already called once.
|
||||
|
||||
:param: object the NSManagedObject type to be deleted
|
||||
*/
|
||||
public override func delete(object: NSManagedObject?) {
|
||||
|
||||
HardcoreData.assert(!self.isCommitted, "Attempted to delete an entity of type \(typeName(object)) from an already committed \(typeName(self)).")
|
||||
|
||||
super.delete(object)
|
||||
}
|
||||
|
||||
/**
|
||||
Rolls back the transaction by resetting the NSManagedObjectContext. After calling this method, all NSManagedObjects fetched within the transaction will become invalid. This method should not be used after the commit() method was already called once.
|
||||
*/
|
||||
public override func rollback() {
|
||||
|
||||
HardcoreData.assert(!self.isCommitted, "Attempted to rollback an already committed \(typeName(self)).")
|
||||
|
||||
super.rollback()
|
||||
}
|
||||
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
internal init(mainContext: NSManagedObjectContext, queue: GCDQueue, closure: (transaction: AsynchronousDataTransaction) -> Void) {
|
||||
|
||||
self.closure = closure
|
||||
|
||||
super.init(mainContext: mainContext, queue: queue)
|
||||
}
|
||||
|
||||
internal func perform() {
|
||||
|
||||
self.transactionQueue.async {
|
||||
|
||||
self.closure(transaction: self)
|
||||
if !self.isCommitted && self.hasChanges {
|
||||
|
||||
HardcoreData.log(.Warning, message: "The closure for the \(typeName(self)) completed without being committed. All changes made within the transaction were discarded.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal func performAndWait() -> SaveResult? {
|
||||
|
||||
self.transactionQueue.sync {
|
||||
|
||||
self.closure(transaction: self)
|
||||
if !self.isCommitted && self.hasChanges {
|
||||
|
||||
HardcoreData.log(.Warning, message: "The closure for the \(typeName(self)) completed without being committed. All changes made within the transaction were discarded.")
|
||||
}
|
||||
}
|
||||
return self.result
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let closure: (transaction: AsynchronousDataTransaction) -> Void
|
||||
}
|
||||
118
HardcoreData/Saving and Processing/BaseDataTransaction.swift
Normal file
118
HardcoreData/Saving and Processing/BaseDataTransaction.swift
Normal file
@@ -0,0 +1,118 @@
|
||||
//
|
||||
// BaseDataTransaction.swift
|
||||
// HardcoreData
|
||||
//
|
||||
// Copyright (c) 2014 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: - BaseDataTransaction
|
||||
|
||||
/**
|
||||
The BaseDataTransaction is an abstract interface for NSManagedObject creates, updates, and deletes. All BaseDataTransaction subclasses manage a private NSManagedObjectContext which are direct children of the NSPersistentStoreCoordinator's root NSManagedObjectContext. This means that all updates are saved first to the persistent store, and then propagated up to the read-only NSManagedObjectContext.
|
||||
*/
|
||||
public /*abstract*/ class BaseDataTransaction {
|
||||
|
||||
// MARK: Object management
|
||||
|
||||
var hasChanges: Bool {
|
||||
|
||||
return self.context.hasChanges
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a new NSManagedObject with the specified entity type.
|
||||
|
||||
:param: entity the NSManagedObject type to be created
|
||||
:returns: a new NSManagedObject instance of the specified entity type.
|
||||
*/
|
||||
public func create<T: NSManagedObject>(entity: T.Type) -> T {
|
||||
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to create an entity of type <\(entity)> outside its designated queue.")
|
||||
|
||||
return T.createInContext(self.context)
|
||||
}
|
||||
|
||||
/**
|
||||
Returns an editable proxy of a specified NSManagedObject.
|
||||
|
||||
:param: object the NSManagedObject type to be edited
|
||||
:returns: an editable proxy for the specified NSManagedObject.
|
||||
*/
|
||||
public func fetch<T: NSManagedObject>(object: T?) -> T? {
|
||||
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to update an entity of type \(typeName(object)) outside its designated queue.")
|
||||
|
||||
return object?.inContext(self.context)
|
||||
}
|
||||
|
||||
/**
|
||||
Deletes a specified NSManagedObject.
|
||||
|
||||
:param: object the NSManagedObject type to be deleted
|
||||
*/
|
||||
public func delete(object: NSManagedObject?) {
|
||||
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to delete an entity of type \(typeName(object)) outside its designated queue.")
|
||||
|
||||
object?.inContext(self.context)?.deleteFromContext()
|
||||
}
|
||||
|
||||
// MARK: Saving changes
|
||||
|
||||
/**
|
||||
Rolls back the transaction by resetting the NSManagedObjectContext. After calling this method, all NSManagedObjects fetched within the transaction will become invalid.
|
||||
*/
|
||||
public func rollback() {
|
||||
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to rollback a \(typeName(self)) outside its designated queue.")
|
||||
|
||||
self.context.reset()
|
||||
}
|
||||
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
internal let context: NSManagedObjectContext
|
||||
internal let transactionQueue: GCDQueue
|
||||
internal let childTransactionQueue: GCDQueue = .createSerial("com.hardcoredata.datastack.childtransactionqueue")
|
||||
|
||||
internal var isCommitted = false
|
||||
internal var result: SaveResult?
|
||||
|
||||
internal init(mainContext: NSManagedObjectContext, queue: GCDQueue) {
|
||||
|
||||
self.transactionQueue = queue
|
||||
|
||||
let context = mainContext.temporaryContextInTransactionWithConcurrencyType(
|
||||
queue == .Main
|
||||
? .MainQueueConcurrencyType
|
||||
: .PrivateQueueConcurrencyType
|
||||
)
|
||||
self.context = context
|
||||
|
||||
context.parentTransaction = self
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
//
|
||||
// DataStack+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: - DataStack
|
||||
|
||||
public extension DataStack {
|
||||
|
||||
// MARK: Public
|
||||
|
||||
/**
|
||||
Begins a transaction asynchronously where NSManagedObject creates, updates, and deletes can be made.
|
||||
|
||||
:param: closure the block where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent NSManagedObjectContext.
|
||||
*/
|
||||
public func beginAsynchronous(closure: (transaction: AsynchronousDataTransaction) -> Void) {
|
||||
|
||||
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to begin a transaction from a \(typeName(self)) outside the main queue.")
|
||||
|
||||
AsynchronousDataTransaction(
|
||||
mainContext: self.rootSavingContext,
|
||||
queue: self.childTransactionQueue,
|
||||
closure: closure).perform()
|
||||
}
|
||||
|
||||
/**
|
||||
Begins a transaction synchronously where NSManagedObject creates, updates, and deletes can be made.
|
||||
|
||||
:param: closure the block where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent NSManagedObjectContext.
|
||||
:returns: a SaveResult value indicating success or failure, or nil if the transaction was not comitted synchronously
|
||||
*/
|
||||
public func beginSynchronous(closure: (transaction: SynchronousDataTransaction) -> Void) -> SaveResult? {
|
||||
|
||||
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to begin a transaction from a \(typeName(self)) outside the main queue.")
|
||||
|
||||
return SynchronousDataTransaction(
|
||||
mainContext: self.rootSavingContext,
|
||||
queue: self.childTransactionQueue,
|
||||
closure: closure).performAndWait()
|
||||
}
|
||||
|
||||
/**
|
||||
Begins a non-contiguous transaction where NSManagedObject creates, updates, and deletes can be made. This is useful for making temporary changes, such as partially filled forms. A detached transaction object should typically be only used from the main queue.
|
||||
|
||||
:returns: a DetachedDataTransaction instance where creates, updates, and deletes can be made.
|
||||
*/
|
||||
public func beginDetached() -> DetachedDataTransaction {
|
||||
|
||||
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to begin a transaction from a \(typeName(self)) outside the main queue.")
|
||||
|
||||
return DetachedDataTransaction(
|
||||
mainContext: self.rootSavingContext,
|
||||
queue: .Main)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
//
|
||||
// DetachedDataTransaction.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 GCDKit
|
||||
|
||||
|
||||
// MARK: - DetachedDataTransaction
|
||||
|
||||
/**
|
||||
The DetachedDataTransaction provides an interface for non-contiguous NSManagedObject creates, updates, and deletes. This is useful for making temporary changes, such as partially filled forms. A detached transaction object should typically be only used from the main queue.
|
||||
*/
|
||||
public final class DetachedDataTransaction: BaseDataTransaction {
|
||||
|
||||
// MARK: Public
|
||||
|
||||
/**
|
||||
Saves the transaction changes asynchronously. For a DetachedDataTransaction, multiple commits are allowed, although it is the developer's responsibility to ensure a reasonable leeway to prevent blocking the main thread.
|
||||
|
||||
:param: completion the block executed after the save completes. Success or failure is reported by the SaveResult argument of the block.
|
||||
*/
|
||||
public func commit(completion: (result: SaveResult) -> Void) {
|
||||
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to commit a \(typeName(self)) outside its designated queue.")
|
||||
|
||||
self.context.saveAsynchronouslyWithCompletion { (result) -> Void in
|
||||
|
||||
self.result = result
|
||||
completion(result: result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
//
|
||||
// HardcoreData+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
|
||||
|
||||
|
||||
// MARK: - HardcoreData
|
||||
|
||||
public extension HardcoreData {
|
||||
|
||||
// MARK: Public
|
||||
|
||||
/**
|
||||
Using the defaultStack, begins a transaction asynchronously where NSManagedObject creates, updates, and deletes can be made.
|
||||
|
||||
:param: closure the block where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent NSManagedObjectContext.
|
||||
*/
|
||||
public static func beginAsynchronous(closure: (transaction: AsynchronousDataTransaction) -> Void) {
|
||||
|
||||
self.defaultStack.beginAsynchronous(closure)
|
||||
}
|
||||
|
||||
/**
|
||||
Using the defaultStack, begins a transaction asynchronously where NSManagedObject creates, updates, and deletes can be made.
|
||||
|
||||
:param: closure the block where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent NSManagedObjectContext.
|
||||
:returns: a SaveResult value indicating success or failure, or nil if the transaction was not comitted synchronously
|
||||
*/
|
||||
public static func beginSynchronous(closure: (transaction: SynchronousDataTransaction) -> Void) -> SaveResult? {
|
||||
|
||||
return self.defaultStack.beginSynchronous(closure)
|
||||
}
|
||||
|
||||
/**
|
||||
Using the defaultStack, begins a non-contiguous transaction where NSManagedObject creates, updates, and deletes can be made. This is useful for making temporary changes, such as partially filled forms. A detached transaction object should typically be only used from the main queue.
|
||||
|
||||
:returns: a DetachedDataTransaction instance where creates, updates, and deletes can be made.
|
||||
*/
|
||||
public static func beginDetached() -> DetachedDataTransaction {
|
||||
|
||||
return self.defaultStack.beginDetached()
|
||||
}
|
||||
}
|
||||
71
HardcoreData/Saving and Processing/SaveResult.swift
Normal file
71
HardcoreData/Saving and Processing/SaveResult.swift
Normal file
@@ -0,0 +1,71 @@
|
||||
//
|
||||
// SaveResult.swift
|
||||
// HardcoreData
|
||||
//
|
||||
// Copyright (c) 2014 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: - SaveResult
|
||||
|
||||
public enum SaveResult {
|
||||
|
||||
case Success(hasChanges: Bool)
|
||||
case Failure(NSError)
|
||||
|
||||
internal init(hasChanges: Bool) {
|
||||
|
||||
self = .Success(hasChanges: hasChanges)
|
||||
}
|
||||
|
||||
internal init(_ error: NSError) {
|
||||
|
||||
self = .Failure(error)
|
||||
}
|
||||
|
||||
internal init(_ errorCode: HardcoreDataErrorCode) {
|
||||
|
||||
self.init(errorCode, userInfo: nil)
|
||||
}
|
||||
|
||||
internal init(_ errorCode: HardcoreDataErrorCode, userInfo: [NSObject: AnyObject]?) {
|
||||
|
||||
self.init(NSError(
|
||||
hardcoreDataErrorCode: errorCode,
|
||||
userInfo: userInfo))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - SaveResult: BooleanType
|
||||
|
||||
extension SaveResult: BooleanType {
|
||||
|
||||
public var boolValue: Bool {
|
||||
|
||||
switch self {
|
||||
case .Success: return true
|
||||
case .Failure: return false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
//
|
||||
// SynchronousDataTransaction.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: - SynchronousDataTransaction
|
||||
|
||||
/**
|
||||
The SynchronousDataTransaction provides an interface for NSManagedObject creates, updates, and deletes. A transaction object should typically be only used from within a transaction block initiated from DataStack.beginSynchronous(_:), or from HardcoreData.beginSynchronous(_:).
|
||||
*/
|
||||
public final class SynchronousDataTransaction: BaseDataTransaction {
|
||||
|
||||
// MARK: Public
|
||||
|
||||
/**
|
||||
Saves the transaction changes and waits for completion synchronously. This method should not be used after the commit() method was already called once.
|
||||
|
||||
:returns: a SaveResult value indicating success or failure.
|
||||
*/
|
||||
public func commit() {
|
||||
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to commit a \(typeName(self)) outside its designated queue.")
|
||||
HardcoreData.assert(!self.isCommitted, "Attempted to commit a \(typeName(self)) more than once.")
|
||||
|
||||
self.isCommitted = true
|
||||
self.result = self.context.saveSynchronously()
|
||||
}
|
||||
|
||||
/**
|
||||
Begins a child transaction synchronously where NSManagedObject creates, updates, and deletes can be made. This method should not be used after the commit() method was already called once.
|
||||
|
||||
:param: closure the block where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent NSManagedObjectContext.
|
||||
:returns: a SaveResult value indicating success or failure, or nil if the transaction was not comitted synchronously
|
||||
*/
|
||||
public func beginSynchronous(closure: (transaction: SynchronousDataTransaction) -> Void) -> SaveResult? {
|
||||
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to begin a child transaction from a \(typeName(self)) outside its designated queue.")
|
||||
HardcoreData.assert(!self.isCommitted, "Attempted to begin a child transaction from an already committed \(typeName(self)).")
|
||||
|
||||
return SynchronousDataTransaction(
|
||||
mainContext: self.context,
|
||||
queue: self.childTransactionQueue,
|
||||
closure: closure).performAndWait()
|
||||
}
|
||||
|
||||
|
||||
// MARK: BaseDataTransaction
|
||||
|
||||
/**
|
||||
Creates a new NSManagedObject with the specified entity type. This method should not be used after the commit() method was already called once.
|
||||
|
||||
:param: entity the NSManagedObject type to be created
|
||||
:returns: a new NSManagedObject instance of the specified entity type.
|
||||
*/
|
||||
public override func create<T: NSManagedObject>(entity: T.Type) -> T {
|
||||
|
||||
HardcoreData.assert(!self.isCommitted, "Attempted to create an entity of type <\(entity)> from an already committed \(typeName(self)).")
|
||||
|
||||
return super.create(entity)
|
||||
}
|
||||
|
||||
/**
|
||||
Returns an editable proxy of a specified NSManagedObject. This method should not be used after the commit() method was already called once.
|
||||
|
||||
:param: object the NSManagedObject type to be edited
|
||||
:returns: an editable proxy for the specified NSManagedObject.
|
||||
*/
|
||||
public override func fetch<T: NSManagedObject>(object: T?) -> T? {
|
||||
|
||||
HardcoreData.assert(!self.isCommitted, "Attempted to update an entity of type \(typeName(object)) from an already committed \(typeName(self)).")
|
||||
|
||||
return super.fetch(object)
|
||||
}
|
||||
|
||||
/**
|
||||
Deletes a specified NSManagedObject. This method should not be used after the commit() method was already called once.
|
||||
|
||||
:param: object the NSManagedObject type to be deleted
|
||||
*/
|
||||
public override func delete(object: NSManagedObject?) {
|
||||
|
||||
HardcoreData.assert(!self.isCommitted, "Attempted to delete an entity of type \(typeName(object)) from an already committed \(typeName(self)).")
|
||||
|
||||
super.delete(object)
|
||||
}
|
||||
|
||||
/**
|
||||
Rolls back the transaction by resetting the NSManagedObjectContext. After calling this method, all NSManagedObjects fetched within the transaction will become invalid. This method should not be used after the commit() method was already called once.
|
||||
*/
|
||||
public override func rollback() {
|
||||
|
||||
HardcoreData.assert(!self.isCommitted, "Attempted to rollback an already committed \(typeName(self)).")
|
||||
|
||||
super.rollback()
|
||||
}
|
||||
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
internal func performAndWait() -> SaveResult? {
|
||||
|
||||
self.transactionQueue.sync {
|
||||
|
||||
self.closure(transaction: self)
|
||||
if !self.isCommitted && self.hasChanges {
|
||||
|
||||
HardcoreData.log(.Warning, message: "The closure for the \(typeName(self)) completed without being committed. All changes made within the transaction were discarded.")
|
||||
}
|
||||
}
|
||||
return self.result
|
||||
}
|
||||
|
||||
internal init(mainContext: NSManagedObjectContext, queue: GCDQueue, closure: (transaction: SynchronousDataTransaction) -> Void) {
|
||||
|
||||
self.closure = closure
|
||||
|
||||
super.init(mainContext: mainContext, queue: queue)
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let closure: (transaction: SynchronousDataTransaction) -> Void
|
||||
}
|
||||
Reference in New Issue
Block a user