mirror of
https://github.com/JohnEstropia/CoreStore.git
synced 2026-03-19 16:21:27 +01:00
allow nested transactions
This commit is contained in:
@@ -73,7 +73,6 @@ public class DataStack {
|
|||||||
self.coordinator = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel)
|
self.coordinator = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel)
|
||||||
self.rootSavingContext = NSManagedObjectContext.rootSavingContextForCoordinator(self.coordinator)
|
self.rootSavingContext = NSManagedObjectContext.rootSavingContextForCoordinator(self.coordinator)
|
||||||
self.mainContext = NSManagedObjectContext.mainContextForRootContext(self.rootSavingContext)
|
self.mainContext = NSManagedObjectContext.mainContextForRootContext(self.rootSavingContext)
|
||||||
self.transactionQueue = .createSerial("com.hardcoredata.datastack.transactionqueue")
|
|
||||||
self.entityNameMapping = (managedObjectModel.entities as! [NSEntityDescription]).reduce([EntityClassNameType: EntityNameType]()) { (var mapping, entityDescription) in
|
self.entityNameMapping = (managedObjectModel.entities as! [NSEntityDescription]).reduce([EntityClassNameType: EntityNameType]()) { (var mapping, entityDescription) in
|
||||||
|
|
||||||
if let entityName = entityDescription.name {
|
if let entityName = entityDescription.name {
|
||||||
@@ -269,7 +268,7 @@ public class DataStack {
|
|||||||
// MARK: Internal
|
// MARK: Internal
|
||||||
|
|
||||||
internal let mainContext: NSManagedObjectContext
|
internal let mainContext: NSManagedObjectContext
|
||||||
internal let transactionQueue: GCDQueue;
|
internal let transactionQueue: GCDQueue = .createSerial("com.hardcoredata.datastack.transactionqueue")
|
||||||
|
|
||||||
internal func entityNameForEntityClass(entityClass: NSManagedObject.Type) -> String? {
|
internal func entityNameForEntityClass(entityClass: NSManagedObject.Type) -> String? {
|
||||||
|
|
||||||
|
|||||||
@@ -37,10 +37,6 @@ public final class DataTransaction {
|
|||||||
|
|
||||||
// MARK: - Public
|
// MARK: - Public
|
||||||
|
|
||||||
/**
|
|
||||||
The background concurrent context managed by the transaction.
|
|
||||||
*/
|
|
||||||
public let context: NSManagedObjectContext
|
|
||||||
|
|
||||||
// MARK: Object management
|
// MARK: Object management
|
||||||
|
|
||||||
@@ -129,9 +125,25 @@ public final class DataTransaction {
|
|||||||
self.result = self.context.saveSynchronously()
|
self.result = self.context.saveSynchronously()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Begins a child 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 performTransactionAndWait(closure: (transaction: DataTransaction) -> Void) -> SaveResult? {
|
||||||
|
|
||||||
|
return DataTransaction(
|
||||||
|
mainContext: self.context,
|
||||||
|
queue: self.childTransactionQueue,
|
||||||
|
closure: closure).performAndWait()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// MARK: - Internal
|
// MARK: - Internal
|
||||||
|
|
||||||
|
internal let context: NSManagedObjectContext
|
||||||
|
|
||||||
internal init(mainContext: NSManagedObjectContext, queue: GCDQueue, closure: (transaction: DataTransaction) -> Void) {
|
internal init(mainContext: NSManagedObjectContext, queue: GCDQueue, closure: (transaction: DataTransaction) -> Void) {
|
||||||
|
|
||||||
self.transactionQueue = queue
|
self.transactionQueue = queue
|
||||||
@@ -168,4 +180,6 @@ public final class DataTransaction {
|
|||||||
private var result: SaveResult?
|
private var result: SaveResult?
|
||||||
private let transactionQueue: GCDQueue
|
private let transactionQueue: GCDQueue
|
||||||
private let closure: (transaction: DataTransaction) -> Void
|
private let closure: (transaction: DataTransaction) -> Void
|
||||||
|
|
||||||
|
private lazy var childTransactionQueue: GCDQueue = .createSerial("com.hardcoredata.datastack.childtransactionqueue")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,24 +30,24 @@ import Foundation
|
|||||||
|
|
||||||
public final class DefaultLogger: HardcoreDataLogger {
|
public final class DefaultLogger: HardcoreDataLogger {
|
||||||
|
|
||||||
public func log(#level: LogLevel, message: String, fileName: StaticString, lineNumber: UWord, functionName: StaticString) {
|
public func log(#level: LogLevel, message: String, fileName: StaticString, lineNumber: Int, functionName: StaticString) {
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
Swift.println("[HardcoreData] \(fileName.stringValue.lastPathComponent):\(lineNumber) \(functionName)\n ↪︎ \(message)\n")
|
Swift.println("[HardcoreData] \(fileName.stringValue.lastPathComponent):\(lineNumber) \(functionName)\n ↪︎ \(message)\n")
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
public func handleError(#error: NSError, message: String, fileName: StaticString, lineNumber: UWord, functionName: StaticString) {
|
public func handleError(#error: NSError, message: String, fileName: StaticString, lineNumber: Int, functionName: StaticString) {
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
Swift.println("[HardcoreData] \(fileName.stringValue.lastPathComponent):\(lineNumber) \(functionName)\n ↪︎ \(message): \(error)\n")
|
Swift.println("[HardcoreData] \(fileName.stringValue.lastPathComponent):\(lineNumber) \(functionName)\n ↪︎ \(message): \(error)\n")
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
public func assert(@autoclosure condition: () -> Bool, message: String, fileName: StaticString, lineNumber: UWord, functionName: StaticString) {
|
public func assert(@autoclosure condition: () -> Bool, message: String, fileName: StaticString, lineNumber: Int, functionName: StaticString) {
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
Swift.assert(condition, message, file: fileName, line: lineNumber)
|
Swift.assert(condition, message, file: fileName, line: numericCast(lineNumber))
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ public enum HardcoreData {
|
|||||||
|
|
||||||
// MARK: Internal
|
// MARK: Internal
|
||||||
|
|
||||||
internal static func log(level: LogLevel, message: String, fileName: StaticString = __FILE__, lineNumber: UWord = __LINE__, functionName: StaticString = __FUNCTION__) {
|
internal static func log(level: LogLevel, message: String, fileName: StaticString = __FILE__, lineNumber: Int = __LINE__, functionName: StaticString = __FUNCTION__) {
|
||||||
|
|
||||||
self.logger.log(
|
self.logger.log(
|
||||||
level: level,
|
level: level,
|
||||||
@@ -91,7 +91,7 @@ public enum HardcoreData {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static func handleError(error: NSError, _ message: String, fileName: StaticString = __FILE__, lineNumber: UWord = __LINE__, functionName: StaticString = __FUNCTION__) {
|
internal static func handleError(error: NSError, _ message: String, fileName: StaticString = __FILE__, lineNumber: Int = __LINE__, functionName: StaticString = __FUNCTION__) {
|
||||||
|
|
||||||
self.logger.handleError(
|
self.logger.handleError(
|
||||||
error: error,
|
error: error,
|
||||||
@@ -101,7 +101,7 @@ public enum HardcoreData {
|
|||||||
functionName: functionName)
|
functionName: functionName)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static func assert(@autoclosure condition: () -> Bool, _ message: String, fileName: StaticString = __FILE__, lineNumber: UWord = __LINE__, functionName: StaticString = __FUNCTION__) {
|
internal static func assert(@autoclosure condition: () -> Bool, _ message: String, fileName: StaticString = __FILE__, lineNumber: Int = __LINE__, functionName: StaticString = __FUNCTION__) {
|
||||||
|
|
||||||
self.logger.assert(
|
self.logger.assert(
|
||||||
condition,
|
condition,
|
||||||
|
|||||||
@@ -41,9 +41,9 @@ public enum LogLevel {
|
|||||||
|
|
||||||
public protocol HardcoreDataLogger {
|
public protocol HardcoreDataLogger {
|
||||||
|
|
||||||
func log(#level: LogLevel, message: String, fileName: StaticString, lineNumber: UWord, functionName: StaticString)
|
func log(#level: LogLevel, message: String, fileName: StaticString, lineNumber: Int, functionName: StaticString)
|
||||||
|
|
||||||
func handleError(#error: NSError, message: String, fileName: StaticString, lineNumber: UWord, functionName: StaticString)
|
func handleError(#error: NSError, message: String, fileName: StaticString, lineNumber: Int, functionName: StaticString)
|
||||||
|
|
||||||
func assert(@autoclosure condition: () -> Bool, message: String, fileName: StaticString, lineNumber: UWord, functionName: StaticString)
|
func assert(@autoclosure condition: () -> Bool, message: String, fileName: StaticString, lineNumber: Int, functionName: StaticString)
|
||||||
}
|
}
|
||||||
@@ -41,11 +41,12 @@ internal extension NSManagedObjectContext {
|
|||||||
|
|
||||||
return parentContext.parentStack
|
return parentContext.parentStack
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.getAssociatedObjectForKey(&PropertyKeys.parentStack)
|
return self.getAssociatedObjectForKey(&PropertyKeys.parentStack)
|
||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
|
|
||||||
if let parentContext = self.parentContext {
|
if self.parentContext != nil {
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ internal extension NSManagedObjectContext {
|
|||||||
context.parentContext = self
|
context.parentContext = self
|
||||||
context.parentStack = self.parentStack
|
context.parentStack = self.parentStack
|
||||||
context.setupForHardcoreDataWithContextName("com.hardcoredata.temporarycontext")
|
context.setupForHardcoreDataWithContextName("com.hardcoredata.temporarycontext")
|
||||||
context.shouldCascadeSavesToParent = true
|
context.shouldCascadeSavesToParent = (self.concurrencyType == .MainQueueConcurrencyType)
|
||||||
|
|
||||||
return context
|
return context
|
||||||
}
|
}
|
||||||
@@ -67,7 +67,6 @@ internal extension NSManagedObjectContext {
|
|||||||
|
|
||||||
if !self.hasChanges {
|
if !self.hasChanges {
|
||||||
|
|
||||||
self.reset()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ class HardcoreDataTests: XCTestCase {
|
|||||||
override func setUp() {
|
override func setUp() {
|
||||||
|
|
||||||
super.setUp()
|
super.setUp()
|
||||||
|
NSFileManager.defaultManager().removeItemAtURL(NSFileManager.defaultManager().URLsForDirectory(.ApplicationSupportDirectory, inDomains: .UserDomainMask).first as! NSURL, error: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tearDown() {
|
override func tearDown() {
|
||||||
@@ -84,8 +85,35 @@ class HardcoreDataTests: XCTestCase {
|
|||||||
obj3.testNumber = 90
|
obj3.testNumber = 90
|
||||||
obj3.testDate = NSDate()
|
obj3.testDate = NSDate()
|
||||||
|
|
||||||
|
|
||||||
|
transaction.performTransactionAndWait { (transaction) -> Void in
|
||||||
|
|
||||||
|
let obj4 = transaction.create(TestEntity2)
|
||||||
|
obj4.testEntityID = 4
|
||||||
|
obj4.testString = "hehehehe"
|
||||||
|
obj4.testNumber = 80
|
||||||
|
obj4.testDate = NSDate()
|
||||||
|
|
||||||
|
let objs4test = transaction.fetchOne(
|
||||||
|
TestEntity2.self,
|
||||||
|
Where("testEntityID", isEqualTo: 4),
|
||||||
|
CustomizeQuery { (fetchRequest) -> Void in
|
||||||
|
|
||||||
|
fetchRequest.includesPendingChanges = true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
XCTAssertNotNil(objs4test, "objs4test != nil")
|
||||||
|
// Dont commit1
|
||||||
|
}
|
||||||
|
|
||||||
transaction.commit { (result) -> Void in
|
transaction.commit { (result) -> Void in
|
||||||
|
|
||||||
|
let objs4test = HardcoreData.fetchOne(
|
||||||
|
TestEntity2.self,
|
||||||
|
Where("testEntityID", isEqualTo: 4)
|
||||||
|
)
|
||||||
|
XCTAssertNil(objs4test, "objs4test == nil")
|
||||||
|
|
||||||
XCTAssertTrue(NSThread.isMainThread(), "NSThread.isMainThread()")
|
XCTAssertTrue(NSThread.isMainThread(), "NSThread.isMainThread()")
|
||||||
switch result {
|
switch result {
|
||||||
|
|
||||||
@@ -115,6 +143,7 @@ class HardcoreDataTests: XCTestCase {
|
|||||||
)
|
)
|
||||||
XCTAssertNotNil(objs2, "objs2 != nil")
|
XCTAssertNotNil(objs2, "objs2 != nil")
|
||||||
XCTAssertTrue(objs2?.count == 2, "objs2?.count == 2")
|
XCTAssertTrue(objs2?.count == 2, "objs2?.count == 2")
|
||||||
|
print(objs2)
|
||||||
|
|
||||||
transaction.commit { (result) -> Void in
|
transaction.commit { (result) -> Void in
|
||||||
|
|
||||||
|
|||||||
Submodule Libraries/GCDKit updated: 2242a85814...d8dabc024e
Reference in New Issue
Block a user