mirror of
https://github.com/JohnEstropia/CoreStore.git
synced 2026-03-11 21:11:58 +01:00
Refetch monitors when persistent stores are added/removed. Allow unsafe transactions to create their own monitors
This commit is contained in:
@@ -31,20 +31,31 @@ import CoreData
|
||||
|
||||
public extension NSFetchedResultsController {
|
||||
|
||||
public convenience init<T: NSManagedObject>(dataStack: DataStack, fetchRequest: NSFetchRequest, from: From<T>? = nil, sectionBy: SectionBy? = nil, fetchClauses: [FetchClause]) {
|
||||
|
||||
// MARK: Public
|
||||
|
||||
public func createForStack<T: NSManagedObject>(dataStack: DataStack, fetchRequest: NSFetchRequest, from: From<T>? = nil, sectionBy: SectionBy? = nil, fetchClauses: [FetchClause]) -> NSFetchedResultsController {
|
||||
|
||||
let context = dataStack.mainContext
|
||||
from?.applyToFetchRequest(fetchRequest, context: context)
|
||||
for clause in fetchClauses {
|
||||
|
||||
clause.applyToFetchRequest(fetchRequest)
|
||||
}
|
||||
|
||||
self.init(
|
||||
return CoreStoreFetchedResultsController<T>(
|
||||
context: dataStack.mainContext,
|
||||
fetchRequest: fetchRequest,
|
||||
managedObjectContext: context,
|
||||
sectionNameKeyPath: sectionBy?.sectionKeyPath,
|
||||
cacheName: nil
|
||||
from: from,
|
||||
sectionBy: sectionBy,
|
||||
fetchClauses: fetchClauses
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
internal func createFromContext<T: NSManagedObject>(context: NSManagedObjectContext, fetchRequest: NSFetchRequest, from: From<T>? = nil, sectionBy: SectionBy? = nil, fetchClauses: [FetchClause]) -> NSFetchedResultsController {
|
||||
|
||||
return CoreStoreFetchedResultsController<T>(
|
||||
context: context,
|
||||
fetchRequest: fetchRequest,
|
||||
from: from,
|
||||
sectionBy: sectionBy,
|
||||
fetchClauses: fetchClauses
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,22 +36,25 @@ public struct From<T: NSManagedObject> {
|
||||
|
||||
// MARK: Public
|
||||
|
||||
/**
|
||||
Initializes a `From` clause with the specified entity type and configuration.
|
||||
Sample Usage:
|
||||
|
||||
let person = transaction.fetchOne(From<MyPersonEntity>())
|
||||
*/
|
||||
public init(){
|
||||
|
||||
self.entityClass = T.self
|
||||
self.findPersistentStores = { _ in nil }
|
||||
self.init(entityClass: T.self)
|
||||
}
|
||||
|
||||
public init(_ entity: T.Type) {
|
||||
|
||||
self.entityClass = entity
|
||||
self.findPersistentStores = { _ in nil }
|
||||
self.init(entityClass: entity)
|
||||
}
|
||||
|
||||
public init(_ entityClass: AnyClass) {
|
||||
|
||||
self.entityClass = entityClass
|
||||
self.findPersistentStores = { _ in nil }
|
||||
self.init(entityClass: entityClass)
|
||||
}
|
||||
|
||||
public init(_ configuration: String?, otherConfigurations: String?...) {
|
||||
@@ -147,10 +150,20 @@ public struct From<T: NSManagedObject> {
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
internal func applyToFetchRequest(fetchRequest: NSFetchRequest, context: NSManagedObjectContext) {
|
||||
internal func applyToFetchRequest(fetchRequest: NSFetchRequest, context: NSManagedObjectContext, applyAffectedStores: Bool = true) {
|
||||
|
||||
fetchRequest.entity = context.entityDescriptionForEntityClass(self.entityClass)
|
||||
fetchRequest.affectedStores = self.findPersistentStores(context: context)
|
||||
if applyAffectedStores {
|
||||
|
||||
self.applyAffectedStoresForFetchedRequest(fetchRequest, context: context)
|
||||
}
|
||||
}
|
||||
|
||||
internal func applyAffectedStoresForFetchedRequest(fetchRequest: NSFetchRequest, context: NSManagedObjectContext) -> Bool {
|
||||
|
||||
let stores = self.findPersistentStores(context: context)
|
||||
fetchRequest.affectedStores = stores
|
||||
return stores?.isEmpty == false
|
||||
}
|
||||
|
||||
|
||||
@@ -160,6 +173,15 @@ public struct From<T: NSManagedObject> {
|
||||
|
||||
private let findPersistentStores: (context: NSManagedObjectContext) -> [NSPersistentStore]?
|
||||
|
||||
private init(entityClass: AnyClass) {
|
||||
|
||||
self.entityClass = entityClass
|
||||
self.findPersistentStores = { (context: NSManagedObjectContext) -> [NSPersistentStore]? in
|
||||
|
||||
return context.parentStack?.persistentStoresForEntityClass(entityClass)
|
||||
}
|
||||
}
|
||||
|
||||
private init(entityClass: AnyClass, configurations: [String?]) {
|
||||
|
||||
let configurationsSet = Set(configurations.map { $0 ?? Into.defaultConfigurationName })
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.4.4</string>
|
||||
<string>1.5.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
|
||||
101
CoreStore/Internal/CoreStoreFetchedResultsController.swift
Normal file
101
CoreStore/Internal/CoreStoreFetchedResultsController.swift
Normal file
@@ -0,0 +1,101 @@
|
||||
//
|
||||
// CoreStoreFetchedResultsController.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: - CoreStoreFetchedResultsController
|
||||
|
||||
@available(OSX, unavailable)
|
||||
internal final class CoreStoreFetchedResultsController<T: NSManagedObject>: NSFetchedResultsController {
|
||||
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
internal convenience init<T>(dataStack: DataStack, fetchRequest: NSFetchRequest, from: From<T>? = nil, sectionBy: SectionBy? = nil, fetchClauses: [FetchClause]) {
|
||||
|
||||
self.init(
|
||||
context: dataStack.mainContext,
|
||||
fetchRequest: fetchRequest,
|
||||
from: from,
|
||||
sectionBy: sectionBy,
|
||||
fetchClauses: fetchClauses
|
||||
)
|
||||
}
|
||||
|
||||
internal init<T>(context: NSManagedObjectContext, fetchRequest: NSFetchRequest, from: From<T>? = nil, sectionBy: SectionBy? = nil, fetchClauses: [FetchClause]) {
|
||||
|
||||
from?.applyToFetchRequest(fetchRequest, context: context, applyAffectedStores: false)
|
||||
for clause in fetchClauses {
|
||||
|
||||
clause.applyToFetchRequest(fetchRequest)
|
||||
}
|
||||
|
||||
if let from = from {
|
||||
|
||||
self.reapplyAffectedStores = {
|
||||
|
||||
return from.applyAffectedStoresForFetchedRequest(fetchRequest, context: context)
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
guard let from = (fetchRequest.entity.flatMap { $0.managedObjectClassName }).flatMap(NSClassFromString).flatMap(From.init) else {
|
||||
|
||||
fatalError("Attempted to create an \(typeName(NSFetchedResultsController)) without a From clause or an NSEntityDescription.")
|
||||
}
|
||||
|
||||
self.reapplyAffectedStores = {
|
||||
|
||||
return from.applyAffectedStoresForFetchedRequest(fetchRequest, context: context)
|
||||
}
|
||||
}
|
||||
|
||||
super.init(
|
||||
fetchRequest: fetchRequest,
|
||||
managedObjectContext: context,
|
||||
sectionNameKeyPath: sectionBy?.sectionKeyPath,
|
||||
cacheName: nil
|
||||
)
|
||||
}
|
||||
|
||||
internal func performFetchFromSpecifiedStores() throws {
|
||||
|
||||
if !self.reapplyAffectedStores() {
|
||||
|
||||
CoreStore.log(
|
||||
.Warning,
|
||||
message: "Attempted to perform a fetch on an \(typeName(NSFetchedResultsController)) but could not find any persistent store for the entity \(typeName(self.fetchRequest.entityName))"
|
||||
)
|
||||
}
|
||||
try self.performFetch()
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let reapplyAffectedStores: () -> Bool
|
||||
}
|
||||
@@ -882,12 +882,15 @@ public final class ListMonitor<T: NSManagedObject> {
|
||||
"Attempted to refetch a \(typeName(self)) outside the main thread."
|
||||
)
|
||||
|
||||
self.isPendingRefetch = true
|
||||
|
||||
NSNotificationCenter.defaultCenter().postNotificationName(
|
||||
ListMonitorWillRefetchListNotification,
|
||||
object: self
|
||||
)
|
||||
if !self.isPendingRefetch {
|
||||
|
||||
self.isPendingRefetch = true
|
||||
|
||||
NSNotificationCenter.defaultCenter().postNotificationName(
|
||||
ListMonitorWillRefetchListNotification,
|
||||
object: self
|
||||
)
|
||||
}
|
||||
|
||||
self.taskGroup.notify(.Main) { [weak self] () -> Void in
|
||||
|
||||
@@ -904,14 +907,14 @@ public final class ListMonitor<T: NSManagedObject> {
|
||||
clause.applyToFetchRequest(fetchRequest)
|
||||
}
|
||||
|
||||
strongSelf.parentStack?.childTransactionQueue.async {
|
||||
strongSelf.transactionQueue.async {
|
||||
|
||||
guard let strongSelf = self else {
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
try! strongSelf.fetchedResultsController.performFetch()
|
||||
try! strongSelf.fetchedResultsController.performFetchFromSpecifiedStores()
|
||||
|
||||
GCDQueue.Main.async { () -> Void in
|
||||
|
||||
@@ -938,36 +941,54 @@ public final class ListMonitor<T: NSManagedObject> {
|
||||
internal convenience init(dataStack: DataStack, from: From<T>, sectionBy: SectionBy?, fetchClauses: [FetchClause]) {
|
||||
|
||||
self.init(
|
||||
dataStack: dataStack,
|
||||
context: dataStack.mainContext,
|
||||
transactionQueue: dataStack.childTransactionQueue,
|
||||
from: from,
|
||||
sectionBy: sectionBy,
|
||||
fetchClauses: fetchClauses,
|
||||
prepareFetch: { _, performFetch in performFetch() }
|
||||
createAsynchronously: nil
|
||||
)
|
||||
}
|
||||
|
||||
internal convenience init(dataStack: DataStack, from: From<T>, sectionBy: SectionBy?, fetchClauses: [FetchClause], createAsynchronously: (ListMonitor<T>) -> Void) {
|
||||
|
||||
let queue = dataStack.childTransactionQueue
|
||||
self.init(
|
||||
dataStack: dataStack,
|
||||
context: dataStack.mainContext,
|
||||
transactionQueue: queue,
|
||||
from: from,
|
||||
sectionBy: sectionBy,
|
||||
fetchClauses: fetchClauses,
|
||||
prepareFetch: { listMonitor, performFetch in
|
||||
|
||||
dataStack.childTransactionQueue.async {
|
||||
|
||||
performFetch()
|
||||
GCDQueue.Main.async {
|
||||
|
||||
createAsynchronously(listMonitor)
|
||||
}
|
||||
}
|
||||
}
|
||||
createAsynchronously: createAsynchronously
|
||||
)
|
||||
}
|
||||
|
||||
private init(dataStack: DataStack, from: From<T>, sectionBy: SectionBy?, fetchClauses: [FetchClause], prepareFetch: (ListMonitor<T>, () -> Void) -> Void) {
|
||||
internal convenience init(unsafeTransaction: UnsafeDataTransaction, from: From<T>, sectionBy: SectionBy?, fetchClauses: [FetchClause]) {
|
||||
|
||||
self.init(
|
||||
context: unsafeTransaction.context,
|
||||
transactionQueue: unsafeTransaction.transactionQueue,
|
||||
from: from,
|
||||
sectionBy: sectionBy,
|
||||
fetchClauses: fetchClauses,
|
||||
createAsynchronously: nil
|
||||
)
|
||||
}
|
||||
|
||||
internal convenience init(unsafeTransaction: UnsafeDataTransaction, from: From<T>, sectionBy: SectionBy?, fetchClauses: [FetchClause], createAsynchronously: (ListMonitor<T>) -> Void) {
|
||||
|
||||
let queue = unsafeTransaction.transactionQueue
|
||||
self.init(
|
||||
context: unsafeTransaction.context,
|
||||
transactionQueue: queue,
|
||||
from: from,
|
||||
sectionBy: sectionBy,
|
||||
fetchClauses: fetchClauses,
|
||||
createAsynchronously: createAsynchronously
|
||||
)
|
||||
}
|
||||
|
||||
private init(context: NSManagedObjectContext, transactionQueue: GCDQueue, from: From<T>, sectionBy: SectionBy?, fetchClauses: [FetchClause], createAsynchronously: ((ListMonitor<T>) -> Void)?) {
|
||||
|
||||
let fetchRequest = NSFetchRequest()
|
||||
fetchRequest.fetchLimit = 0
|
||||
@@ -976,8 +997,8 @@ public final class ListMonitor<T: NSManagedObject> {
|
||||
fetchRequest.includesPendingChanges = false
|
||||
fetchRequest.shouldRefreshRefetchedObjects = true
|
||||
|
||||
let fetchedResultsController = NSFetchedResultsController(
|
||||
dataStack: dataStack,
|
||||
let fetchedResultsController = CoreStoreFetchedResultsController<T>(
|
||||
context: context,
|
||||
fetchRequest: fetchRequest,
|
||||
from: from,
|
||||
sectionBy: sectionBy,
|
||||
@@ -988,7 +1009,6 @@ public final class ListMonitor<T: NSManagedObject> {
|
||||
|
||||
self.fetchedResultsController = fetchedResultsController
|
||||
self.fetchedResultsControllerDelegate = fetchedResultsControllerDelegate
|
||||
self.parentStack = dataStack
|
||||
|
||||
if let sectionIndexTransformer = sectionBy?.sectionIndexTransformer {
|
||||
|
||||
@@ -998,26 +1018,118 @@ public final class ListMonitor<T: NSManagedObject> {
|
||||
|
||||
self.sectionIndexTransformer = { $0 }
|
||||
}
|
||||
self.transactionQueue = transactionQueue
|
||||
|
||||
fetchedResultsControllerDelegate.handler = self
|
||||
fetchedResultsControllerDelegate.fetchedResultsController = fetchedResultsController
|
||||
|
||||
prepareFetch(self, { try! fetchedResultsController.performFetch() })
|
||||
guard let coordinator = context.parentStack?.coordinator else {
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
self.observerForWillChangePersistentStore = NotificationObserver(
|
||||
notificationName: NSPersistentStoreCoordinatorStoresWillChangeNotification,
|
||||
object: coordinator,
|
||||
closure: { [weak self] (note) -> Void in
|
||||
|
||||
guard let `self` = self else {
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
self.isPersistentStoreChanging = true
|
||||
|
||||
guard let removedStores = (note.userInfo?[NSRemovedPersistentStoresKey] as? [NSPersistentStore]).flatMap(Set.init)
|
||||
where !Set(self.fetchedResultsController.fetchRequest.affectedStores ?? []).intersect(removedStores).isEmpty else {
|
||||
|
||||
return
|
||||
}
|
||||
self.refetch(fetchClauses)
|
||||
}
|
||||
)
|
||||
|
||||
self.observerForDidChangePersistentStore = NotificationObserver(
|
||||
notificationName: NSPersistentStoreCoordinatorStoresDidChangeNotification,
|
||||
object: coordinator,
|
||||
closure: { [weak self] (note) -> Void in
|
||||
|
||||
guard let `self` = self else {
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if !self.isPendingRefetch {
|
||||
|
||||
let previousStores = Set(self.fetchedResultsController.fetchRequest.affectedStores ?? [])
|
||||
let currentStores = previousStores
|
||||
.subtract(note.userInfo?[NSRemovedPersistentStoresKey] as? [NSPersistentStore] ?? [])
|
||||
.union(note.userInfo?[NSAddedPersistentStoresKey] as? [NSPersistentStore] ?? [])
|
||||
|
||||
if previousStores != currentStores {
|
||||
|
||||
self.refetch(fetchClauses)
|
||||
}
|
||||
}
|
||||
|
||||
self.isPersistentStoreChanging = false
|
||||
}
|
||||
)
|
||||
|
||||
if let createAsynchronously = createAsynchronously {
|
||||
|
||||
transactionQueue.async {
|
||||
|
||||
try! fetchedResultsController.performFetchFromSpecifiedStores()
|
||||
self.taskGroup.notify(.Main) {
|
||||
|
||||
createAsynchronously(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
try! fetchedResultsController.performFetchFromSpecifiedStores()
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
|
||||
self.fetchedResultsControllerDelegate.fetchedResultsController = nil
|
||||
self.isPersistentStoreChanging = false
|
||||
}
|
||||
|
||||
private var isPersistentStoreChanging: Bool = false {
|
||||
|
||||
didSet {
|
||||
|
||||
let newValue = self.isPersistentStoreChanging
|
||||
guard newValue != oldValue else {
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if newValue {
|
||||
|
||||
self.taskGroup.enter()
|
||||
}
|
||||
else {
|
||||
|
||||
self.taskGroup.leave()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let fetchedResultsController: NSFetchedResultsController
|
||||
private let fetchedResultsController: CoreStoreFetchedResultsController<T>
|
||||
private let fetchedResultsControllerDelegate: FetchedResultsControllerDelegate
|
||||
private let sectionIndexTransformer: (sectionName: KeyPath?) -> String?
|
||||
private var observerForWillChangePersistentStore: NotificationObserver!
|
||||
private var observerForDidChangePersistentStore: NotificationObserver!
|
||||
private let taskGroup = GCDGroup()
|
||||
private weak var parentStack: DataStack?
|
||||
private let transactionQueue: GCDQueue
|
||||
|
||||
private var willChangeListKey: Void?
|
||||
private var didChangeListKey: Void?
|
||||
|
||||
@@ -165,7 +165,17 @@ public final class ObjectMonitor<T: NSManagedObject> {
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
internal init(dataStack: DataStack, object: T) {
|
||||
internal convenience init(dataStack: DataStack, object: T) {
|
||||
|
||||
self.init(context: dataStack.mainContext, object: object)
|
||||
}
|
||||
|
||||
internal convenience init(unsafeTransaction: UnsafeDataTransaction, object: T) {
|
||||
|
||||
self.init(context: unsafeTransaction.context, object: object)
|
||||
}
|
||||
|
||||
private init(context: NSManagedObjectContext, object: T) {
|
||||
|
||||
let fetchRequest = NSFetchRequest()
|
||||
fetchRequest.entity = object.entity
|
||||
@@ -175,8 +185,8 @@ public final class ObjectMonitor<T: NSManagedObject> {
|
||||
fetchRequest.includesPendingChanges = false
|
||||
fetchRequest.shouldRefreshRefetchedObjects = true
|
||||
|
||||
let fetchedResultsController = NSFetchedResultsController(
|
||||
dataStack: dataStack,
|
||||
let fetchedResultsController = CoreStoreFetchedResultsController<T>(
|
||||
context: context,
|
||||
fetchRequest: fetchRequest,
|
||||
fetchClauses: [Where("SELF", isEqualTo: object.objectID)]
|
||||
)
|
||||
@@ -185,11 +195,10 @@ public final class ObjectMonitor<T: NSManagedObject> {
|
||||
|
||||
self.fetchedResultsController = fetchedResultsController
|
||||
self.fetchedResultsControllerDelegate = fetchedResultsControllerDelegate
|
||||
self.parentStack = dataStack
|
||||
|
||||
fetchedResultsControllerDelegate.handler = self
|
||||
fetchedResultsControllerDelegate.fetchedResultsController = fetchedResultsController
|
||||
try! fetchedResultsController.performFetch()
|
||||
try! fetchedResultsController.performFetchFromSpecifiedStores()
|
||||
|
||||
self.lastCommittedAttributes = (self.object?.committedValuesForKeys(nil) as? [String: NSObject]) ?? [:]
|
||||
}
|
||||
@@ -202,10 +211,9 @@ public final class ObjectMonitor<T: NSManagedObject> {
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let fetchedResultsController: NSFetchedResultsController
|
||||
private let fetchedResultsController: CoreStoreFetchedResultsController<T>
|
||||
private let fetchedResultsControllerDelegate: FetchedResultsControllerDelegate
|
||||
private var lastCommittedAttributes = [String: NSObject]()
|
||||
private weak var parentStack: DataStack?
|
||||
|
||||
private var willChangeObjectKey: Void?
|
||||
private var didDeleteObjectKey: Void?
|
||||
|
||||
201
CoreStore/Observing/UnsafeDataTransaction+Observing.swift
Normal file
201
CoreStore/Observing/UnsafeDataTransaction+Observing.swift
Normal file
@@ -0,0 +1,201 @@
|
||||
//
|
||||
// UnsafeDataTransaction+Observing.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
|
||||
#if USE_FRAMEWORKS
|
||||
import GCDKit
|
||||
#endif
|
||||
|
||||
|
||||
// MARK: - UnsafeDataTransaction
|
||||
|
||||
@available(OSX, unavailable)
|
||||
public extension UnsafeDataTransaction {
|
||||
|
||||
|
||||
// MARK: Public
|
||||
|
||||
/**
|
||||
Creates a `ObjectMonitor` for the specified `NSManagedObject`. Multiple `ObjectObserver`s may then register themselves to be notified when changes are made to the `NSManagedObject`.
|
||||
|
||||
- parameter object: the `NSManagedObject` to observe changes from
|
||||
- returns: a `ObjectMonitor` that monitors changes to `object`
|
||||
*/
|
||||
@warn_unused_result
|
||||
public func monitorObject<T: NSManagedObject>(object: T) -> ObjectMonitor<T> {
|
||||
|
||||
return ObjectMonitor(
|
||||
unsafeTransaction: self,
|
||||
object: object
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a `ListMonitor` for a list of `NSManagedObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list.
|
||||
|
||||
- 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: a `ListMonitor` instance that monitors changes to the list
|
||||
*/
|
||||
@warn_unused_result
|
||||
public func monitorList<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> ListMonitor<T> {
|
||||
|
||||
return self.monitorList(from, fetchClauses)
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a `ListMonitor` for a list of `NSManagedObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list.
|
||||
|
||||
- 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: a `ListMonitor` instance that monitors changes to the list
|
||||
*/
|
||||
@warn_unused_result
|
||||
public func monitorList<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> ListMonitor<T> {
|
||||
|
||||
CoreStore.assert(
|
||||
fetchClauses.filter { $0 is OrderBy }.count > 0,
|
||||
"A ListMonitor requires an OrderBy clause."
|
||||
)
|
||||
|
||||
return ListMonitor(
|
||||
unsafeTransaction: self,
|
||||
from: from,
|
||||
sectionBy: nil,
|
||||
fetchClauses: fetchClauses
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
Asynchronously creates a `ListMonitor` for a list of `NSManagedObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list. Since `NSFetchedResultsController` greedily locks the persistent store on initial fetch, you may prefer this method instead of the synchronous counterpart to avoid deadlocks while background updates/saves are being executed.
|
||||
|
||||
- parameter createAsynchronously: the closure that receives the created `ListMonitor` instance
|
||||
- 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.
|
||||
*/
|
||||
public func monitorList<T: NSManagedObject>(createAsynchronously createAsynchronously: (ListMonitor<T>) -> Void, _ from: From<T>, _ fetchClauses: FetchClause...) {
|
||||
|
||||
self.monitorList(createAsynchronously: createAsynchronously, from, fetchClauses)
|
||||
}
|
||||
|
||||
/**
|
||||
Asynchronously creates a `ListMonitor` for a list of `NSManagedObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list. Since `NSFetchedResultsController` greedily locks the persistent store on initial fetch, you may prefer this method instead of the synchronous counterpart to avoid deadlocks while background updates/saves are being executed.
|
||||
|
||||
- parameter createAsynchronously: the closure that receives the created `ListMonitor` instance
|
||||
- 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.
|
||||
*/
|
||||
public func monitorList<T: NSManagedObject>(createAsynchronously createAsynchronously: (ListMonitor<T>) -> Void, _ from: From<T>, _ fetchClauses: [FetchClause]) {
|
||||
|
||||
CoreStore.assert(
|
||||
fetchClauses.filter { $0 is OrderBy }.count > 0,
|
||||
"A ListMonitor requires an OrderBy clause."
|
||||
)
|
||||
|
||||
_ = ListMonitor(
|
||||
unsafeTransaction: self,
|
||||
from: from,
|
||||
sectionBy: nil,
|
||||
fetchClauses: fetchClauses,
|
||||
createAsynchronously: createAsynchronously
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a `ListMonitor` for a sectioned list of `NSManagedObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list.
|
||||
|
||||
- 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: a `ListMonitor` instance that monitors changes to the list
|
||||
*/
|
||||
@warn_unused_result
|
||||
public func monitorSectionedList<T: NSManagedObject>(from: From<T>, _ sectionBy: SectionBy, _ fetchClauses: FetchClause...) -> ListMonitor<T> {
|
||||
|
||||
return self.monitorSectionedList(from, sectionBy, fetchClauses)
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a `ListMonitor` for a sectioned list of `NSManagedObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list.
|
||||
|
||||
- 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: a `ListMonitor` instance that monitors changes to the list
|
||||
*/
|
||||
@warn_unused_result
|
||||
public func monitorSectionedList<T: NSManagedObject>(from: From<T>, _ sectionBy: SectionBy, _ fetchClauses: [FetchClause]) -> ListMonitor<T> {
|
||||
|
||||
CoreStore.assert(
|
||||
fetchClauses.filter { $0 is OrderBy }.count > 0,
|
||||
"A ListMonitor requires an OrderBy clause."
|
||||
)
|
||||
|
||||
return ListMonitor(
|
||||
unsafeTransaction: self,
|
||||
from: from,
|
||||
sectionBy: sectionBy,
|
||||
fetchClauses: fetchClauses
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
Asynchronously creates a `ListMonitor` for a sectioned list of `NSManagedObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list. Since `NSFetchedResultsController` greedily locks the persistent store on initial fetch, you may prefer this method instead of the synchronous counterpart to avoid deadlocks while background updates/saves are being executed.
|
||||
|
||||
- parameter createAsynchronously: the closure that receives the created `ListMonitor` instance
|
||||
- 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.
|
||||
*/
|
||||
public func monitorSectionedList<T: NSManagedObject>(createAsynchronously createAsynchronously: (ListMonitor<T>) -> Void, _ from: From<T>, _ sectionBy: SectionBy, _ fetchClauses: FetchClause...) {
|
||||
|
||||
self.monitorSectionedList(createAsynchronously: createAsynchronously, from, sectionBy, fetchClauses)
|
||||
}
|
||||
|
||||
/**
|
||||
Asynchronously creates a `ListMonitor` for a sectioned list of `NSManagedObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list. Since `NSFetchedResultsController` greedily locks the persistent store on initial fetch, you may prefer this method instead of the synchronous counterpart to avoid deadlocks while background updates/saves are being executed.
|
||||
|
||||
- parameter createAsynchronously: the closure that receives the created `ListMonitor` instance
|
||||
- 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.
|
||||
*/
|
||||
public func monitorSectionedList<T: NSManagedObject>(createAsynchronously createAsynchronously: (ListMonitor<T>) -> Void, _ from: From<T>, _ sectionBy: SectionBy, _ fetchClauses: [FetchClause]) {
|
||||
|
||||
CoreStore.assert(
|
||||
fetchClauses.filter { $0 is OrderBy }.count > 0,
|
||||
"A ListMonitor requires an OrderBy clause."
|
||||
)
|
||||
|
||||
_ = ListMonitor(
|
||||
unsafeTransaction: self,
|
||||
from: from,
|
||||
sectionBy: sectionBy,
|
||||
fetchClauses: fetchClauses,
|
||||
createAsynchronously: createAsynchronously
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -367,11 +367,12 @@ public final class DataStack {
|
||||
self.configurationStoreMapping[configurationName] = persistentStore
|
||||
for entityDescription in (self.coordinator.managedObjectModel.entitiesForConfiguration(configurationName) ?? []) {
|
||||
|
||||
if self.entityConfigurationsMapping[entityDescription.managedObjectClassName] == nil {
|
||||
let managedObjectClassName = entityDescription.managedObjectClassName
|
||||
if self.entityConfigurationsMapping[managedObjectClassName] == nil {
|
||||
|
||||
self.entityConfigurationsMapping[entityDescription.managedObjectClassName] = []
|
||||
self.entityConfigurationsMapping[managedObjectClassName] = []
|
||||
}
|
||||
self.entityConfigurationsMapping[entityDescription.managedObjectClassName]?.insert(configurationName)
|
||||
self.entityConfigurationsMapping[managedObjectClassName]?.insert(configurationName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user