Compare commits

...

12 Commits

Author SHA1 Message Date
John Rommel Estropia
2e4c9a7a25 fix From clauses not setting the NSFetchRequest's affectedStores property properly 2016-06-05 17:19:20 +09:00
John Rommel Estropia
0c8731c610 improved flushing behavior 2016-05-28 22:34:14 +09:00
John Rommel Estropia
f617f2cc53 Simpler API for creating CoreStore-managed NSFetchedResultsControllers, useful for external SDK's that require them 2016-05-28 01:47:48 +09:00
John Rommel Estropia
f396b01043 fixed Cartfile dependency 2016-05-26 01:28:26 +09:00
John Rommel Estropia
e38093342d version bump, updated dependendencies 2016-05-24 00:42:35 +09:00
John Rommel Estropia
c15afcb381 Allow flushing UnsafeDataTransactions to notify its ListMonitors and ObjectMonitors without saving changes to the DataStack (fixes #71) 2016-05-24 00:40:13 +09:00
John Rommel Estropia
a97001cdbb fixed scheme 2016-05-24 00:32:54 +09:00
John Estropia
c8ca16a982 Merge pull request #69 from bretsko/master
fixed Readme
2016-05-17 14:05:11 +09:00
Oleksandr Bretsko
9e0493a20d fixed Readme
replaced importObjects with importUniqueObjects
2016-05-17 08:43:40 +04:00
John Rommel Estropia
c990f7764a version bump 2016-05-07 12:19:30 +08:00
John Rommel Estropia
26ae6293ca merge changes to main context synchronously or asynchronously depending on the caller intention (fixes #65) 2016-05-07 12:18:54 +08:00
John Rommel Estropia
b9a2f96d6e renamed refreshAllObjectsAsFaults to refreshAndMergeAllObjects, which is more correct 2016-05-07 12:04:59 +08:00
14 changed files with 251 additions and 26 deletions

View File

@@ -1 +1 @@
github "JohnEstropia/GCDKit" == 1.2.4
github "JohnEstropia/GCDKit" == 1.2.5

View File

@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "CoreStore"
s.version = "1.6.4"
s.version = "1.6.10"
s.license = "MIT"
s.summary = "Unleashing the real power of Core Data with the elegance and safety of Swift"
s.homepage = "https://github.com/JohnEstropia/CoreStore"
@@ -18,5 +18,5 @@ Pod::Spec.new do |s|
s.requires_arc = true
s.pod_target_xcconfig = { 'OTHER_SWIFT_FLAGS' => '-D USE_FRAMEWORKS' }
s.dependency "GCDKit", "1.2.4"
s.dependency "GCDKit", "1.2.5"
end

View File

@@ -15,7 +15,7 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B5D9E2ED1CA2C317007A9D52"
BuildableName = "CoreStore.framework"
BuildableName = "CoreStore_iOS7.framework"
BlueprintName = "CoreStore iOS7"
ReferencedContainer = "container:CoreStore.xcodeproj">
</BuildableReference>
@@ -79,7 +79,7 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B5D9E2ED1CA2C317007A9D52"
BuildableName = "CoreStore.framework"
BuildableName = "CoreStore_iOS7.framework"
BlueprintName = "CoreStore iOS7"
ReferencedContainer = "container:CoreStore.xcodeproj">
</BuildableReference>

View File

@@ -32,8 +32,170 @@ import CoreData
public extension NSFetchedResultsController {
/**
Utility for creating an `NSFetchedResultsController` from a `DataStack`. This is useful to partially support Objective-C classes by passing an `NSFetchedResultsController` instance instead of a `ListMonitor`.
Utility for creating an `NSFetchedResultsController` from a `DataStack`. This is useful when an `NSFetchedResultsController` is preferred over the overhead of `ListMonitor`s abstraction.
- parameter dataStack: the `DataStack` to observe objects from
- parameter from: a `From` clause indicating the entity type
- parameter sectionBy: a `SectionBy` clause indicating the keyPath for the attribute to use when sorting the list into sections
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: an `NSFetchedResultsController` that observes a `DataStack`
*/
@nonobjc
public static func createFor<T: NSManagedObject>(dataStack: DataStack, _ from: From<T>, _ sectionBy: SectionBy, _ fetchClauses: FetchClause...) -> NSFetchedResultsController {
return CoreStoreFetchedResultsController<T>(
context: dataStack.mainContext,
fetchRequest: CoreStoreFetchRequest(),
from: from,
sectionBy: sectionBy,
fetchClauses: fetchClauses
)
}
/**
Utility for creating an `NSFetchedResultsController` from a `DataStack`. This is useful when an `NSFetchedResultsController` is preferred over the overhead of `ListMonitor`s abstraction.
- parameter dataStack: the `DataStack` to observe objects from
- parameter from: a `From` clause indicating the entity type
- parameter sectionBy: a `SectionBy` clause indicating the keyPath for the attribute to use when sorting the list into sections
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: an `NSFetchedResultsController` that observes a `DataStack`
*/
@nonobjc
public static func createFor<T: NSManagedObject>(dataStack: DataStack, _ from: From<T>, _ sectionBy: SectionBy, _ fetchClauses: [FetchClause]) -> NSFetchedResultsController {
return CoreStoreFetchedResultsController<T>(
context: dataStack.mainContext,
fetchRequest: CoreStoreFetchRequest(),
from: from,
sectionBy: sectionBy,
fetchClauses: fetchClauses
)
}
/**
Utility for creating an `NSFetchedResultsController` from a `DataStack`. This is useful when an `NSFetchedResultsController` is preferred over the overhead of `ListMonitor`s abstraction.
- parameter dataStack: the `DataStack` to observe objects from
- parameter from: a `From` clause indicating the entity type
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: an `NSFetchedResultsController` that observes a `DataStack`
*/
@nonobjc
public static func createFor<T: NSManagedObject>(dataStack: DataStack, _ from: From<T>, _ fetchClauses: FetchClause...) -> NSFetchedResultsController {
return CoreStoreFetchedResultsController<T>(
context: dataStack.mainContext,
fetchRequest: CoreStoreFetchRequest(),
from: from,
sectionBy: nil,
fetchClauses: fetchClauses
)
}
/**
Utility for creating an `NSFetchedResultsController` from a `DataStack`. This is useful when an `NSFetchedResultsController` is preferred over the overhead of `ListMonitor`s abstraction.
- parameter dataStack: the `DataStack` to observe objects from
- parameter from: a `From` clause indicating the entity type
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: an `NSFetchedResultsController` that observes a `DataStack`
*/
@nonobjc
public static func createFor<T: NSManagedObject>(dataStack: DataStack, _ from: From<T>, _ fetchClauses: [FetchClause]) -> NSFetchedResultsController {
return CoreStoreFetchedResultsController<T>(
context: dataStack.mainContext,
fetchRequest: CoreStoreFetchRequest(),
from: from,
sectionBy: nil,
fetchClauses: fetchClauses
)
}
/**
Utility for creating an `NSFetchedResultsController` from an `UnsafeDataTransaction`. This is useful when an `NSFetchedResultsController` is preferred over the overhead of `ListMonitor`s abstraction.
- parameter transaction: the `UnsafeDataTransaction` to observe objects from
- parameter from: a `From` clause indicating the entity type
- parameter sectionBy: a `SectionBy` clause indicating the keyPath for the attribute to use when sorting the list into sections
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: an `NSFetchedResultsController` that observes an `UnsafeDataTransaction`
*/
@nonobjc
public static func createFor<T: NSManagedObject>(transaction: UnsafeDataTransaction, _ from: From<T>, _ sectionBy: SectionBy, _ fetchClauses: FetchClause...) -> NSFetchedResultsController {
return CoreStoreFetchedResultsController<T>(
context: transaction.context,
fetchRequest: CoreStoreFetchRequest(),
from: from,
sectionBy: sectionBy,
fetchClauses: fetchClauses
)
}
/**
Utility for creating an `NSFetchedResultsController` from an `UnsafeDataTransaction`. This is useful when an `NSFetchedResultsController` is preferred over the overhead of `ListMonitor`s abstraction.
- parameter transaction: the `UnsafeDataTransaction` to observe objects from
- parameter from: a `From` clause indicating the entity type
- parameter sectionBy: a `SectionBy` clause indicating the keyPath for the attribute to use when sorting the list into sections
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: an `NSFetchedResultsController` that observes an `UnsafeDataTransaction`
*/
@nonobjc
public static func createFor<T: NSManagedObject>(transaction: UnsafeDataTransaction, _ from: From<T>, _ sectionBy: SectionBy, _ fetchClauses: [FetchClause]) -> NSFetchedResultsController {
return CoreStoreFetchedResultsController<T>(
context: transaction.context,
fetchRequest: CoreStoreFetchRequest(),
from: from,
sectionBy: sectionBy,
fetchClauses: fetchClauses
)
}
/**
Utility for creating an `NSFetchedResultsController` from an `UnsafeDataTransaction`. This is useful when an `NSFetchedResultsController` is preferred over the overhead of `ListMonitor`s abstraction.
- parameter transaction: the `UnsafeDataTransaction` to observe objects from
- parameter from: a `From` clause indicating the entity type
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: an `NSFetchedResultsController` that observes an `UnsafeDataTransaction`
*/
@nonobjc
public static func createFor<T: NSManagedObject>(transaction: UnsafeDataTransaction, _ from: From<T>, _ fetchClauses: FetchClause...) -> NSFetchedResultsController {
return CoreStoreFetchedResultsController<T>(
context: transaction.context,
fetchRequest: CoreStoreFetchRequest(),
from: from,
sectionBy: nil,
fetchClauses: fetchClauses
)
}
/**
Utility for creating an `NSFetchedResultsController` from an `UnsafeDataTransaction`. This is useful when an `NSFetchedResultsController` is preferred over the overhead of `ListMonitor`s abstraction.
- parameter transaction: the `UnsafeDataTransaction` to observe objects from
- parameter from: a `From` clause indicating the entity type
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
*/
@nonobjc
public static func createFor<T: NSManagedObject>(transaction: UnsafeDataTransaction, _ from: From<T>, _ fetchClauses: [FetchClause]) -> NSFetchedResultsController {
return CoreStoreFetchedResultsController<T>(
context: transaction.context,
fetchRequest: CoreStoreFetchRequest(),
from: from,
sectionBy: nil,
fetchClauses: fetchClauses
)
}
@available(*, deprecated=1.6.8, message="Use NSFetchedResultsController.createFor(_:_:_:_:) instead.")
@nonobjc
public static func createForStack<T: NSManagedObject>(dataStack: DataStack, fetchRequest: NSFetchRequest, from: From<T>? = nil, sectionBy: SectionBy? = nil, fetchClauses: [FetchClause]) -> NSFetchedResultsController {
return CoreStoreFetchedResultsController<T>(
@@ -45,7 +207,8 @@ public extension NSFetchedResultsController {
)
}
@available(*, deprecated=1.5.2, message="Use NSFetchedResultsController.createForStack(_:fetchRequest:from:sectionBy:fetchClauses:) to create NSFetchedResultsControllers directly")
@available(*, deprecated=1.5.2, message="Use NSFetchedResultsController.createFor(_:_:_:_:) to create NSFetchedResultsControllers directly")
@nonobjc
public convenience init<T: NSManagedObject>(dataStack: DataStack, fetchRequest: NSFetchRequest, from: From<T>? = nil, sectionBy: SectionBy? = nil, fetchClauses: [FetchClause]) {
let context = dataStack.mainContext

View File

@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.6.4</string>
<string>1.6.10</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>

View File

@@ -33,11 +33,10 @@ import CoreData
// http://stackoverflow.com/questions/14396375/nsfetchedresultscontroller-crashes-in-ios-6-if-affectedstores-is-specified
internal final class CoreStoreFetchRequest: NSFetchRequest {
@objc
override var affectedStores: [NSPersistentStore]? {
get { return self.safeAffectedStores }
set { self.safeAffectedStores = newValue }
get { return super.affectedStores }
set { super.affectedStores = newValue }
}
private var safeAffectedStores: [NSPersistentStore]?
}

View File

@@ -82,14 +82,28 @@ internal extension NSManagedObjectContext {
object: rootContext,
closure: { [weak context] (note) -> Void in
context?.performBlock { () -> Void in
guard let rootContext = note.object as? NSManagedObjectContext,
let context = context else {
return
}
let mergeChanges = { () -> Void in
let updatedObjects = (note.userInfo?[NSUpdatedObjectsKey] as? Set<NSManagedObject>) ?? []
for object in updatedObjects {
context?.objectWithID(object.objectID).willAccessValueForKey(nil)
context.objectWithID(object.objectID).willAccessValueForKey(nil)
}
context?.mergeChangesFromContextDidSaveNotification(note)
context.mergeChangesFromContextDidSaveNotification(note)
}
if rootContext.isSavingSynchronously == true {
context.performBlockAndWait(mergeChanges)
}
else {
context.performBlock(mergeChanges)
}
}
)

View File

@@ -55,6 +55,26 @@ internal extension NSManagedObjectContext {
}
}
internal var isSavingSynchronously: Bool? {
get {
let value: NSNumber? = getAssociatedObjectForKey(
&PropertyKeys.isSavingSynchronously,
inObject: self
)
return value?.boolValue
}
set {
setAssociatedWeakObject(
newValue.flatMap { NSNumber(bool: $0) },
forKey: &PropertyKeys.isSavingSynchronously,
inObject: self
)
}
}
internal func isRunningInAllowedQueue() -> Bool {
guard let parentTransaction = self.parentTransaction else {
@@ -89,7 +109,9 @@ internal extension NSManagedObjectContext {
do {
self.isSavingSynchronously = true
try self.save()
self.isSavingSynchronously = nil
}
catch {
@@ -137,7 +159,9 @@ internal extension NSManagedObjectContext {
do {
self.isSavingSynchronously = false
try self.save()
self.isSavingSynchronously = nil
}
catch {
@@ -167,7 +191,7 @@ internal extension NSManagedObjectContext {
}
}
internal func refreshAllObjectsAsFaults() {
internal func refreshAndMergeAllObjects() {
if #available(iOS 8.3, OSX 10.11, *) {
@@ -175,7 +199,7 @@ internal extension NSManagedObjectContext {
}
else {
self.registeredObjects.forEach { self.refreshObject($0, mergeChanges: false) }
self.registeredObjects.forEach { self.refreshObject($0, mergeChanges: true) }
}
}
@@ -185,5 +209,6 @@ internal extension NSManagedObjectContext {
private struct PropertyKeys {
static var parentTransaction: Void?
static var isSavingSynchronously: Void?
}
}

View File

@@ -191,14 +191,14 @@ public /*abstract*/ class BaseDataTransaction {
/**
Refreshes all registered objects `NSManagedObject`s in the transaction.
*/
public func refreshAllObjectsAsFaults() {
public func refreshAndMergeAllObjects() {
CoreStore.assert(
self.isRunningInAllowedQueue(),
"Attempted to refresh entities outside their designated queue."
)
self.context.refreshAllObjectsAsFaults()
self.context.refreshAndMergeAllObjects()
}

View File

@@ -66,9 +66,9 @@ public extension CoreStore {
/**
Refreshes all registered objects `NSManagedObject`s in the `DataStack`.
*/
public static func refreshAllObjectsAsFaults() {
public static func refreshAndMergeAllObjects() {
self.defaultStack.refreshAllObjectsAsFaults()
self.defaultStack.refreshAndMergeAllObjects()
}
@available(*, deprecated=1.3.1, renamed="beginUnsafe")

View File

@@ -83,14 +83,14 @@ public extension DataStack {
/**
Refreshes all registered objects `NSManagedObject`s in the `DataStack`.
*/
public func refreshAllObjectsAsFaults() {
public func refreshAndMergeAllObjects() {
CoreStore.assert(
NSThread.isMainThread(),
"Attempted to refresh entities outside their designated queue."
)
self.mainContext.refreshAllObjectsAsFaults()
self.mainContext.refreshAndMergeAllObjects()
}
@available(*, deprecated=1.3.1, renamed="beginUnsafe")

View File

@@ -91,6 +91,30 @@ public final class UnsafeDataTransaction: BaseDataTransaction {
self.context.undo()
}
/**
Immediately flushes all pending changes to the transaction's observers. This is useful in conjunction with `ListMonitor`s and `ObjectMonitor`s created from `UnsafeDataTransaction`s used to manage temporary "scratch" data.
- Important: Note that unlike `commit()`, `flush()` does not propagate/save updates to the `DataStack` and the persistent store. However, the flushed changes will be seen by children transactions created further from the current transaction (i.e. through `transaction.beginUnsafe()`)
- throws: an error thrown from `closure`, or an error thrown by Core Data (usually validation errors or conflict errors)
*/
public func flush() {
self.context.processPendingChanges()
}
/**
Flushes all pending changes to the transaction's observers at the end of the `closure`'s execution. This is useful in conjunction with `ListMonitor`s and `ObjectMonitor`s created from `UnsafeDataTransaction`s used to manage temporary "scratch" data.
- Important: Note that unlike `commit()`, `flush()` does not propagate/save updates to the `DataStack` and the persistent store. However, the flushed changes will be seen by children transactions created further from the current transaction (i.e. through `transaction.beginUnsafe()`)
- parameter closure: the closure where changes can be made prior to the flush
- throws: an error thrown from `closure`, or an error thrown by Core Data (usually validation errors or conflict errors)
*/
public func flush(@noescape closure: () throws -> Void) rethrows {
try closure()
self.context.processPendingChanges()
}
/**
Redo's the last undone change to the transaction.
*/
@@ -122,7 +146,7 @@ public final class UnsafeDataTransaction: BaseDataTransaction {
/**
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:
- Important: 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.
*/

View File

@@ -733,7 +733,7 @@ or multiple objects at once with the `importUniqueObjects(...)` method:
```swift
CoreStore.beginAsynchronous { (transaction) -> Void in
let jsonArray: [[String: AnyObject]] = // ...
try! transaction.importObjects(
try! transaction.importUniqueObjects(
Into(MyPersonEntity),
sourceArray: jsonArray
)