diff --git a/CoreStore.xcodeproj/project.xcworkspace/xcshareddata/CoreStore.xccheckout b/CoreStore.xcodeproj/project.xcworkspace/xcshareddata/CoreStore.xccheckout
index 867b6eb..7a53aef 100644
--- a/CoreStore.xcodeproj/project.xcworkspace/xcshareddata/CoreStore.xccheckout
+++ b/CoreStore.xcodeproj/project.xcworkspace/xcshareddata/CoreStore.xccheckout
@@ -22,7 +22,7 @@
4B60F1BCB491FF717C56441AE7783C74F417BE48
../..
8B2E522D57154DFA93A06982C36315ECBEA4FA97
- ../..Libraries/GCDKit/
+ ../..Libraries/GCDKit
IDESourceControlProjectURL
github.com:JohnEstropia/CoreStore.git
diff --git a/CoreStore/Info.plist b/CoreStore/Info.plist
index 738d37e..4f315e0 100644
--- a/CoreStore/Info.plist
+++ b/CoreStore/Info.plist
@@ -15,7 +15,7 @@
CFBundlePackageType
FMWK
CFBundleShortVersionString
- 0.1.0
+ 0.1.1
CFBundleSignature
????
CFBundleVersion
diff --git a/CoreStore/Internal/NSManagedObject+Transaction.swift b/CoreStore/Internal/NSManagedObject+Transaction.swift
index 1d9d49c..c574773 100644
--- a/CoreStore/Internal/NSManagedObject+Transaction.swift
+++ b/CoreStore/Internal/NSManagedObject+Transaction.swift
@@ -41,6 +41,11 @@ internal extension NSManagedObject {
)
}
+ internal class func inContext(context: NSManagedObjectContext, withObjectID objectID: NSManagedObjectID) -> Self? {
+
+ return self.typedObjectInContext(context, objectID: objectID)
+ }
+
internal func inContext(context: NSManagedObjectContext) -> Self? {
return self.typedObjectInContext(context)
@@ -54,6 +59,20 @@ internal extension NSManagedObject {
// MARK: Private
+ private class func typedObjectInContext(context: NSManagedObjectContext, objectID: NSManagedObjectID) -> T? {
+
+ var error: NSError?
+ if let existingObject = context.existingObjectWithID(objectID, error: &error) {
+
+ return (existingObject as! T)
+ }
+
+ CoreStore.handleError(
+ error ?? NSError(coreStoreErrorCode: .UnknownError),
+ "Failed to load existing \(typeName(self)) in context.")
+ return nil;
+ }
+
private func typedObjectInContext(context: NSManagedObjectContext) -> T? {
let objectID = self.objectID
diff --git a/CoreStore/Saving and Processing/AsynchronousDataTransaction.swift b/CoreStore/Saving and Processing/AsynchronousDataTransaction.swift
index 0e2e9cb..7099d86 100644
--- a/CoreStore/Saving and Processing/AsynchronousDataTransaction.swift
+++ b/CoreStore/Saving and Processing/AsynchronousDataTransaction.swift
@@ -109,11 +109,25 @@ public final class AsynchronousDataTransaction: BaseDataTransaction {
:param: object the `NSManagedObject` type to be edited
:returns: an editable proxy for the specified `NSManagedObject`.
*/
- public override func fetch(object: T?) -> T? {
+ public override func edit(object: T?) -> T? {
CoreStore.assert(!self.isCommitted, "Attempted to update an entity of type \(typeName(object)) from an already committed \(typeName(self)).")
- return super.fetch(object)
+ 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.
+
+ :param: into an `Into` clause specifying the entity type
+ :param: objectID the `NSManagedObjectID` for the object to be edited
+ :returns: an editable proxy for the specified `NSManagedObject`.
+ */
+ public override func edit(into: Into, _ objectID: NSManagedObjectID) -> T? {
+
+ CoreStore.assert(!self.isCommitted, "Attempted to update an entity of type <\(T.self)> from an already committed \(typeName(self)).")
+
+ return super.edit(into, objectID)
}
/**
@@ -128,6 +142,31 @@ public final class AsynchronousDataTransaction: BaseDataTransaction {
super.delete(object)
}
+ /**
+ Deletes the specified `NSManagedObject`'s.
+
+ :param: object the `NSManagedObject` type to be deleted
+ :param: objects other `NSManagedObject`'s type to be deleted
+ */
+ public override func delete(object: NSManagedObject?, _ objects: NSManagedObject?...) {
+
+ CoreStore.assert(!self.isCommitted, "Attempted to delete an entities from an already committed \(typeName(self)).")
+
+ super.delete([object] + objects)
+ }
+
+ /**
+ Deletes the specified `NSManagedObject`'s.
+
+ :param: objects the `NSManagedObject`'s type to be deleted
+ */
+ public override func delete(objects: [NSManagedObject?]) {
+
+ CoreStore.assert(!self.isCommitted, "Attempted to delete an entities from an already committed \(typeName(self)).")
+
+ super.delete(objects)
+ }
+
/**
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.
*/
diff --git a/CoreStore/Saving and Processing/BaseDataTransaction.swift b/CoreStore/Saving and Processing/BaseDataTransaction.swift
index e98a08b..f995db3 100644
--- a/CoreStore/Saving and Processing/BaseDataTransaction.swift
+++ b/CoreStore/Saving and Processing/BaseDataTransaction.swift
@@ -45,6 +45,11 @@ public struct Into {
// MARK: Public
+ internal static var defaultConfigurationName: String {
+
+ return "PF_DEFAULT_CONFIGURATION_NAME"
+ }
+
/**
Initializes an `Into` clause.
Sample Usage:
@@ -180,13 +185,28 @@ public /*abstract*/ class BaseDataTransaction {
:param: object the `NSManagedObject` type to be edited
:returns: an editable proxy for the specified `NSManagedObject`.
*/
- public func fetch(object: T?) -> T? {
+ public func edit(object: T?) -> T? {
CoreStore.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to update an entity of type \(typeName(object)) outside its designated queue.")
return object?.inContext(self.context)
}
+ /**
+ Returns an editable proxy of the object with the specified `NSManagedObjectID`.
+
+ :param: into an `Into` clause specifying the entity type
+ :param: objectID the `NSManagedObjectID` for the object to be edited
+ :returns: an editable proxy for the specified `NSManagedObject`.
+ */
+ public func edit(into: Into, _ objectID: NSManagedObjectID) -> T? {
+
+ CoreStore.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to update an entity of type <\(T.self)> outside its designated queue.")
+ CoreStore.assert(into.inferStoreIfPossible || (into.configuration ?? Into.defaultConfigurationName) == objectID.persistentStore?.configurationName, "Attempted to update an entity of type <\(T.self)> but the specified persistent store do not match the `NSManagedObjectID`.")
+
+ return T.inContext(self.context, withObjectID: objectID)
+ }
+
/**
Deletes a specified `NSManagedObject`.
@@ -194,11 +214,38 @@ public /*abstract*/ class BaseDataTransaction {
*/
public func delete(object: NSManagedObject?) {
- CoreStore.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to delete an entity of type \(typeName(object)) outside its designated queue.")
+ CoreStore.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to delete an entity outside its designated queue.")
object?.inContext(self.context)?.deleteFromContext()
}
+ /**
+ Deletes the specified `NSManagedObject`'s.
+
+ :param: object the `NSManagedObject` type to be deleted
+ :param: objects other `NSManagedObject`'s type to be deleted
+ */
+ public func delete(object: NSManagedObject?, _ objects: NSManagedObject?...) {
+
+ self.delete([object] + objects)
+ }
+
+ /**
+ Deletes the specified `NSManagedObject`'s.
+
+ :param: objects the `NSManagedObject`'s type to be deleted
+ */
+ public func delete(objects: [NSManagedObject?]) {
+
+ CoreStore.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to delete entities outside their designated queue.")
+
+ let context = self.context
+ for object in objects {
+
+ object?.inContext(context)?.deleteFromContext()
+ }
+ }
+
// MARK: Saving changes
/**
diff --git a/CoreStore/Saving and Processing/SynchronousDataTransaction.swift b/CoreStore/Saving and Processing/SynchronousDataTransaction.swift
index a54edef..aff2d6f 100644
--- a/CoreStore/Saving and Processing/SynchronousDataTransaction.swift
+++ b/CoreStore/Saving and Processing/SynchronousDataTransaction.swift
@@ -88,11 +88,25 @@ public final class SynchronousDataTransaction: BaseDataTransaction {
:param: object the `NSManagedObject` type to be edited
:returns: an editable proxy for the specified `NSManagedObject`.
*/
- public override func fetch(object: T?) -> T? {
+ public override func edit(object: T?) -> T? {
CoreStore.assert(!self.isCommitted, "Attempted to update an entity of type \(typeName(object)) from an already committed \(typeName(self)).")
- return super.fetch(object)
+ 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.
+
+ :param: into an `Into` clause specifying the entity type
+ :param: objectID the `NSManagedObjectID` for the object to be edited
+ :returns: an editable proxy for the specified `NSManagedObject`.
+ */
+ public override func edit(into: Into, _ objectID: NSManagedObjectID) -> T? {
+
+ CoreStore.assert(!self.isCommitted, "Attempted to update an entity of type <\(T.self)> from an already committed \(typeName(self)).")
+
+ return super.edit(into, objectID)
}
/**
@@ -107,6 +121,31 @@ public final class SynchronousDataTransaction: BaseDataTransaction {
super.delete(object)
}
+ /**
+ Deletes the specified `NSManagedObject`'s.
+
+ :param: object the `NSManagedObject` type to be deleted
+ :param: objects other `NSManagedObject`'s type to be deleted
+ */
+ public override func delete(object: NSManagedObject?, _ objects: NSManagedObject?...) {
+
+ CoreStore.assert(!self.isCommitted, "Attempted to delete an entities from an already committed \(typeName(self)).")
+
+ super.delete([object] + objects)
+ }
+
+ /**
+ Deletes the specified `NSManagedObject`'s.
+
+ :param: objects the `NSManagedObject`'s type to be deleted
+ */
+ public override func delete(objects: [NSManagedObject?]) {
+
+ CoreStore.assert(!self.isCommitted, "Attempted to delete an entities from an already committed \(typeName(self)).")
+
+ super.delete(objects)
+ }
+
/**
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.
*/
diff --git a/CoreStore/Setting Up/DataStack.swift b/CoreStore/Setting Up/DataStack.swift
index 4dcd52b..b4f89fc 100644
--- a/CoreStore/Setting Up/DataStack.swift
+++ b/CoreStore/Setting Up/DataStack.swift
@@ -28,8 +28,6 @@ import CoreData
import GCDKit
-private let defaultConfigurationName = "PF_DEFAULT_CONFIGURATION_NAME"
-
private let applicationSupportDirectory = NSFileManager.defaultManager().URLsForDirectory(.ApplicationSupportDirectory, inDomains: .UserDomainMask).first as! NSURL
private let applicationName = ((NSBundle.mainBundle().objectForInfoDictionaryKey("CFBundleName") as? String) ?? "CoreData")
@@ -185,7 +183,7 @@ public final class DataStack {
if store.type == NSSQLiteStoreType
&& isExistingStoreAutomigrating == automigrating
- && store.configurationName == (configuration ?? defaultConfigurationName) {
+ && store.configurationName == (configuration ?? Into.defaultConfigurationName) {
return PersistentStoreResult(store)
}
diff --git a/CoreStoreTests/CoreStoreTests.swift b/CoreStoreTests/CoreStoreTests.swift
index e8dddb1..a63918a 100644
--- a/CoreStoreTests/CoreStoreTests.swift
+++ b/CoreStoreTests/CoreStoreTests.swift
@@ -253,6 +253,17 @@ class CoreStoreTests: XCTestCase {
case .Success(let hasChanges):
XCTAssertTrue(hasChanges, "hasChanges == true")
+ CoreStore.beginSynchronous { (transaction) -> Void in
+
+ let obj5Copy1 = transaction.edit(obj5)
+ XCTAssertTrue(obj5.objectID == obj5Copy1?.objectID, "obj5.objectID == obj5Copy1?.objectID")
+ XCTAssertFalse(obj5 == obj5Copy1, "obj5 == obj5Copy1")
+
+ let obj5Copy2 = transaction.edit(Into(TestEntity1), obj5.objectID)
+ XCTAssertTrue(obj5.objectID == obj5Copy2?.objectID, "obj5.objectID == obj5Copy2?.objectID")
+ XCTAssertFalse(obj5 == obj5Copy2, "obj5 == obj5Copy2")
+ }
+
let count: Int? = CoreStore.queryValue(
From(TestEntity1),
Select(.Count("testNumber"))
@@ -279,6 +290,22 @@ class CoreStoreTests: XCTestCase {
)
XCTAssertTrue(count == 2, "count == 2 (actual: \(count))")
+
+ CoreStore.beginSynchronous { (transaction) -> Void in
+
+ let obj6 = transaction.edit(obj6)
+ let obj5 = transaction.edit(obj5)
+ transaction.delete(obj5, obj6)
+
+ transaction.commit()
+ }
+
+ let count2 = CoreStore.queryValue(
+ From(TestEntity1),
+ Select(.Count("testNumber"))
+ )
+ XCTAssertTrue(count2 == 0, "count == 0 (actual: \(count2))")
+
detachedExpectation.fulfill()
case .Failure(let error):
diff --git a/README.md b/README.md
index d56f2dd..440b02d 100644
--- a/README.md
+++ b/README.md
@@ -254,9 +254,86 @@ Note that if you do explicitly specify the configuration name, CoreStore will on
### Updating objects
+After creating an object from the transaction, you can simply update it's properties as normal:
+
+ CoreStore.beginAsynchronous { (transaction) -> Void in
+ let person = transaction.create(Into(MyPersonEntity))
+ person.name = "John Smith"
+ person.age = 30
+ transaction.commit()
+ }
+
+To update an existing object, fetch the object's instance from the transaction:
+
+ CoreStore.beginAsynchronous { (transaction) -> Void in
+ let person = transaction.fetchOne(
+ From(MyPersonEntity),
+ Where("name", isEqualTo: "Jane Smith")
+ )
+ person.age = person.age + 1
+ transaction.commit()
+ }
+
+*(For more about fetching, read [Fetching and querying](#fetch_query))*
+
+**Do not update an instance that was not created/fetched from the transaction.** If you have a reference to the object already, use the transaction's `edit(...)` method to get an editable proxy instance for that object:
+
+ let jane: MyPersonEntity = // ...
+
+ CoreStore.beginAsynchronous { (transaction) -> Void in
+ // WRONG: jane.age = jane.age + 1
+ // RIGHT:
+ let jane = transaction.edit(jane) // using the same variable name protects us from misusing the non-transaction instance
+ jane.age = jane.age + 1
+ transaction.commit()
+ }
+
+This is also true when updating an object's relationships. Make sure that the object assigned to the relationship is also created/fetched from the transaction:
+
+ let jane: MyPersonEntity = // ...
+ let john: MyPersonEntity = // ...
+
+ CoreStore.beginAsynchronous { (transaction) -> Void in
+ // WRONG: jane.friends = [john]
+ // RIGHT:
+ let jane = transaction.edit(jane)
+ let john = transaction.edit(john)
+ jane.friends = [john]
+ transaction.commit()
+ }
### Deleting objects
+Deleting an object is simpler as you can tell a transaction to delete an object directly without fetching an editable proxy (CoreStore does that for you):
+
+ let john: MyPersonEntity = // ...
+
+ CoreStore.beginAsynchronous { (transaction) -> Void in
+ transaction.delete(john)
+ transaction.commit()
+ }
+
+or several objects at once:
+
+ let john: MyPersonEntity = // ...
+ let jane: MyPersonEntity = // ...
+
+ CoreStore.beginAsynchronous { (transaction) -> Void in
+ transaction.delete(john, jane)
+ // transaction.delete([john, jane]) is also allowed
+ transaction.commit()
+ }
+
+If you do not have references yet to the objects to be deleted, transactions have a `deleteAll(...)` method you can pass a query to:
+
+ CoreStore.beginAsynchronous { (transaction) -> Void in
+ transaction.deleteAll(
+ From(MyPersonEntity)
+ Where("age > 30")
+ )
+ transaction.commit()
+ }
+
## Fetching and querying
(implemented; README pending)