Swift Package Manager support

This commit is contained in:
John Estropia
2016-03-09 17:47:46 +09:00
parent 40c27b9fcb
commit c86ca06bd4
74 changed files with 42 additions and 25 deletions

View File

@@ -0,0 +1,256 @@
//
// AsynchronousDataTransaction.swift
// CoreStore
//
// Copyright © 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
#if USE_FRAMEWORKS
import GCDKit
#endif
// 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 `CoreStore.beginAsynchronous(_:)`.
*/
public final class AsynchronousDataTransaction: BaseDataTransaction {
/**
Saves the transaction changes. This method should not be used after the `commit()` method was already called once.
- parameter 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 = { _ in }) {
CoreStore.assert(
self.transactionQueue.isCurrentExecutionContext(),
"Attempted to commit a \(typeName(self)) outside its designated queue."
)
CoreStore.assert(
!self.isCommitted,
"Attempted to commit a \(typeName(self)) more than once."
)
self.isCommitted = true
let group = GCDGroup()
group.enter()
self.context.saveAsynchronouslyWithCompletion { (result) -> Void in
self.result = result
completion(result: result)
group.leave()
}
group.wait()
}
/**
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.
- parameter 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? {
CoreStore.assert(
self.transactionQueue.isCurrentExecutionContext(),
"Attempted to begin a child transaction from a \(typeName(self)) outside its designated queue."
)
CoreStore.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.
- parameter into: the `Into` clause indicating the destination `NSManagedObject` entity type and the destination configuration
- returns: a new `NSManagedObject` instance of the specified entity type.
*/
public override func create<T: NSManagedObject>(into: Into<T>) -> T {
CoreStore.assert(
!self.isCommitted,
"Attempted to create an entity of type \(typeName(T)) from an already committed \(typeName(self))."
)
return super.create(into)
}
/**
Returns an editable proxy of a specified `NSManagedObject`. This method should not be used after the `commit()` method was already called once.
- parameter object: the `NSManagedObject` type to be edited
- returns: an editable proxy for the specified `NSManagedObject`.
*/
@warn_unused_result
public override func edit<T: NSManagedObject>(object: T?) -> T? {
CoreStore.assert(
!self.isCommitted,
"Attempted to update an entity of type \(typeName(object)) from an already committed \(typeName(self))."
)
return super.edit(object)
}
/**
Returns an editable proxy of the object with the specified `NSManagedObjectID`. This method should not be used after the `commit()` method was already called once.
- parameter into: an `Into` clause specifying the entity type
- parameter objectID: the `NSManagedObjectID` for the object to be edited
- returns: an editable proxy for the specified `NSManagedObject`.
*/
@warn_unused_result
public override func edit<T: NSManagedObject>(into: Into<T>, _ objectID: NSManagedObjectID) -> T? {
CoreStore.assert(
!self.isCommitted,
"Attempted to update an entity of type \(typeName(T)) from an already committed \(typeName(self))."
)
return super.edit(into, objectID)
}
/**
Deletes a specified `NSManagedObject`. This method should not be used after the `commit()` method was already called once.
- parameter object: the `NSManagedObject` type to be deleted
*/
public override func delete(object: NSManagedObject?) {
CoreStore.assert(
!self.isCommitted,
"Attempted to delete an entity of type \(typeName(object)) from an already committed \(typeName(self))."
)
super.delete(object)
}
/**
Deletes the specified `NSManagedObject`s.
- parameter object1: the `NSManagedObject` type to be deleted
- parameter object2: another `NSManagedObject` type to be deleted
- parameter objects: other `NSManagedObject`s type to be deleted
*/
public override func delete(object1: NSManagedObject?, _ object2: NSManagedObject?, _ objects: NSManagedObject?...) {
CoreStore.assert(
!self.isCommitted,
"Attempted to delete an entities from an already committed \(typeName(self))."
)
super.delete(([object1, object2] + objects).flatMap { $0 })
}
/**
Deletes the specified `NSManagedObject`s.
- parameter objects: the `NSManagedObject`s type to be deleted
*/
public override func delete<S: SequenceType where S.Generator.Element: NSManagedObject>(objects: S) {
CoreStore.assert(
!self.isCommitted,
"Attempted to delete an entities from an already committed \(typeName(self))."
)
super.delete(objects)
}
// MARK: Internal
internal init(mainContext: NSManagedObjectContext, queue: GCDQueue, closure: (transaction: AsynchronousDataTransaction) -> Void) {
self.closure = closure
super.init(mainContext: mainContext, queue: queue, supportsUndo: false, bypassesQueueing: false)
}
internal func perform() {
self.transactionQueue.async {
self.closure(transaction: self)
if !self.isCommitted && self.hasChanges {
CoreStore.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 {
CoreStore.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
// MARK: Deprecated
@available(*, deprecated=1.3.4, obsoleted=2.0.0, message="Resetting the context is inherently unsafe. This method will be removed in the near future. Use `beginUnsafe()` to create transactions with `undo` support.")
public func rollback() {
CoreStore.assert(
!self.isCommitted,
"Attempted to rollback an already committed \(typeName(self))."
)
CoreStore.assert(
self.transactionQueue.isCurrentExecutionContext(),
"Attempted to rollback a \(typeName(self)) outside its designated queue."
)
self.context.reset()
}
}

View File

@@ -0,0 +1,482 @@
//
// BaseDataTransaction.swift
// CoreStore
//
// Copyright © 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
#if USE_FRAMEWORKS
import GCDKit
#endif
// 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
/**
Indicates if the transaction has pending changes
*/
public var hasChanges: Bool {
return self.context.hasChanges
}
/**
Creates a new `NSManagedObject` with the specified entity type.
- parameter into: the `Into` clause indicating the destination `NSManagedObject` entity type and the destination configuration
- returns: a new `NSManagedObject` instance of the specified entity type.
*/
public func create<T: NSManagedObject>(into: Into<T>) -> T {
CoreStore.assert(
self.isRunningInAllowedQueue(),
"Attempted to create an entity of type \(typeName(T)) outside its designated queue."
)
let context = self.context
let entityClass = (into.entityClass as! NSManagedObject.Type)
if into.inferStoreIfPossible {
switch context.parentStack!.persistentStoreForEntityClass(entityClass, configuration: nil, inferStoreIfPossible: true) {
case (let persistentStore?, _):
let object = entityClass.createInContext(context) as! T
context.assignObject(object, toPersistentStore: persistentStore)
return object
case (.None, true):
fatalError("Attempted to create an entity of type \(typeName(entityClass)) with ambiguous destination persistent store, but the configuration name was not specified.")
default:
fatalError("Attempted to create an entity of type \(typeName(entityClass)), but a destination persistent store containing the entity type could not be found.")
}
}
else {
switch context.parentStack!.persistentStoreForEntityClass(entityClass, configuration: into.configuration, inferStoreIfPossible: false) {
case (let persistentStore?, _):
let object = entityClass.createInContext(context) as! T
context.assignObject(object, toPersistentStore: persistentStore)
return object
default:
if let configuration = into.configuration {
fatalError("Attempted to create an entity of type \(typeName(entityClass)) into the configuration \"\(configuration)\", which it doesn't belong to.")
}
else {
fatalError("Attempted to create an entity of type \(typeName(entityClass)) into the default configuration, which it doesn't belong to.")
}
}
}
}
/**
Returns an editable proxy of a specified `NSManagedObject`.
- parameter object: the `NSManagedObject` type to be edited
- returns: an editable proxy for the specified `NSManagedObject`.
*/
@warn_unused_result
public func edit<T: NSManagedObject>(object: T?) -> T? {
CoreStore.assert(
self.isRunningInAllowedQueue(),
"Attempted to update an entity of type \(typeName(object)) outside its designated queue."
)
guard let object = object else {
return nil
}
return self.context.fetchExisting(object)
}
/**
Returns an editable proxy of the object with the specified `NSManagedObjectID`.
- parameter into: an `Into` clause specifying the entity type
- parameter objectID: the `NSManagedObjectID` for the object to be edited
- returns: an editable proxy for the specified `NSManagedObject`.
*/
@warn_unused_result
public func edit<T: NSManagedObject>(into: Into<T>, _ objectID: NSManagedObjectID) -> T? {
CoreStore.assert(
self.isRunningInAllowedQueue(),
"Attempted to update an entity of type \(typeName(T)) outside its designated queue."
)
CoreStore.assert(
into.inferStoreIfPossible
|| (into.configuration ?? Into.defaultConfigurationName) == objectID.persistentStore?.configurationName,
"Attempted to update an entity of type \(typeName(T)) but the specified persistent store do not match the `NSManagedObjectID`."
)
return self.fetchExisting(objectID) as? T
}
/**
Deletes a specified `NSManagedObject`.
- parameter object: the `NSManagedObject` to be deleted
*/
public func delete(object: NSManagedObject?) {
CoreStore.assert(
self.isRunningInAllowedQueue(),
"Attempted to delete an entity outside its designated queue."
)
guard let object = object else {
return
}
self.context.fetchExisting(object)?.deleteFromContext()
}
/**
Deletes the specified `NSManagedObject`s.
- parameter object1: the `NSManagedObject` to be deleted
- parameter object2: another `NSManagedObject` to be deleted
- parameter objects: other `NSManagedObject`s to be deleted
*/
public func delete(object1: NSManagedObject?, _ object2: NSManagedObject?, _ objects: NSManagedObject?...) {
self.delete(([object1, object2] + objects).flatMap { $0 })
}
/**
Deletes the specified `NSManagedObject`s.
- parameter objects: the `NSManagedObject`s to be deleted
*/
public func delete<S: SequenceType where S.Generator.Element: NSManagedObject>(objects: S) {
CoreStore.assert(
self.isRunningInAllowedQueue(),
"Attempted to delete entities outside their designated queue."
)
let context = self.context
objects.forEach { context.fetchExisting($0)?.deleteFromContext() }
}
/**
Refreshes all registered objects `NSManagedObject`s in the transaction.
*/
public func refreshAllObjectsAsFaults() {
CoreStore.assert(
self.isRunningInAllowedQueue(),
"Attempted to refresh entities outside their designated queue."
)
self.context.refreshAllObjectsAsFaults()
}
// MARK: Inspecting Pending Objects
/**
Returns all pending `NSManagedObject`s that were inserted to the transaction. This method should not be called after the `commit()` method was called.
- returns: a `Set` of pending `NSManagedObject`s that were inserted to the transaction.
*/
public func insertedObjects() -> Set<NSManagedObject> {
CoreStore.assert(
self.transactionQueue.isCurrentExecutionContext(),
"Attempted to access inserted objects from a \(typeName(self)) outside its designated queue."
)
CoreStore.assert(
!self.isCommitted,
"Attempted to access inserted objects from an already committed \(typeName(self))."
)
return self.context.insertedObjects
}
/**
Returns all pending `NSManagedObject`s of the specified type that were inserted to the transaction. This method should not be called after the `commit()` method was called.
- parameter entity: the `NSManagedObject` subclass to filter
- returns: a `Set` of pending `NSManagedObject`s of the specified type that were inserted to the transaction.
*/
public func insertedObjects<T: NSManagedObject>(entity: T.Type) -> Set<T> {
CoreStore.assert(
self.transactionQueue.isCurrentExecutionContext(),
"Attempted to access inserted objects from a \(typeName(self)) outside its designated queue."
)
CoreStore.assert(
!self.isCommitted,
"Attempted to access inserted objects from an already committed \(typeName(self))."
)
return Set(self.context.insertedObjects.flatMap { $0 as? T })
}
/**
Returns all pending `NSManagedObjectID`s that were inserted to the transaction. This method should not be called after the `commit()` method was called.
- returns: a `Set` of pending `NSManagedObjectID`s that were inserted to the transaction.
*/
public func insertedObjectIDs() -> Set<NSManagedObjectID> {
CoreStore.assert(
self.transactionQueue.isCurrentExecutionContext(),
"Attempted to access inserted object IDs from a \(typeName(self)) outside its designated queue."
)
CoreStore.assert(
!self.isCommitted,
"Attempted to access inserted objects IDs from an already committed \(typeName(self))."
)
return Set(self.context.insertedObjects.map { $0.objectID })
}
/**
Returns all pending `NSManagedObjectID`s of the specified type that were inserted to the transaction. This method should not be called after the `commit()` method was called.
- parameter entity: the `NSManagedObject` subclass to filter
- returns: a `Set` of pending `NSManagedObjectID`s of the specified type that were inserted to the transaction.
*/
public func insertedObjectIDs<T: NSManagedObject>(entity: T.Type) -> Set<NSManagedObjectID> {
CoreStore.assert(
self.transactionQueue.isCurrentExecutionContext(),
"Attempted to access inserted object IDs from a \(typeName(self)) outside its designated queue."
)
CoreStore.assert(
!self.isCommitted,
"Attempted to access inserted objects IDs from an already committed \(typeName(self))."
)
return Set(self.context.insertedObjects.flatMap { $0 as? T }.map { $0.objectID })
}
/**
Returns all pending `NSManagedObject`s that were updated in the transaction. This method should not be called after the `commit()` method was called.
- returns: a `Set` of pending `NSManagedObject`s that were updated to the transaction.
*/
public func updatedObjects() -> Set<NSManagedObject> {
CoreStore.assert(
self.transactionQueue.isCurrentExecutionContext(),
"Attempted to access updated objects from a \(typeName(self)) outside its designated queue."
)
CoreStore.assert(
!self.isCommitted,
"Attempted to access updated objects from an already committed \(typeName(self))."
)
return self.context.updatedObjects
}
/**
Returns all pending `NSManagedObject`s of the specified type that were updated in the transaction. This method should not be called after the `commit()` method was called.
- parameter entity: the `NSManagedObject` subclass to filter
- returns: a `Set` of pending `NSManagedObject`s of the specified type that were updated in the transaction.
*/
public func updatedObjects<T: NSManagedObject>(entity: T.Type) -> Set<T> {
CoreStore.assert(
self.transactionQueue.isCurrentExecutionContext(),
"Attempted to access updated objects from a \(typeName(self)) outside its designated queue."
)
CoreStore.assert(
!self.isCommitted,
"Attempted to access updated objects from an already committed \(typeName(self))."
)
return Set(self.context.updatedObjects.flatMap { $0 as? T })
}
/**
Returns all pending `NSManagedObjectID`s that were updated in the transaction. This method should not be called after the `commit()` method was called.
- returns: a `Set` of pending `NSManagedObjectID`s that were updated in the transaction.
*/
public func updatedObjectIDs() -> Set<NSManagedObjectID> {
CoreStore.assert(
self.transactionQueue.isCurrentExecutionContext(),
"Attempted to access updated object IDs from a \(typeName(self)) outside its designated queue."
)
CoreStore.assert(
!self.isCommitted,
"Attempted to access updated object IDs from an already committed \(typeName(self))."
)
return Set(self.context.updatedObjects.map { $0.objectID })
}
/**
Returns all pending `NSManagedObjectID`s of the specified type that were updated in the transaction. This method should not be called after the `commit()` method was called.
- parameter entity: the `NSManagedObject` subclass to filter
- returns: a `Set` of pending `NSManagedObjectID`s of the specified type that were updated in the transaction.
*/
public func updatedObjectIDs<T: NSManagedObject>(entity: T.Type) -> Set<NSManagedObjectID> {
CoreStore.assert(
self.transactionQueue.isCurrentExecutionContext(),
"Attempted to access updated object IDs from a \(typeName(self)) outside its designated queue."
)
CoreStore.assert(
!self.isCommitted,
"Attempted to access updated object IDs from an already committed \(typeName(self))."
)
return Set(self.context.updatedObjects.flatMap { $0 as? T }.map { $0.objectID })
}
/**
Returns all pending `NSManagedObject`s that were deleted from the transaction. This method should not be called after the `commit()` method was called.
- returns: a `Set` of pending `NSManagedObject`s that were deleted from the transaction.
*/
public func deletedObjects() -> Set<NSManagedObject> {
CoreStore.assert(
self.transactionQueue.isCurrentExecutionContext(),
"Attempted to access deleted objects from a \(typeName(self)) outside its designated queue."
)
CoreStore.assert(
!self.isCommitted,
"Attempted to access deleted objects from an already committed \(typeName(self))."
)
return self.context.deletedObjects
}
/**
Returns all pending `NSManagedObject`s of the specified type that were deleted from the transaction. This method should not be called after the `commit()` method was called.
- parameter entity: the `NSManagedObject` subclass to filter
- returns: a `Set` of pending `NSManagedObject`s of the specified type that were deleted from the transaction.
*/
public func deletedObjects<T: NSManagedObject>(entity: T.Type) -> Set<T> {
CoreStore.assert(
self.transactionQueue.isCurrentExecutionContext(),
"Attempted to access deleted objects from a \(typeName(self)) outside its designated queue."
)
CoreStore.assert(
!self.isCommitted,
"Attempted to access deleted objects from an already committed \(typeName(self))."
)
return Set(self.context.deletedObjects.flatMap { $0 as? T })
}
/**
Returns all pending `NSManagedObjectID`s of the specified type that were deleted from the transaction. This method should not be called after the `commit()` method was called.
- parameter entity: the `NSManagedObject` subclass to filter
- returns: a `Set` of pending `NSManagedObjectID`s of the specified type that were deleted from the transaction.
*/
public func deletedObjectIDs() -> Set<NSManagedObjectID> {
CoreStore.assert(
self.transactionQueue.isCurrentExecutionContext(),
"Attempted to access deleted object IDs from a \(typeName(self)) outside its designated queue."
)
CoreStore.assert(
!self.isCommitted,
"Attempted to access deleted object IDs from an already committed \(typeName(self))."
)
return Set(self.context.deletedObjects.map { $0.objectID })
}
/**
Returns all pending `NSManagedObjectID`s of the specified type that were deleted from the transaction. This method should not be called after the `commit()` method was called.
- parameter entity: the `NSManagedObject` subclass to filter
- returns: a `Set` of pending `NSManagedObjectID`s of the specified type that were deleted from the transaction.
*/
public func deletedObjectIDs<T: NSManagedObject>(entity: T.Type) -> Set<NSManagedObjectID> {
CoreStore.assert(
self.transactionQueue.isCurrentExecutionContext(),
"Attempted to access deleted object IDs from a \(typeName(self)) outside its designated queue."
)
CoreStore.assert(
!self.isCommitted,
"Attempted to access deleted object IDs from an already committed \(typeName(self))."
)
return Set(self.context.deletedObjects.flatMap { $0 as? T }.map { $0.objectID })
}
// MARK: Internal
internal let context: NSManagedObjectContext
internal let transactionQueue: GCDQueue
internal let childTransactionQueue: GCDQueue = .createSerial("com.corestore.datastack.childtransactionqueue")
internal let supportsUndo: Bool
internal let bypassesQueueing: Bool
internal var isCommitted = false
internal var result: SaveResult?
internal init(mainContext: NSManagedObjectContext, queue: GCDQueue, supportsUndo: Bool, bypassesQueueing: Bool) {
let context = mainContext.temporaryContextInTransactionWithConcurrencyType(
queue == .Main
? .MainQueueConcurrencyType
: .PrivateQueueConcurrencyType
)
self.transactionQueue = queue
self.context = context
self.supportsUndo = supportsUndo
self.bypassesQueueing = bypassesQueueing
context.parentTransaction = self
if !supportsUndo {
context.undoManager = nil
}
else if context.undoManager == nil {
context.undoManager = NSUndoManager()
}
}
internal func isRunningInAllowedQueue() -> Bool {
return self.bypassesQueueing || self.transactionQueue.isCurrentExecutionContext()
}
}

View File

@@ -0,0 +1,83 @@
//
// CoreStore+Transaction.swift
// CoreStore
//
// Copyright © 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: - CoreStore
public extension CoreStore {
/**
Using the `defaultStack`, begins a transaction asynchronously where `NSManagedObject` creates, updates, and deletes can be made.
- parameter 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.
- parameter 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. An unsafe transaction object should typically be only used from the main queue.
- prameter supportsUndo: `undo()`, `redo()`, and `rollback()` methods are only available when this parameter is `true`, otherwise those method will raise an exception. Defaults to `false`. Note that turning on Undo support may heavily impact performance especially on iOS or watchOS where memory is limited.
- returns: a `UnsafeDataTransaction` instance where creates, updates, and deletes can be made.
*/
@warn_unused_result
public static func beginUnsafe(supportsUndo supportsUndo: Bool = false) -> UnsafeDataTransaction {
return self.defaultStack.beginUnsafe(supportsUndo: supportsUndo)
}
/**
Refreshes all registered objects `NSManagedObject`s in the `DataStack`.
*/
public static func refreshAllObjectsAsFaults() {
self.defaultStack.refreshAllObjectsAsFaults()
}
// MARK: Deprecated
@available(*, deprecated=1.3.1, obsoleted=2.0.0, renamed="beginUnsafe")
@warn_unused_result
public static func beginDetached() -> UnsafeDataTransaction {
return self.beginUnsafe()
}
}

View File

@@ -0,0 +1,105 @@
//
// DataStack+Transaction.swift
// CoreStore
//
// Copyright © 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
#if USE_FRAMEWORKS
import GCDKit
#endif
// MARK: - DataStack
public extension DataStack {
/**
Begins a transaction asynchronously where `NSManagedObject` creates, updates, and deletes can be made.
- parameter 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) {
AsynchronousDataTransaction(
mainContext: self.rootSavingContext,
queue: self.childTransactionQueue,
closure: closure).perform()
}
/**
Begins a transaction synchronously where `NSManagedObject` creates, updates, and deletes can be made.
- parameter 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? {
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.
- prameter supportsUndo: `undo()`, `redo()`, and `rollback()` methods are only available when this parameter is `true`, otherwise those method will raise an exception. Defaults to `false`. Note that turning on Undo support may heavily impact performance especially on iOS or watchOS where memory is limited.
- returns: a `UnsafeDataTransaction` instance where creates, updates, and deletes can be made.
*/
@warn_unused_result
public func beginUnsafe(supportsUndo supportsUndo: Bool = false) -> UnsafeDataTransaction {
return UnsafeDataTransaction(
mainContext: self.rootSavingContext,
queue: .createSerial(
"com.coreStore.dataStack.unsafeTransactionQueue",
targetQueue: .UserInitiated
),
supportsUndo: supportsUndo
)
}
/**
Refreshes all registered objects `NSManagedObject`s in the `DataStack`.
*/
public func refreshAllObjectsAsFaults() {
CoreStore.assert(
NSThread.isMainThread(),
"Attempted to refresh entities outside their designated queue."
)
self.mainContext.refreshAllObjectsAsFaults()
}
// MARK: Deprecated
@available(*, deprecated=1.3.1, obsoleted=2.0.0, renamed="beginUnsafe")
@warn_unused_result
public func beginDetached() -> UnsafeDataTransaction {
return self.beginUnsafe()
}
}

View File

@@ -0,0 +1,146 @@
//
// Into.swift
// CoreStore
//
// Copyright © 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
// MARK: - Into
/**
An `Into` clause contains the destination entity and destination persistent store for a `create(...)` method. A common usage is to just indicate the entity:
```
let person = transaction.create(Into(MyPersonEntity))
```
For cases where multiple `NSPersistentStore`s contain the same entity, the destination configuration's name needs to be specified as well:
```
let person = transaction.create(Into<MyPersonEntity>("Configuration1"))
```
*/
public struct Into<T: NSManagedObject> {
/**
Initializes an `Into` clause.
Sample Usage:
```
let person = transaction.create(Into<MyPersonEntity>())
```
*/
public init(){
self.configuration = nil
self.inferStoreIfPossible = true
self.entityClass = T.self
}
/**
Initializes an `Into` clause with the specified entity type.
Sample Usage:
```
let person = transaction.create(Into(MyPersonEntity))
```
- parameter entity: the `NSManagedObject` type to be created
*/
public init(_ entity: T.Type) {
self.configuration = nil
self.inferStoreIfPossible = true
self.entityClass = entity
}
/**
Initializes an `Into` clause with the specified entity class.
Sample Usage:
```
let person = transaction.create(Into(MyPersonEntity))
```
- parameter entityClass: the `NSManagedObject` class type to be created
*/
public init(_ entityClass: AnyClass) {
self.configuration = nil
self.inferStoreIfPossible = true
self.entityClass = entityClass
}
/**
Initializes an `Into` clause with the specified configuration.
Sample Usage:
```
let person = transaction.create(Into<MyPersonEntity>("Configuration1"))
```
- parameter configuration: the `NSPersistentStore` configuration name to associate the object to. This parameter is required if multiple configurations contain the created `NSManagedObject`'s entity type. Set to `nil` to use the default configuration.
*/
public init(_ configuration: String?) {
self.configuration = configuration
self.inferStoreIfPossible = false
self.entityClass = T.self
}
/**
Initializes an `Into` clause with the specified entity type and configuration.
Sample Usage:
```
let person = transaction.create(Into(MyPersonEntity.self, "Configuration1"))
```
- parameter entity: the `NSManagedObject` type to be created
- parameter configuration: the `NSPersistentStore` configuration name to associate the object to. This parameter is required if multiple configurations contain the created `NSManagedObject`'s entity type. Set to `nil` to use the default configuration.
*/
public init(_ entity: T.Type, _ configuration: String?) {
self.configuration = configuration
self.inferStoreIfPossible = false
self.entityClass = entity
}
/**
Initializes an `Into` clause with the specified entity class and configuration.
Sample Usage:
```
let person = transaction.create(Into(MyPersonEntity.self, "Configuration1"))
```
- parameter entityClass: the `NSManagedObject` class type to be created
- parameter configuration: the `NSPersistentStore` configuration name to associate the object to. This parameter is required if multiple configurations contain the created `NSManagedObject`'s entity type. Set to `nil` to use the default configuration.
*/
public init(_ entityClass: AnyClass, _ configuration: String?) {
self.configuration = configuration
self.inferStoreIfPossible = false
self.entityClass = entityClass
}
// MARK: Internal
internal static var defaultConfigurationName: String {
return "PF_DEFAULT_CONFIGURATION_NAME"
}
internal let entityClass: AnyClass
internal let configuration: String?
internal let inferStoreIfPossible: Bool
}

View File

@@ -0,0 +1,61 @@
//
// NSManagedObject+Transaction.swift
// CoreStore
//
// Copyright © 2016 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: - NSManagedObject
public extension NSManagedObject {
/**
Returns this object's parent `UnsafeDataTransaction` instance if it was created from one. Returns `nil` if the parent transaction is either an `AsynchronousDataTransaction` or a `SynchronousDataTransaction`, or if the object is not managed by CoreStore.
When using an `UnsafeDataTransaction` and passing around a temporary object, you can use this property to execute fetches and updates to the transaction without having to pass around both the object and the transaction instances.
Note that the internal reference to the transaction is `weak`, and it is still the developer's responsibility to retain a strong reference to the `UnsafeDataTransaction`.
*/
public var unsafeDataTransaction: UnsafeDataTransaction? {
return self.managedObjectContext?.parentTransaction as? UnsafeDataTransaction
}
// MARK: Internal
internal dynamic class func createInContext(context: NSManagedObjectContext) -> Self {
return self.init(
entity: context.entityDescriptionForEntityType(self)!,
insertIntoManagedObjectContext: context
)
}
internal func deleteFromContext() {
self.managedObjectContext?.deleteObject(self)
}
}

View File

@@ -0,0 +1,109 @@
//
// SaveResult.swift
// CoreStore
//
// Copyright © 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
/**
The `SaveResult` indicates the result of a `commit(...)` for a transaction.
The `SaveResult` can be treated as a boolean:
```
CoreStore.beginAsynchronous { transaction in
// ...
let result = transaction.commit()
if result {
// succeeded
}
else {
// failed
}
}
```
or as an `enum`, where the resulting associated object can also be inspected:
```
CoreStore.beginAsynchronous { transaction in
// ...
let result = transaction.commit()
switch result {
case .Success(let hasChanges):
// hasChanges indicates if there were changes or not
case .Failure(let error):
// error is the NSError instance for the failure
}
}
```
*/
public enum SaveResult {
/**
`SaveResult.Success` indicates that the `commit()` for the transaction succeeded, either because the save succeeded or because there were no changes to save. The associated value `hasChanges` indicates if there were saved changes or not.
*/
case Success(hasChanges: Bool)
/**
`SaveResult.Failure` indicates that the `commit()` for the transaction failed. The associated object for this value is the related `NSError` instance.
*/
case Failure(NSError)
// MARK: Internal
internal init(hasChanges: Bool) {
self = .Success(hasChanges: hasChanges)
}
internal init(_ error: NSError) {
self = .Failure(error)
}
internal init(_ errorCode: CoreStoreErrorCode) {
self.init(errorCode, userInfo: nil)
}
internal init(_ errorCode: CoreStoreErrorCode, userInfo: [NSObject: AnyObject]?) {
self.init(NSError(coreStoreErrorCode: errorCode, userInfo: userInfo))
}
}
// MARK: - SaveResult: BooleanType
extension SaveResult: BooleanType {
public var boolValue: Bool {
switch self {
case .Success: return true
case .Failure: return false
}
}
}

View File

@@ -0,0 +1,242 @@
//
// SynchronousDataTransaction.swift
// CoreStore
//
// Copyright © 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
#if USE_FRAMEWORKS
import GCDKit
#endif
// 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 `CoreStore.beginSynchronous(_:)`.
*/
public final class SynchronousDataTransaction: BaseDataTransaction {
/**
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` containing the success or failure information
*/
public func commitAndWait() -> SaveResult {
CoreStore.assert(
self.transactionQueue.isCurrentExecutionContext(),
"Attempted to commit a \(typeName(self)) outside its designated queue."
)
CoreStore.assert(
!self.isCommitted,
"Attempted to commit a \(typeName(self)) more than once."
)
self.isCommitted = true
let result = self.context.saveSynchronously()
self.result = result
return result
}
/**
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.
- parameter 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? {
CoreStore.assert(
self.transactionQueue.isCurrentExecutionContext(),
"Attempted to begin a child transaction from a \(typeName(self)) outside its designated queue."
)
CoreStore.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.
- parameter into: the `Into` clause indicating the destination `NSManagedObject` entity type and the destination configuration
- returns: a new `NSManagedObject` instance of the specified entity type.
*/
public override func create<T: NSManagedObject>(into: Into<T>) -> T {
CoreStore.assert(
!self.isCommitted,
"Attempted to create an entity of type \(typeName(T)) from an already committed \(typeName(self))."
)
return super.create(into)
}
/**
Returns an editable proxy of a specified `NSManagedObject`. This method should not be used after the `commit()` method was already called once.
- parameter object: the `NSManagedObject` type to be edited
- returns: an editable proxy for the specified `NSManagedObject`.
*/
@warn_unused_result
public override func edit<T: NSManagedObject>(object: T?) -> T? {
CoreStore.assert(
!self.isCommitted,
"Attempted to update an entity of type \(typeName(object)) from an already committed \(typeName(self))."
)
return super.edit(object)
}
/**
Returns an editable proxy of the object with the specified `NSManagedObjectID`. This method should not be used after the `commit()` method was already called once.
- parameter into: an `Into` clause specifying the entity type
- parameter objectID: the `NSManagedObjectID` for the object to be edited
- returns: an editable proxy for the specified `NSManagedObject`.
*/
@warn_unused_result
public override func edit<T: NSManagedObject>(into: Into<T>, _ objectID: NSManagedObjectID) -> T? {
CoreStore.assert(
!self.isCommitted,
"Attempted to update an entity of type \(typeName(T)) from an already committed \(typeName(self))."
)
return super.edit(into, objectID)
}
/**
Deletes a specified `NSManagedObject`. This method should not be used after the `commit()` method was already called once.
- parameter object: the `NSManagedObject` type to be deleted
*/
public override func delete(object: NSManagedObject?) {
CoreStore.assert(
!self.isCommitted,
"Attempted to delete an entity of type \(typeName(object)) from an already committed \(typeName(self))."
)
super.delete(object)
}
/**
Deletes the specified `NSManagedObject`s.
- parameter object1: the `NSManagedObject` to be deleted
- parameter object2: another `NSManagedObject` to be deleted
- parameter objects: other `NSManagedObject`s to be deleted
*/
public override func delete(object1: NSManagedObject?, _ object2: NSManagedObject?, _ objects: NSManagedObject?...) {
CoreStore.assert(
!self.isCommitted,
"Attempted to delete an entities from an already committed \(typeName(self))."
)
super.delete(([object1, object2] + objects).flatMap { $0 })
}
/**
Deletes the specified `NSManagedObject`s.
- parameter objects: the `NSManagedObject`s to be deleted
*/
public override func delete<S: SequenceType where S.Generator.Element: NSManagedObject>(objects: S) {
CoreStore.assert(
!self.isCommitted,
"Attempted to delete an entities from an already committed \(typeName(self))."
)
super.delete(objects)
}
// MARK: Internal
internal init(mainContext: NSManagedObjectContext, queue: GCDQueue, closure: (transaction: SynchronousDataTransaction) -> Void) {
self.closure = closure
super.init(mainContext: mainContext, queue: queue, supportsUndo: false, bypassesQueueing: false)
}
internal func performAndWait() -> SaveResult? {
self.transactionQueue.sync {
self.closure(transaction: self)
if !self.isCommitted && self.hasChanges {
CoreStore.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: SynchronousDataTransaction) -> Void
// MARK: Deprecated
@available(*, deprecated=1.3.4, obsoleted=2.0.0, message="Resetting the context is inherently unsafe. This method will be removed in the near future. Use `beginUnsafe()` to create transactions with `undo` support.")
public func rollback() {
CoreStore.assert(
!self.isCommitted,
"Attempted to rollback an already committed \(typeName(self))."
)
CoreStore.assert(
self.transactionQueue.isCurrentExecutionContext(),
"Attempted to rollback a \(typeName(self)) outside its designated queue."
)
self.context.reset()
}
@available(*, deprecated=1.5.2, renamed="commitAndWait")
public func commit() {
self.commitAndWait()
}
}

View File

@@ -0,0 +1,153 @@
//
// UnsafeDataTransaction.swift
// CoreStore
//
// Copyright © 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
#if USE_FRAMEWORKS
import GCDKit
#endif
// MARK: - UnsafeDataTransaction
/**
The `UnsafeDataTransaction` provides an interface for non-contiguous `NSManagedObject` creates, updates, and deletes. This is useful for making temporary changes, such as partially filled forms. An unsafe transaction object should typically be only used from the main queue.
*/
public final class UnsafeDataTransaction: BaseDataTransaction {
/**
Saves the transaction changes asynchronously. For a `UnsafeDataTransaction`, multiple commits are allowed, although it is the developer's responsibility to ensure a reasonable leeway to prevent blocking the main thread.
- parameter 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) {
self.context.saveAsynchronouslyWithCompletion { (result) -> Void in
self.result = result
completion(result: result)
}
}
/**
Saves the transaction changes and waits for completion synchronously. For a `UnsafeDataTransaction`, multiple commits are allowed, although it is the developer's responsibility to ensure a reasonable leeway to prevent blocking the main thread.
- returns: a `SaveResult` containing the success or failure information
*/
public func commitAndWait() -> SaveResult {
let result = self.context.saveSynchronously()
self.result = result
return result
}
/**
Rolls back the transaction.
*/
public func rollback() {
CoreStore.assert(
self.supportsUndo,
"Attempted to rollback a \(typeName(self)) with Undo support disabled."
)
self.context.rollback()
}
/**
Undo's the last change made to the transaction.
*/
public func undo() {
CoreStore.assert(
self.supportsUndo,
"Attempted to undo a \(typeName(self)) with Undo support disabled."
)
self.context.undo()
}
/**
Redo's the last undone change to the transaction.
*/
public func redo() {
CoreStore.assert(
self.supportsUndo,
"Attempted to redo a \(typeName(self)) with Undo support disabled."
)
self.context.redo()
}
/**
Begins a child transaction where `NSManagedObject` creates, updates, and deletes can be made. This is useful for making temporary changes, such as partially filled forms.
- prameter supportsUndo: `undo()`, `redo()`, and `rollback()` methods are only available when this parameter is `true`, otherwise those method will raise an exception. Defaults to `false`. Note that turning on Undo support may heavily impact performance especially on iOS or watchOS where memory is limited.
- returns: a `UnsafeDataTransaction` instance where creates, updates, and deletes can be made.
*/
@warn_unused_result
public func beginUnsafe(supportsUndo supportsUndo: Bool = false) -> UnsafeDataTransaction {
return UnsafeDataTransaction(
mainContext: self.context,
queue: self.transactionQueue,
supportsUndo: supportsUndo
)
}
/**
Returns the `NSManagedObjectContext` for this unsafe transaction. Use only for cases where external frameworks need an `NSManagedObjectContext` instance to work with.
Note that it is the developer's responsibility to ensure the following:
- that the `UnsafeDataTransaction` that owns this context should be strongly referenced and prevented from being deallocated during the context's lifetime
- that all saves will be done either through the `UnsafeDataTransaction`'s `commit(...)` method, or by calling `save()` manually on the context, its parent, and all other ancestor contexts if there are any.
*/
public var internalContext: NSManagedObjectContext {
return self.context
}
// MARK: Internal
internal init(mainContext: NSManagedObjectContext, queue: GCDQueue, supportsUndo: Bool) {
super.init(mainContext: mainContext, queue: queue, supportsUndo: supportsUndo, bypassesQueueing: true)
}
// MARK: Deprecated
@available(*, deprecated=1.3.1, obsoleted=2.0.0, renamed="beginUnsafe")
@warn_unused_result
public func beginDetached() -> UnsafeDataTransaction {
return self.beginUnsafe()
}
}
// MARK: Deprecated
@available(*, deprecated=1.3.1, obsoleted=2.0.0, renamed="UnsafeDataTransaction")
public typealias DetachedDataTransaction = UnsafeDataTransaction