mirror of
https://github.com/JohnEstropia/CoreStore.git
synced 2026-01-14 13:13:33 +01:00
Compare commits
14 Commits
8.0.0
...
transactio
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bf10f4668c | ||
|
|
a1a04aaf8a | ||
|
|
4ddfa95140 | ||
|
|
45215c7a18 | ||
|
|
d2f1656fdd | ||
|
|
5fbb1ab09d | ||
|
|
d0cc01877e | ||
|
|
a434628249 | ||
|
|
9cc616f720 | ||
|
|
4534bc06f5 | ||
|
|
798d30bbd9 | ||
|
|
7938aa2447 | ||
|
|
dda69389eb | ||
|
|
6a2394052c |
@@ -1,6 +1,6 @@
|
|||||||
Pod::Spec.new do |s|
|
Pod::Spec.new do |s|
|
||||||
s.name = "CoreStore"
|
s.name = "CoreStore"
|
||||||
s.version = "8.0.0"
|
s.version = "8.0.1"
|
||||||
s.swift_version = "5.4"
|
s.swift_version = "5.4"
|
||||||
s.license = "MIT"
|
s.license = "MIT"
|
||||||
s.homepage = "https://github.com/JohnEstropia/CoreStore"
|
s.homepage = "https://github.com/JohnEstropia/CoreStore"
|
||||||
|
|||||||
@@ -3465,7 +3465,7 @@
|
|||||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||||
MARKETING_VERSION = 8.0.0;
|
MARKETING_VERSION = 8.0.1;
|
||||||
OTHER_LDFLAGS = (
|
OTHER_LDFLAGS = (
|
||||||
"-weak_framework",
|
"-weak_framework",
|
||||||
Combine,
|
Combine,
|
||||||
@@ -3488,7 +3488,7 @@
|
|||||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||||
MARKETING_VERSION = 8.0.0;
|
MARKETING_VERSION = 8.0.1;
|
||||||
OTHER_LDFLAGS = (
|
OTHER_LDFLAGS = (
|
||||||
"-weak_framework",
|
"-weak_framework",
|
||||||
Combine,
|
Combine,
|
||||||
@@ -3548,7 +3548,7 @@
|
|||||||
GCC_NO_COMMON_BLOCKS = YES;
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||||
MARKETING_VERSION = 8.0.0;
|
MARKETING_VERSION = 8.0.1;
|
||||||
OTHER_LDFLAGS = (
|
OTHER_LDFLAGS = (
|
||||||
"-weak_framework",
|
"-weak_framework",
|
||||||
Combine,
|
Combine,
|
||||||
@@ -3575,7 +3575,7 @@
|
|||||||
GCC_NO_COMMON_BLOCKS = YES;
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||||
MARKETING_VERSION = 8.0.0;
|
MARKETING_VERSION = 8.0.1;
|
||||||
OTHER_LDFLAGS = (
|
OTHER_LDFLAGS = (
|
||||||
"-weak_framework",
|
"-weak_framework",
|
||||||
Combine,
|
Combine,
|
||||||
@@ -3641,7 +3641,7 @@
|
|||||||
GCC_NO_COMMON_BLOCKS = YES;
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
|
||||||
MARKETING_VERSION = 8.0.0;
|
MARKETING_VERSION = 8.0.1;
|
||||||
OTHER_LDFLAGS = (
|
OTHER_LDFLAGS = (
|
||||||
"-weak_framework",
|
"-weak_framework",
|
||||||
Combine,
|
Combine,
|
||||||
@@ -3670,7 +3670,7 @@
|
|||||||
GCC_NO_COMMON_BLOCKS = YES;
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
|
||||||
MARKETING_VERSION = 8.0.0;
|
MARKETING_VERSION = 8.0.1;
|
||||||
OTHER_LDFLAGS = (
|
OTHER_LDFLAGS = (
|
||||||
"-weak_framework",
|
"-weak_framework",
|
||||||
Combine,
|
Combine,
|
||||||
@@ -3736,7 +3736,7 @@
|
|||||||
GCC_NO_COMMON_BLOCKS = YES;
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||||
MARKETING_VERSION = 8.0.0;
|
MARKETING_VERSION = 8.0.1;
|
||||||
OTHER_LDFLAGS = (
|
OTHER_LDFLAGS = (
|
||||||
"-weak_framework",
|
"-weak_framework",
|
||||||
Combine,
|
Combine,
|
||||||
@@ -3765,7 +3765,7 @@
|
|||||||
GCC_NO_COMMON_BLOCKS = YES;
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||||
MARKETING_VERSION = 8.0.0;
|
MARKETING_VERSION = 8.0.1;
|
||||||
OTHER_LDFLAGS = (
|
OTHER_LDFLAGS = (
|
||||||
"-weak_framework",
|
"-weak_framework",
|
||||||
Combine,
|
Combine,
|
||||||
|
|||||||
@@ -23,6 +23,9 @@
|
|||||||
// SOFTWARE.
|
// SOFTWARE.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import CoreData
|
||||||
|
import XCTest
|
||||||
|
|
||||||
@testable
|
@testable
|
||||||
import CoreStore
|
import CoreStore
|
||||||
|
|
||||||
|
|||||||
@@ -197,7 +197,7 @@ class DynamicModelTests: BaseTestDataTestCase {
|
|||||||
modelVersion: "V1",
|
modelVersion: "V1",
|
||||||
entities: [
|
entities: [
|
||||||
Entity<Animal>("Animal"),
|
Entity<Animal>("Animal"),
|
||||||
Entity<Dog>("Dog"),
|
Entity<Dog>("Dog", indexes: [[\Dog.$nickname, \Dog.$age]]),
|
||||||
Entity<Person>("Person")
|
Entity<Person>("Person")
|
||||||
],
|
],
|
||||||
versionLock: [
|
versionLock: [
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
// SOFTWARE.
|
// SOFTWARE.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import CoreData
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
@testable
|
@testable
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
// SOFTWARE.
|
// SOFTWARE.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import CoreData
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
@testable
|
@testable
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
// SOFTWARE.
|
// SOFTWARE.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import CoreData
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
@testable
|
@testable
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
// SOFTWARE.
|
// SOFTWARE.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import CoreData
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
@testable
|
@testable
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
// SOFTWARE.
|
// SOFTWARE.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import CoreData
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
@testable
|
@testable
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
// SOFTWARE.
|
// SOFTWARE.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import CoreData
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
@testable
|
@testable
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
// SOFTWARE.
|
// SOFTWARE.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import CoreData
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
@testable
|
@testable
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
// SOFTWARE.
|
// SOFTWARE.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import CoreData
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
@testable
|
@testable
|
||||||
|
|||||||
@@ -23,6 +23,9 @@
|
|||||||
// SOFTWARE.
|
// SOFTWARE.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import CoreData
|
||||||
|
import XCTest
|
||||||
|
|
||||||
@testable
|
@testable
|
||||||
import CoreStore
|
import CoreStore
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
// SOFTWARE.
|
// SOFTWARE.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import CoreData
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
@testable
|
@testable
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
// SOFTWARE.
|
// SOFTWARE.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import CoreData
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
@testable
|
@testable
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
// SOFTWARE.
|
// SOFTWARE.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import CoreData
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
@testable
|
@testable
|
||||||
|
|||||||
@@ -102,6 +102,7 @@ extension Modern.ColorsDemo {
|
|||||||
|
|
||||||
try transaction.deleteAll(From<Modern.ColorsDemo.Palette>())
|
try transaction.deleteAll(From<Modern.ColorsDemo.Palette>())
|
||||||
},
|
},
|
||||||
|
sourceIdentifier: TransactionSource.clear,
|
||||||
completion: { _ in }
|
completion: { _ in }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -113,6 +114,7 @@ extension Modern.ColorsDemo {
|
|||||||
|
|
||||||
_ = transaction.create(Into<Modern.ColorsDemo.Palette>())
|
_ = transaction.create(Into<Modern.ColorsDemo.Palette>())
|
||||||
},
|
},
|
||||||
|
sourceIdentifier: TransactionSource.add,
|
||||||
completion: { _ in }
|
completion: { _ in }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -127,6 +129,7 @@ extension Modern.ColorsDemo {
|
|||||||
palette.setRandomHue()
|
palette.setRandomHue()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
sourceIdentifier: TransactionSource.shuffle,
|
||||||
completion: { _ in }
|
completion: { _ in }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,9 +65,22 @@ extension Modern {
|
|||||||
sectionIndexTransformer: { $0?.first?.uppercased() }
|
sectionIndexTransformer: { $0?.first?.uppercased() }
|
||||||
)
|
)
|
||||||
.where(self.filter.whereClause())
|
.where(self.filter.whereClause())
|
||||||
.orderBy(.ascending(\.$hue))
|
.orderBy(.ascending(\.$hue)),
|
||||||
|
sourceIdentifier: TransactionSource.refetch
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - TransactionSource
|
||||||
|
|
||||||
|
enum TransactionSource {
|
||||||
|
|
||||||
|
case add
|
||||||
|
case delete
|
||||||
|
case shuffle
|
||||||
|
case clear
|
||||||
|
case refetch
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -91,7 +91,8 @@ extension Modern.ColorsDemo.UIKit {
|
|||||||
func objectMonitor(
|
func objectMonitor(
|
||||||
_ monitor: ObjectMonitor<Modern.ColorsDemo.Palette>,
|
_ monitor: ObjectMonitor<Modern.ColorsDemo.Palette>,
|
||||||
didUpdateObject object: Modern.ColorsDemo.Palette,
|
didUpdateObject object: Modern.ColorsDemo.Palette,
|
||||||
changedPersistentKeys: Set<KeyPathString>
|
changedPersistentKeys: Set<KeyPathString>,
|
||||||
|
sourceIdentifier: Any?
|
||||||
) {
|
) {
|
||||||
|
|
||||||
self.reloadPaletteInfo(object, changedKeys: changedPersistentKeys)
|
self.reloadPaletteInfo(object, changedKeys: changedPersistentKeys)
|
||||||
|
|||||||
@@ -32,17 +32,26 @@ extension Modern.ColorsDemo.UIKit {
|
|||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
⭐️ Sample 2: Once the views are created, we can start binding `ListPublisher` updates to the `DiffableDataSource`. We typically call this at the end of `viewDidLoad`. Note that the `addObserver`'s closure argument will only be called on the succeeding updates, so to immediately display the current values, we need to call `dataSource.apply()` once.
|
⭐️ Sample 2: Once the views are created, we can start binding `ListPublisher` updates to the `DiffableDataSource`. We typically call this at the end of `viewDidLoad`. Note that the `addObserver`'s closure argument will only be called on the succeeding updates, so to immediately display the current values, we need to call `dataSource.apply()` once. This example inspects the optional `transactionSource` to determine the source of the update which is helpful for debugging or for fine-tuning animations.
|
||||||
*/
|
*/
|
||||||
private func startObservingList() {
|
private func startObservingList() {
|
||||||
|
|
||||||
let dataSource = self.dataSource
|
let dataSource = self.dataSource
|
||||||
self.listPublisher.addObserver(self) { (listPublisher) in
|
self.listPublisher.addObserver(self, notifyInitial: true) { (listPublisher, transactionSource) in
|
||||||
|
|
||||||
dataSource.apply(listPublisher.snapshot, animatingDifferences: true)
|
switch transactionSource as? Modern.ColorsDemo.TransactionSource {
|
||||||
|
|
||||||
|
case .add,
|
||||||
|
.delete,
|
||||||
|
.shuffle,
|
||||||
|
.clear:
|
||||||
|
dataSource.apply(listPublisher.snapshot, animatingDifferences: true)
|
||||||
|
|
||||||
|
case nil,
|
||||||
|
.refetch:
|
||||||
|
dataSource.apply(listPublisher.snapshot, animatingDifferences: false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dataSource.apply(self.listPublisher.snapshot, animatingDifferences: false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -74,6 +83,7 @@ extension Modern.ColorsDemo.UIKit {
|
|||||||
|
|
||||||
transaction.delete(objectIDs: [itemID])
|
transaction.delete(objectIDs: [itemID])
|
||||||
},
|
},
|
||||||
|
sourceIdentifier: Modern.ColorsDemo.TransactionSource.delete,
|
||||||
completion: { _ in }
|
completion: { _ in }
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ let package = Package(
|
|||||||
.macOS(.v10_13), .iOS(.v11), .tvOS(.v11), .watchOS(.v4)
|
.macOS(.v10_13), .iOS(.v11), .tvOS(.v11), .watchOS(.v4)
|
||||||
],
|
],
|
||||||
products: [
|
products: [
|
||||||
.library(name: "CoreStore", type: .static, targets: ["CoreStore"])
|
.library(name: "CoreStore", targets: ["CoreStore"])
|
||||||
],
|
],
|
||||||
dependencies: [],
|
dependencies: [],
|
||||||
targets: [
|
targets: [
|
||||||
|
|||||||
@@ -2577,7 +2577,7 @@ This installs CoreStore as a framework. Declare `import CoreStore` in your swift
|
|||||||
#### Install with Swift Package Manager:
|
#### Install with Swift Package Manager:
|
||||||
```swift
|
```swift
|
||||||
dependencies: [
|
dependencies: [
|
||||||
.package(url: "https://github.com/JohnEstropia/CoreStore.git", from: "8.0.0"))
|
.package(url: "https://github.com/JohnEstropia/CoreStore.git", from: "8.0.1"))
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
Declare `import CoreStore` in your swift file to use the library.
|
Declare `import CoreStore` in your swift file to use the library.
|
||||||
|
|||||||
@@ -158,9 +158,19 @@ public final class AsynchronousDataTransaction: BaseDataTransaction {
|
|||||||
|
|
||||||
// MARK: Internal
|
// MARK: Internal
|
||||||
|
|
||||||
internal init(mainContext: NSManagedObjectContext, queue: DispatchQueue) {
|
internal init(
|
||||||
|
mainContext: NSManagedObjectContext,
|
||||||
|
queue: DispatchQueue,
|
||||||
|
sourceIdentifier: Any?
|
||||||
|
) {
|
||||||
|
|
||||||
super.init(mainContext: mainContext, queue: queue, supportsUndo: false, bypassesQueueing: false)
|
super.init(
|
||||||
|
mainContext: mainContext,
|
||||||
|
queue: queue,
|
||||||
|
supportsUndo: false,
|
||||||
|
bypassesQueueing: false,
|
||||||
|
sourceIdentifier: sourceIdentifier
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func autoCommit(_ completion: @escaping (_ hasChanges: Bool, _ error: CoreStoreError?) -> Void) {
|
internal func autoCommit(_ completion: @escaping (_ hasChanges: Bool, _ error: CoreStoreError?) -> Void) {
|
||||||
@@ -168,12 +178,15 @@ public final class AsynchronousDataTransaction: BaseDataTransaction {
|
|||||||
self.isCommitted = true
|
self.isCommitted = true
|
||||||
let group = DispatchGroup()
|
let group = DispatchGroup()
|
||||||
group.enter()
|
group.enter()
|
||||||
self.context.saveAsynchronouslyWithCompletion { (hasChanges, error) -> Void in
|
self.context.saveAsynchronously(
|
||||||
|
sourceIdentifier: self.sourceIdentifier,
|
||||||
completion(hasChanges, error)
|
completion: { (hasChanges, error) -> Void in
|
||||||
self.result = (hasChanges, error)
|
|
||||||
group.leave()
|
completion(hasChanges, error)
|
||||||
}
|
self.result = (hasChanges, error)
|
||||||
|
group.leave()
|
||||||
|
}
|
||||||
|
)
|
||||||
group.wait()
|
group.wait()
|
||||||
self.context.reset()
|
self.context.reset()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -412,6 +412,11 @@ public /*abstract*/ class BaseDataTransaction {
|
|||||||
|
|
||||||
// MARK: 3rd Party Utilities
|
// MARK: 3rd Party Utilities
|
||||||
|
|
||||||
|
/**
|
||||||
|
An arbitrary value that identifies the source of this transaction. Callers of the transaction can provide this value through the `DataStack.perform(...)` methods.
|
||||||
|
*/
|
||||||
|
public let sourceIdentifier: Any?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Allow external libraries to store custom data in the transaction. App code should rarely have a need for this.
|
Allow external libraries to store custom data in the transaction. App code should rarely have a need for this.
|
||||||
```
|
```
|
||||||
@@ -435,7 +440,13 @@ public /*abstract*/ class BaseDataTransaction {
|
|||||||
internal var isCommitted = false
|
internal var isCommitted = false
|
||||||
internal var result: (hasChanges: Bool, error: CoreStoreError?)?
|
internal var result: (hasChanges: Bool, error: CoreStoreError?)?
|
||||||
|
|
||||||
internal init(mainContext: NSManagedObjectContext, queue: DispatchQueue, supportsUndo: Bool, bypassesQueueing: Bool) {
|
internal init(
|
||||||
|
mainContext: NSManagedObjectContext,
|
||||||
|
queue: DispatchQueue,
|
||||||
|
supportsUndo: Bool,
|
||||||
|
bypassesQueueing: Bool,
|
||||||
|
sourceIdentifier: Any?
|
||||||
|
) {
|
||||||
|
|
||||||
let context = mainContext.temporaryContextInTransactionWithConcurrencyType(
|
let context = mainContext.temporaryContextInTransactionWithConcurrencyType(
|
||||||
queue == .main
|
queue == .main
|
||||||
@@ -446,6 +457,7 @@ public /*abstract*/ class BaseDataTransaction {
|
|||||||
self.context = context
|
self.context = context
|
||||||
self.supportsUndo = supportsUndo
|
self.supportsUndo = supportsUndo
|
||||||
self.bypassesQueueing = bypassesQueueing
|
self.bypassesQueueing = bypassesQueueing
|
||||||
|
self.sourceIdentifier = sourceIdentifier
|
||||||
|
|
||||||
context.parentTransaction = self
|
context.parentTransaction = self
|
||||||
context.isTransactionContext = true
|
context.isTransactionContext = true
|
||||||
|
|||||||
@@ -49,7 +49,10 @@ public final class CSSynchronousDataTransaction: CSBaseDataTransaction, CoreStor
|
|||||||
|
|
||||||
return bridge(error) {
|
return bridge(error) {
|
||||||
|
|
||||||
if case (_, let error?) = self.bridgeToSwift.context.saveSynchronously(waitForMerge: true) {
|
if case (_, let error?) = self.bridgeToSwift.context.saveSynchronously(
|
||||||
|
waitForMerge: true,
|
||||||
|
sourceIdentifier: nil
|
||||||
|
) {
|
||||||
|
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,21 +46,24 @@ public final class CSUnsafeDataTransaction: CSBaseDataTransaction, CoreStoreObje
|
|||||||
@objc
|
@objc
|
||||||
public func commitWithSuccess(_ success: (() -> Void)?, _ failure: ((CSError) -> Void)?) {
|
public func commitWithSuccess(_ success: (() -> Void)?, _ failure: ((CSError) -> Void)?) {
|
||||||
|
|
||||||
self.bridgeToSwift.context.saveAsynchronouslyWithCompletion { (_, error) in
|
self.bridgeToSwift.context.saveAsynchronously(
|
||||||
|
sourceIdentifier: nil,
|
||||||
defer {
|
completion: { (_, error) in
|
||||||
|
|
||||||
withExtendedLifetime(self, {})
|
defer {
|
||||||
|
|
||||||
|
withExtendedLifetime(self, {})
|
||||||
|
}
|
||||||
|
if let error = error {
|
||||||
|
|
||||||
|
failure?(error.bridgeToObjectiveC)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
success?()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if let error = error {
|
)
|
||||||
|
|
||||||
failure?(error.bridgeToObjectiveC)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
|
|
||||||
success?()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -71,10 +74,13 @@ public final class CSUnsafeDataTransaction: CSBaseDataTransaction, CoreStoreObje
|
|||||||
*/
|
*/
|
||||||
@objc
|
@objc
|
||||||
public func commitAndWait(error: NSErrorPointer) -> Bool {
|
public func commitAndWait(error: NSErrorPointer) -> Bool {
|
||||||
|
|
||||||
return bridge(error) {
|
return bridge(error) {
|
||||||
|
|
||||||
if case (_, let error?) = self.bridgeToSwift.context.saveSynchronously(waitForMerge: true) {
|
if case (_, let error?) = self.bridgeToSwift.context.saveSynchronously(
|
||||||
|
waitForMerge: true,
|
||||||
|
sourceIdentifier: nil
|
||||||
|
) {
|
||||||
|
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -585,7 +585,7 @@ public final class CoreStoreSchema: DynamicSchema {
|
|||||||
entityDescription.indexes = entity.indexes.map { (compoundIndexes) in
|
entityDescription.indexes = entity.indexes.map { (compoundIndexes) in
|
||||||
|
|
||||||
return NSFetchIndexDescription.init(
|
return NSFetchIndexDescription.init(
|
||||||
name: "_CoreStoreSchema_indexes_\(entityDescription.name!)_\(compoundIndexes.joined(separator: "-"))",
|
name: "_CoreStoreSchema_indexes_\(entityDescription.name!)_\(compoundIndexes.joined(separator: "_"))",
|
||||||
elements: compoundIndexes.map { (keyPath) in
|
elements: compoundIndexes.map { (keyPath) in
|
||||||
|
|
||||||
return NSFetchIndexElementDescription(
|
return NSFetchIndexElementDescription(
|
||||||
|
|||||||
@@ -35,12 +35,18 @@ extension DataStack {
|
|||||||
Performs a transaction asynchronously where `NSManagedObject` or `CoreStoreObject` creates, updates, and deletes can be made. The changes are commited automatically after the `task` closure returns. On success, the value returned from closure will be the wrapped as `.success(T)` in the `completion`'s `Result<T>`. Any errors thrown from inside the `task` will be reported as `.failure(CoreStoreError)`. To cancel/rollback changes, call `try transaction.cancel()`, which throws a `CoreStoreError.userCancelled`.
|
Performs a transaction asynchronously where `NSManagedObject` or `CoreStoreObject` creates, updates, and deletes can be made. The changes are commited automatically after the `task` closure returns. On success, the value returned from closure will be the wrapped as `.success(T)` in the `completion`'s `Result<T>`. Any errors thrown from inside the `task` will be reported as `.failure(CoreStoreError)`. To cancel/rollback changes, call `try transaction.cancel()`, which throws a `CoreStoreError.userCancelled`.
|
||||||
|
|
||||||
- parameter task: the asynchronous closure 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`.
|
- parameter task: the asynchronous closure 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`.
|
||||||
|
- parameter sourceIdentifier: an optional value that identifies the source of this transaction. This identifier will be passed to the change notifications and callers can use it for custom handling that depends on the source.
|
||||||
- parameter completion: the closure executed after the save completes. The `Result` argument of the closure will either wrap the return value of `task`, or any uncaught errors thrown from within `task`. Cancelled `task`s will be indicated by `.failure(error: CoreStoreError.userCancelled)`. Custom errors thrown by the user will be wrapped in `CoreStoreError.userError(error: Error)`.
|
- parameter completion: the closure executed after the save completes. The `Result` argument of the closure will either wrap the return value of `task`, or any uncaught errors thrown from within `task`. Cancelled `task`s will be indicated by `.failure(error: CoreStoreError.userCancelled)`. Custom errors thrown by the user will be wrapped in `CoreStoreError.userError(error: Error)`.
|
||||||
*/
|
*/
|
||||||
public func perform<T>(asynchronous task: @escaping (_ transaction: AsynchronousDataTransaction) throws -> T, completion: @escaping (AsynchronousDataTransaction.Result<T>) -> Void) {
|
public func perform<T>(
|
||||||
|
asynchronous task: @escaping (_ transaction: AsynchronousDataTransaction) throws -> T,
|
||||||
|
sourceIdentifier: Any? = nil,
|
||||||
|
completion: @escaping (AsynchronousDataTransaction.Result<T>) -> Void
|
||||||
|
) {
|
||||||
|
|
||||||
self.perform(
|
self.perform(
|
||||||
asynchronous: task,
|
asynchronous: task,
|
||||||
|
sourceIdentifier: sourceIdentifier,
|
||||||
success: { completion(.success($0)) },
|
success: { completion(.success($0)) },
|
||||||
failure: { completion(.failure($0)) }
|
failure: { completion(.failure($0)) }
|
||||||
)
|
)
|
||||||
@@ -50,14 +56,21 @@ extension DataStack {
|
|||||||
Performs a transaction asynchronously where `NSManagedObject` or `CoreStoreObject` creates, updates, and deletes can be made. The changes are commited automatically after the `task` closure returns. On success, the value returned from closure will be the argument of the `success` closure. Any errors thrown from inside the `task` will be wrapped in a `CoreStoreError` and reported in the `failure` closure. To cancel/rollback changes, call `try transaction.cancel()`, which throws a `CoreStoreError.userCancelled`.
|
Performs a transaction asynchronously where `NSManagedObject` or `CoreStoreObject` creates, updates, and deletes can be made. The changes are commited automatically after the `task` closure returns. On success, the value returned from closure will be the argument of the `success` closure. Any errors thrown from inside the `task` will be wrapped in a `CoreStoreError` and reported in the `failure` closure. To cancel/rollback changes, call `try transaction.cancel()`, which throws a `CoreStoreError.userCancelled`.
|
||||||
|
|
||||||
- parameter task: the asynchronous closure 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`.
|
- parameter task: the asynchronous closure 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`.
|
||||||
|
- parameter sourceIdentifier: an optional value that identifies the source of this transaction. This identifier will be passed to the change notifications and callers can use it for custom handling that depends on the source.
|
||||||
- parameter success: the closure executed after the save succeeds. The `T` argument of the closure will be the value returned from `task`.
|
- parameter success: the closure executed after the save succeeds. The `T` argument of the closure will be the value returned from `task`.
|
||||||
- parameter failure: the closure executed if the save fails or if any errors are thrown within `task`. Cancelled `task`s will be indicated by `CoreStoreError.userCancelled`. Custom errors thrown by the user will be wrapped in `CoreStoreError.userError(error: Error)`.
|
- parameter failure: the closure executed if the save fails or if any errors are thrown within `task`. Cancelled `task`s will be indicated by `CoreStoreError.userCancelled`. Custom errors thrown by the user will be wrapped in `CoreStoreError.userError(error: Error)`.
|
||||||
*/
|
*/
|
||||||
public func perform<T>(asynchronous task: @escaping (_ transaction: AsynchronousDataTransaction) throws -> T, success: @escaping (T) -> Void, failure: @escaping (CoreStoreError) -> Void) {
|
public func perform<T>(
|
||||||
|
asynchronous task: @escaping (_ transaction: AsynchronousDataTransaction) throws -> T,
|
||||||
|
sourceIdentifier: Any? = nil,
|
||||||
|
success: @escaping (T) -> Void,
|
||||||
|
failure: @escaping (CoreStoreError) -> Void
|
||||||
|
) {
|
||||||
|
|
||||||
let transaction = AsynchronousDataTransaction(
|
let transaction = AsynchronousDataTransaction(
|
||||||
mainContext: self.rootSavingContext,
|
mainContext: self.rootSavingContext,
|
||||||
queue: self.childTransactionQueue
|
queue: self.childTransactionQueue,
|
||||||
|
sourceIdentifier: sourceIdentifier
|
||||||
)
|
)
|
||||||
transaction.transactionQueue.cs_async {
|
transaction.transactionQueue.cs_async {
|
||||||
|
|
||||||
@@ -99,14 +112,20 @@ extension DataStack {
|
|||||||
|
|
||||||
- parameter task: the synchronous non-escaping closure 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`.
|
- parameter task: the synchronous non-escaping closure 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`.
|
||||||
- parameter waitForAllObservers: When `true`, this method waits for all observers to be notified of the changes before returning. This results in more predictable data update order, but may risk triggering deadlocks. When `false`, this method does not wait for observers to be notified of the changes before returning. This results in lower risk for deadlocks, but the updated data may not have been propagated to the `DataStack` after returning. Defaults to `true`.
|
- parameter waitForAllObservers: When `true`, this method waits for all observers to be notified of the changes before returning. This results in more predictable data update order, but may risk triggering deadlocks. When `false`, this method does not wait for observers to be notified of the changes before returning. This results in lower risk for deadlocks, but the updated data may not have been propagated to the `DataStack` after returning. Defaults to `true`.
|
||||||
|
- parameter sourceIdentifier: an optional value that identifies the source of this transaction. This identifier will be passed to the change notifications and callers can use it for custom handling that depends on the source.
|
||||||
- throws: a `CoreStoreError` value indicating the failure. Cancelled `task`s will be indicated by `CoreStoreError.userCancelled`. Custom errors thrown by the user will be wrapped in `CoreStoreError.userError(error: Error)`.
|
- throws: a `CoreStoreError` value indicating the failure. Cancelled `task`s will be indicated by `CoreStoreError.userCancelled`. Custom errors thrown by the user will be wrapped in `CoreStoreError.userError(error: Error)`.
|
||||||
- returns: the value returned from `task`
|
- returns: the value returned from `task`
|
||||||
*/
|
*/
|
||||||
public func perform<T>(synchronous task: ((_ transaction: SynchronousDataTransaction) throws -> T), waitForAllObservers: Bool = true) throws -> T {
|
public func perform<T>(
|
||||||
|
synchronous task: ((_ transaction: SynchronousDataTransaction) throws -> T),
|
||||||
|
waitForAllObservers: Bool = true,
|
||||||
|
sourceIdentifier: Any? = nil
|
||||||
|
) throws -> T {
|
||||||
|
|
||||||
let transaction = SynchronousDataTransaction(
|
let transaction = SynchronousDataTransaction(
|
||||||
mainContext: self.rootSavingContext,
|
mainContext: self.rootSavingContext,
|
||||||
queue: self.childTransactionQueue
|
queue: self.childTransactionQueue,
|
||||||
|
sourceIdentifier: sourceIdentifier
|
||||||
)
|
)
|
||||||
return try transaction.transactionQueue.cs_sync {
|
return try transaction.transactionQueue.cs_sync {
|
||||||
|
|
||||||
@@ -141,15 +160,20 @@ extension DataStack {
|
|||||||
/**
|
/**
|
||||||
Begins a non-contiguous transaction where `NSManagedObject` or `CoreStoreObject` creates, updates, and deletes can be made. This is useful for making temporary changes, such as partially filled forms.
|
Begins a non-contiguous transaction where `NSManagedObject` or `CoreStoreObject` 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.
|
- parameter 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.
|
||||||
|
- parameter sourceIdentifier: an optional value that identifies the source of this transaction. This identifier will be passed to the change notifications and callers can use it for custom handling that depends on the source.
|
||||||
- returns: a `UnsafeDataTransaction` instance where creates, updates, and deletes can be made.
|
- returns: a `UnsafeDataTransaction` instance where creates, updates, and deletes can be made.
|
||||||
*/
|
*/
|
||||||
public func beginUnsafe(supportsUndo: Bool = false) -> UnsafeDataTransaction {
|
public func beginUnsafe(
|
||||||
|
supportsUndo: Bool = false,
|
||||||
|
sourceIdentifier: Any? = nil
|
||||||
|
) -> UnsafeDataTransaction {
|
||||||
|
|
||||||
return UnsafeDataTransaction(
|
return UnsafeDataTransaction(
|
||||||
mainContext: self.rootSavingContext,
|
mainContext: self.rootSavingContext,
|
||||||
queue: DispatchQueue.serial("com.coreStore.dataStack.unsafeTransactionQueue", qos: .userInitiated),
|
queue: DispatchQueue.serial("com.coreStore.dataStack.unsafeTransactionQueue", qos: .userInitiated),
|
||||||
supportsUndo: supportsUndo
|
supportsUndo: supportsUndo,
|
||||||
|
sourceIdentifier: sourceIdentifier
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -116,10 +116,10 @@ extension DiffableDataSource {
|
|||||||
target.reload(
|
target.reload(
|
||||||
using: changeset,
|
using: changeset,
|
||||||
animated: animatingDifferences,
|
animated: animatingDifferences,
|
||||||
setData: setSections
|
setData: setSections,
|
||||||
|
completion: completion
|
||||||
)
|
)
|
||||||
},
|
}
|
||||||
completion: completion
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,10 +148,21 @@ extension DiffableDataSource {
|
|||||||
target.reload(
|
target.reload(
|
||||||
using: changeset,
|
using: changeset,
|
||||||
animated: animatingDifferences,
|
animated: animatingDifferences,
|
||||||
setData: setSections
|
setData: setSections,
|
||||||
|
completion: completion
|
||||||
)
|
)
|
||||||
},
|
}
|
||||||
completion: completion
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Creates a new empty `ListSnapshot` suitable for building custom lists inside subclass implementations of `apply(_:animatingDifferences:completion:)`.
|
||||||
|
*/
|
||||||
|
public func makeEmptySnapshot() -> ListSnapshot<O> {
|
||||||
|
|
||||||
|
return .init(
|
||||||
|
diffableSnapshot: .init(),
|
||||||
|
context: self.dataStack.unsafeContext()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -83,7 +83,12 @@ extension DiffableDataSource {
|
|||||||
- parameter itemProvider: a closure that configures and returns the `NSCollectionViewItem` for the object
|
- parameter itemProvider: a closure that configures and returns the `NSCollectionViewItem` for the object
|
||||||
*/
|
*/
|
||||||
@nonobjc
|
@nonobjc
|
||||||
public init(collectionView: NSCollectionView, dataStack: DataStack, itemProvider: @escaping (NSCollectionView, IndexPath, O) -> NSCollectionViewItem?, supplementaryViewProvider: @escaping (NSCollectionView, String, IndexPath) -> NSView? = { _, _, _ in nil }) {
|
public init(
|
||||||
|
collectionView: NSCollectionView,
|
||||||
|
dataStack: DataStack,
|
||||||
|
itemProvider: @escaping (NSCollectionView, IndexPath, O) -> NSCollectionViewItem?,
|
||||||
|
supplementaryViewProvider: @escaping (NSCollectionView, String, IndexPath) -> NSView? = { _, _, _ in nil }
|
||||||
|
) {
|
||||||
|
|
||||||
self.itemProvider = itemProvider
|
self.itemProvider = itemProvider
|
||||||
self.supplementaryViewProvider = supplementaryViewProvider
|
self.supplementaryViewProvider = supplementaryViewProvider
|
||||||
@@ -97,19 +102,27 @@ extension DiffableDataSource {
|
|||||||
// MARK: - NSCollectionViewDataSource
|
// MARK: - NSCollectionViewDataSource
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
public dynamic func numberOfSections(in collectionView: NSCollectionView) -> Int {
|
public dynamic func numberOfSections(
|
||||||
|
in collectionView: NSCollectionView
|
||||||
|
) -> Int {
|
||||||
|
|
||||||
return self.numberOfSections()
|
return self.numberOfSections()
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
public dynamic func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int {
|
public dynamic func collectionView(
|
||||||
|
_ collectionView: NSCollectionView,
|
||||||
|
numberOfItemsInSection section: Int
|
||||||
|
) -> Int {
|
||||||
|
|
||||||
return self.numberOfItems(inSection: section) ?? 0
|
return self.numberOfItems(inSection: section) ?? 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
open dynamic func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
|
open dynamic func collectionView(
|
||||||
|
_ collectionView: NSCollectionView,
|
||||||
|
itemForRepresentedObjectAt indexPath: IndexPath
|
||||||
|
) -> NSCollectionViewItem {
|
||||||
|
|
||||||
guard let objectID = self.itemID(for: indexPath) else {
|
guard let objectID = self.itemID(for: indexPath) else {
|
||||||
|
|
||||||
@@ -127,7 +140,11 @@ extension DiffableDataSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
open dynamic func collectionView(_ collectionView: NSCollectionView, viewForSupplementaryElementOfKind kind: NSCollectionView.SupplementaryElementKind, at indexPath: IndexPath) -> NSView {
|
open dynamic func collectionView(
|
||||||
|
_ collectionView: NSCollectionView,
|
||||||
|
viewForSupplementaryElementOfKind kind: NSCollectionView.SupplementaryElementKind,
|
||||||
|
at indexPath: IndexPath
|
||||||
|
) -> NSView {
|
||||||
|
|
||||||
guard let view = self.supplementaryViewProvider(collectionView, kind, indexPath) else {
|
guard let view = self.supplementaryViewProvider(collectionView, kind, indexPath) else {
|
||||||
|
|
||||||
@@ -207,9 +224,9 @@ extension DiffableDataSource {
|
|||||||
self.base?.moveItem(at: indexPath, to: newIndexPath)
|
self.base?.moveItem(at: indexPath, to: newIndexPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func performBatchUpdates(updates: () -> Void, animated: Bool) {
|
public func performBatchUpdates(updates: () -> Void, animated: Bool, completion: @escaping () -> Void) {
|
||||||
|
|
||||||
self.base?.animator().performBatchUpdates(updates, completionHandler: nil)
|
self.base?.animator().performBatchUpdates(updates, completionHandler: { _ in completion() })
|
||||||
}
|
}
|
||||||
|
|
||||||
public func reloadData() {
|
public func reloadData() {
|
||||||
|
|||||||
@@ -83,7 +83,12 @@ extension DiffableDataSource {
|
|||||||
- parameter cellProvider: a closure that configures and returns the `UICollectionViewCell` for the object
|
- parameter cellProvider: a closure that configures and returns the `UICollectionViewCell` for the object
|
||||||
- parameter supplementaryViewProvider: an optional closure for providing `UICollectionReusableView` supplementary views. If not set, defaults to returning `nil`
|
- parameter supplementaryViewProvider: an optional closure for providing `UICollectionReusableView` supplementary views. If not set, defaults to returning `nil`
|
||||||
*/
|
*/
|
||||||
public init(collectionView: UICollectionView, dataStack: DataStack, cellProvider: @escaping (UICollectionView, IndexPath, O) -> UICollectionViewCell?, supplementaryViewProvider: @escaping (UICollectionView, String, IndexPath) -> UICollectionReusableView? = { _, _, _ in nil }) {
|
public init(
|
||||||
|
collectionView: UICollectionView,
|
||||||
|
dataStack: DataStack,
|
||||||
|
cellProvider: @escaping (UICollectionView, IndexPath, O) -> UICollectionViewCell?,
|
||||||
|
supplementaryViewProvider: @escaping (UICollectionView, String, IndexPath) -> UICollectionReusableView? = { _, _, _ in nil }
|
||||||
|
) {
|
||||||
|
|
||||||
self.cellProvider = cellProvider
|
self.cellProvider = cellProvider
|
||||||
self.supplementaryViewProvider = supplementaryViewProvider
|
self.supplementaryViewProvider = supplementaryViewProvider
|
||||||
@@ -97,19 +102,30 @@ extension DiffableDataSource {
|
|||||||
// MARK: - UICollectionViewDataSource
|
// MARK: - UICollectionViewDataSource
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
public dynamic func numberOfSections(in collectionView: UICollectionView) -> Int {
|
@MainActor
|
||||||
|
public dynamic func numberOfSections(
|
||||||
|
in collectionView: UICollectionView
|
||||||
|
) -> Int {
|
||||||
|
|
||||||
return self.numberOfSections()
|
return self.numberOfSections()
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
public dynamic func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
@MainActor
|
||||||
|
public dynamic func collectionView(
|
||||||
|
_ collectionView: UICollectionView,
|
||||||
|
numberOfItemsInSection section: Int
|
||||||
|
) -> Int {
|
||||||
|
|
||||||
return self.numberOfItems(inSection: section) ?? 0
|
return self.numberOfItems(inSection: section) ?? 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
open dynamic func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
@MainActor
|
||||||
|
open dynamic func collectionView(
|
||||||
|
_ collectionView: UICollectionView,
|
||||||
|
cellForItemAt indexPath: IndexPath
|
||||||
|
) -> UICollectionViewCell {
|
||||||
|
|
||||||
guard let objectID = self.itemID(for: indexPath) else {
|
guard let objectID = self.itemID(for: indexPath) else {
|
||||||
|
|
||||||
@@ -127,7 +143,12 @@ extension DiffableDataSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
open dynamic func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
|
@MainActor
|
||||||
|
open dynamic func collectionView(
|
||||||
|
_ collectionView: UICollectionView,
|
||||||
|
viewForSupplementaryElementOfKind kind: String,
|
||||||
|
at indexPath: IndexPath
|
||||||
|
) -> UICollectionReusableView {
|
||||||
|
|
||||||
guard let view = self.supplementaryViewProvider(collectionView, kind, indexPath) else {
|
guard let view = self.supplementaryViewProvider(collectionView, kind, indexPath) else {
|
||||||
|
|
||||||
@@ -207,9 +228,9 @@ extension DiffableDataSource {
|
|||||||
self.base?.moveItem(at: indexPath, to: newIndexPath)
|
self.base?.moveItem(at: indexPath, to: newIndexPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func performBatchUpdates(updates: () -> Void, animated: Bool) {
|
public func performBatchUpdates(updates: () -> Void, animated: Bool, completion: @escaping () -> Void) {
|
||||||
|
|
||||||
self.base?.performBatchUpdates(updates, completion: nil)
|
self.base?.performBatchUpdates(updates, completion: { _ in completion() })
|
||||||
}
|
}
|
||||||
|
|
||||||
public func reloadData() {
|
public func reloadData() {
|
||||||
|
|||||||
@@ -82,7 +82,11 @@ extension DiffableDataSource {
|
|||||||
- parameter dataStack: the `DataStack` instance that the dataSource will fetch objects from
|
- parameter dataStack: the `DataStack` instance that the dataSource will fetch objects from
|
||||||
- parameter cellProvider: a closure that configures and returns the `UITableViewCell` for the object
|
- parameter cellProvider: a closure that configures and returns the `UITableViewCell` for the object
|
||||||
*/
|
*/
|
||||||
public init(tableView: UITableView, dataStack: DataStack, cellProvider: @escaping (UITableView, IndexPath, O) -> UITableViewCell?) {
|
public init(
|
||||||
|
tableView: UITableView,
|
||||||
|
dataStack: DataStack,
|
||||||
|
cellProvider: @escaping (UITableView, IndexPath, O) -> UITableViewCell?
|
||||||
|
) {
|
||||||
|
|
||||||
self.cellProvider = cellProvider
|
self.cellProvider = cellProvider
|
||||||
super.init(target: .init(tableView), dataStack: dataStack)
|
super.init(target: .init(tableView), dataStack: dataStack)
|
||||||
@@ -102,31 +106,48 @@ extension DiffableDataSource {
|
|||||||
// MARK: - UITableViewDataSource
|
// MARK: - UITableViewDataSource
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
|
@MainActor
|
||||||
public dynamic func numberOfSections(in tableView: UITableView) -> Int {
|
public dynamic func numberOfSections(in tableView: UITableView) -> Int {
|
||||||
|
|
||||||
return self.numberOfSections()
|
return self.numberOfSections()
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
public dynamic func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
@MainActor
|
||||||
|
public dynamic func tableView(
|
||||||
|
_ tableView: UITableView,
|
||||||
|
numberOfRowsInSection section: Int
|
||||||
|
) -> Int {
|
||||||
|
|
||||||
return self.numberOfItems(inSection: section) ?? 0
|
return self.numberOfItems(inSection: section) ?? 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
open dynamic func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
@MainActor
|
||||||
|
open dynamic func tableView(
|
||||||
|
_ tableView: UITableView,
|
||||||
|
titleForHeaderInSection section: Int
|
||||||
|
) -> String? {
|
||||||
|
|
||||||
return self.sectionID(for: section)
|
return self.sectionID(for: section)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
open dynamic func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
|
@MainActor
|
||||||
|
open dynamic func tableView(
|
||||||
|
_ tableView: UITableView,
|
||||||
|
titleForFooterInSection section: Int
|
||||||
|
) -> String? {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
open dynamic func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
@MainActor
|
||||||
|
open dynamic func tableView(
|
||||||
|
_ tableView: UITableView,
|
||||||
|
cellForRowAt indexPath: IndexPath
|
||||||
|
) -> UITableViewCell {
|
||||||
|
|
||||||
guard let objectID = self.itemID(for: indexPath) else {
|
guard let objectID = self.itemID(for: indexPath) else {
|
||||||
|
|
||||||
@@ -144,28 +165,49 @@ extension DiffableDataSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
open dynamic func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
|
@MainActor
|
||||||
|
open dynamic func tableView(
|
||||||
|
_ tableView: UITableView,
|
||||||
|
canEditRowAt indexPath: IndexPath
|
||||||
|
) -> Bool {
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
open dynamic func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle {
|
@MainActor
|
||||||
|
open dynamic func tableView(
|
||||||
|
_ tableView: UITableView,
|
||||||
|
editingStyleForRowAt indexPath: IndexPath
|
||||||
|
) -> UITableViewCell.EditingStyle {
|
||||||
|
|
||||||
return .delete
|
return .delete
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
open dynamic func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {}
|
@MainActor
|
||||||
|
open dynamic func tableView(
|
||||||
|
_ tableView: UITableView,
|
||||||
|
commit editingStyle: UITableViewCell.EditingStyle,
|
||||||
|
forRowAt indexPath: IndexPath
|
||||||
|
) {}
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
open dynamic func sectionIndexTitles(for tableView: UITableView) -> [String]? {
|
@MainActor
|
||||||
|
open dynamic func sectionIndexTitles(
|
||||||
|
for tableView: UITableView
|
||||||
|
) -> [String]? {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
open dynamic func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int {
|
@MainActor
|
||||||
|
open dynamic func tableView(
|
||||||
|
_ tableView: UITableView,
|
||||||
|
sectionForSectionIndexTitle title: String,
|
||||||
|
at index: Int
|
||||||
|
) -> Int {
|
||||||
|
|
||||||
return index
|
return index
|
||||||
}
|
}
|
||||||
@@ -241,13 +283,13 @@ extension DiffableDataSource {
|
|||||||
self.base?.moveRow(at: indexPath, to: newIndexPath)
|
self.base?.moveRow(at: indexPath, to: newIndexPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func performBatchUpdates(updates: () -> Void, animated: Bool) {
|
public func performBatchUpdates(updates: () -> Void, animated: Bool, completion: @escaping () -> Void) {
|
||||||
|
|
||||||
guard let base = self.base else {
|
guard let base = self.base else {
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
base.performBatchUpdates(updates)
|
base.performBatchUpdates(updates, completion: { _ in completion() })
|
||||||
}
|
}
|
||||||
|
|
||||||
public func reloadData() {
|
public func reloadData() {
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ public protocol DiffableDataSourceTarget {
|
|||||||
/**
|
/**
|
||||||
Animates multiple insert, delete, reload, and move operations as a group.
|
Animates multiple insert, delete, reload, and move operations as a group.
|
||||||
*/
|
*/
|
||||||
func performBatchUpdates(updates: () -> Void, animated: Bool)
|
func performBatchUpdates(updates: () -> Void, animated: Bool, completion: @escaping () -> Void)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Reloads all sections and items.
|
Reloads all sections and items.
|
||||||
@@ -114,9 +114,15 @@ extension DiffableDataSource.Target {
|
|||||||
using stagedChangeset: Internals.DiffableDataUIDispatcher<O>.StagedChangeset<C>,
|
using stagedChangeset: Internals.DiffableDataUIDispatcher<O>.StagedChangeset<C>,
|
||||||
animated: Bool,
|
animated: Bool,
|
||||||
interrupt: ((Internals.DiffableDataUIDispatcher<O>.Changeset<C>) -> Bool)? = nil,
|
interrupt: ((Internals.DiffableDataUIDispatcher<O>.Changeset<C>) -> Bool)? = nil,
|
||||||
setData: (C) -> Void
|
setData: (C) -> Void,
|
||||||
|
completion: @escaping () -> Void
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
let group = DispatchGroup()
|
||||||
|
defer {
|
||||||
|
|
||||||
|
group.notify(queue: .main, execute: completion)
|
||||||
|
}
|
||||||
if self.shouldSuspendBatchUpdates, let data = stagedChangeset.last?.data {
|
if self.shouldSuspendBatchUpdates, let data = stagedChangeset.last?.data {
|
||||||
|
|
||||||
setData(data)
|
setData(data)
|
||||||
@@ -133,6 +139,7 @@ extension DiffableDataSource.Target {
|
|||||||
self.reloadData()
|
self.reloadData()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
group.enter()
|
||||||
self.performBatchUpdates(
|
self.performBatchUpdates(
|
||||||
updates: {
|
updates: {
|
||||||
|
|
||||||
@@ -206,7 +213,8 @@ extension DiffableDataSource.Target {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
animated: animated
|
animated: animated,
|
||||||
|
completion: group.leave
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,19 +44,30 @@ internal protocol DiffableDataSourceSnapshotProtocol {
|
|||||||
func indexOfItem(_ identifier: NSManagedObjectID) -> Int?
|
func indexOfItem(_ identifier: NSManagedObjectID) -> Int?
|
||||||
func indexOfSection(_ identifier: String) -> Int?
|
func indexOfSection(_ identifier: String) -> Int?
|
||||||
|
|
||||||
mutating func appendItems(_ identifiers: [NSManagedObjectID], toSection sectionIdentifier: String?)
|
mutating func appendItems<C: Collection>(_ identifiers: C, toSection sectionIdentifier: String?) where C.Element == NSManagedObjectID
|
||||||
mutating func insertItems(_ identifiers: [NSManagedObjectID], beforeItem beforeIdentifier: NSManagedObjectID)
|
mutating func insertItems<C: Collection>(_ identifiers: C, beforeItem beforeIdentifier: NSManagedObjectID) where C.Element == NSManagedObjectID
|
||||||
mutating func insertItems(_ identifiers: [NSManagedObjectID], afterItem afterIdentifier: NSManagedObjectID)
|
mutating func insertItems<C: Collection>(_ identifiers: C, afterItem afterIdentifier: NSManagedObjectID) where C.Element == NSManagedObjectID
|
||||||
mutating func deleteItems(_ identifiers: [NSManagedObjectID])
|
mutating func deleteItems<C: Collection>(_ identifiers: C) where C.Element == NSManagedObjectID
|
||||||
mutating func deleteAllItems()
|
mutating func deleteAllItems()
|
||||||
mutating func moveItem(_ identifier: NSManagedObjectID, beforeItem toIdentifier: NSManagedObjectID)
|
mutating func moveItem(_ identifier: NSManagedObjectID, beforeItem toIdentifier: NSManagedObjectID)
|
||||||
mutating func moveItem(_ identifier: NSManagedObjectID, afterItem toIdentifier: NSManagedObjectID)
|
mutating func moveItem(_ identifier: NSManagedObjectID, afterItem toIdentifier: NSManagedObjectID)
|
||||||
mutating func reloadItems(_ identifiers: [NSManagedObjectID])
|
mutating func reloadItems<C: Collection>(_ identifiers: C) where C.Element == NSManagedObjectID
|
||||||
mutating func appendSections(_ identifiers: [String])
|
mutating func appendSections<C: Collection>(_ identifiers: C) where C.Element == String
|
||||||
mutating func insertSections(_ identifiers: [String], beforeSection toIdentifier: String)
|
mutating func insertSections<C: Collection>(_ identifiers: C, beforeSection toIdentifier: String) where C.Element == String
|
||||||
mutating func insertSections(_ identifiers: [String], afterSection toIdentifier: String)
|
mutating func insertSections<C: Collection>(_ identifiers: C, afterSection toIdentifier: String) where C.Element == String
|
||||||
mutating func deleteSections(_ identifiers: [String])
|
mutating func deleteSections<C: Collection>(_ identifiers: C) where C.Element == String
|
||||||
mutating func moveSection(_ identifier: String, beforeSection toIdentifier: String)
|
mutating func moveSection(_ identifier: String, beforeSection toIdentifier: String)
|
||||||
mutating func moveSection(_ identifier: String, afterSection toIdentifier: String)
|
mutating func moveSection(_ identifier: String, afterSection toIdentifier: String)
|
||||||
mutating func reloadSections(_ identifiers: [String])
|
mutating func reloadSections<C: Collection>(_ identifiers: C) where C.Element == String
|
||||||
|
|
||||||
|
mutating func unsafeAppendItems<C: Collection>(_ identifiers: C, toSectionAt sectionIndex: Int) where C.Element == NSManagedObjectID
|
||||||
|
mutating func unsafeInsertItems<C: Collection>(_ identifiers: C, at indexPath: IndexPath) where C.Element == NSManagedObjectID
|
||||||
|
mutating func unsafeDeleteItems<C: Collection>(at indexPaths: C) where C.Element == IndexPath
|
||||||
|
mutating func unsafeMoveItem(at indexPath: IndexPath, to newIndexPath: IndexPath)
|
||||||
|
mutating func unsafeReloadItems<C: Collection>(at indexPaths: C) where C.Element == IndexPath
|
||||||
|
mutating func unsafeInsertSections<C: Collection>(_ identifiers: C, at sectionIndex: Int) where C.Element == String
|
||||||
|
mutating func unsafeDeleteSections<C: Collection>(at sectionIndices: C) where C.Element == Int
|
||||||
|
mutating func unsafeMoveSection(at sectionIndex: Int, to newSectionIndex: Int)
|
||||||
|
mutating func unsafeReloadSections<C: Collection>(at sectionIndices: C) where C.Element == Int
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,10 @@ extension Internals {
|
|||||||
|
|
||||||
internal final class Closure<T, U> {
|
internal final class Closure<T, U> {
|
||||||
|
|
||||||
// MARK: FilePrivate
|
// MARK: Internal
|
||||||
|
|
||||||
|
internal typealias Arguments = T
|
||||||
|
internal typealias Result = U
|
||||||
|
|
||||||
internal init(_ closure: @escaping (T) -> U) {
|
internal init(_ closure: @escaping (T) -> U) {
|
||||||
|
|
||||||
|
|||||||
@@ -200,7 +200,7 @@ extension Internals {
|
|||||||
self.structure.insert(itemIDs: identifiers, after: afterIdentifier)
|
self.structure.insert(itemIDs: identifiers, after: afterIdentifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
mutating func deleteItems<S: Sequence>(_ identifiers: S) where S.Element == NSManagedObjectID {
|
mutating func deleteItems<C: Collection>(_ identifiers: C) where C.Element == NSManagedObjectID {
|
||||||
|
|
||||||
self.structure.remove(itemIDs: identifiers)
|
self.structure.remove(itemIDs: identifiers)
|
||||||
}
|
}
|
||||||
@@ -220,7 +220,7 @@ extension Internals {
|
|||||||
self.structure.move(itemID: identifier, after: toIdentifier)
|
self.structure.move(itemID: identifier, after: toIdentifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
mutating func reloadItems<S: Sequence>(_ identifiers: S) where S.Element == NSManagedObjectID {
|
mutating func reloadItems<C: Collection>(_ identifiers: C) where C.Element == NSManagedObjectID {
|
||||||
|
|
||||||
self.structure.update(itemIDs: identifiers)
|
self.structure.update(itemIDs: identifiers)
|
||||||
}
|
}
|
||||||
@@ -240,7 +240,7 @@ extension Internals {
|
|||||||
self.structure.insert(sectionIDs: identifiers, after: toIdentifier)
|
self.structure.insert(sectionIDs: identifiers, after: toIdentifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
mutating func deleteSections<S: Sequence>(_ identifiers: S) where S.Element == String {
|
mutating func deleteSections<C: Collection>(_ identifiers: C) where C.Element == String {
|
||||||
|
|
||||||
self.structure.remove(sectionIDs: identifiers)
|
self.structure.remove(sectionIDs: identifiers)
|
||||||
}
|
}
|
||||||
@@ -255,10 +255,55 @@ extension Internals {
|
|||||||
self.structure.move(sectionID: identifier, after: toIdentifier)
|
self.structure.move(sectionID: identifier, after: toIdentifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
mutating func reloadSections<S: Sequence>(_ identifiers: S) where S.Element == String {
|
mutating func reloadSections<C: Collection>(_ identifiers: C) where C.Element == String {
|
||||||
|
|
||||||
self.structure.update(sectionIDs: identifiers)
|
self.structure.update(sectionIDs: identifiers)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mutating func unsafeAppendItems<C: Collection>(_ identifiers: C, toSectionAt sectionIndex: Int) where C.Element == NSManagedObjectID {
|
||||||
|
|
||||||
|
self.structure.unsafeAppend(identifiers, toSectionAt: sectionIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
mutating func unsafeInsertItems<C: Collection>(_ identifiers: C, at indexPath: IndexPath) where C.Element == NSManagedObjectID {
|
||||||
|
|
||||||
|
self.structure.unsafeInsert(itemIDs: identifiers, at: indexPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
mutating func unsafeDeleteItems<C: Collection>(at indexPaths: C) where C.Element == IndexPath {
|
||||||
|
|
||||||
|
self.structure.unsafeRemove(itemsAt: indexPaths)
|
||||||
|
}
|
||||||
|
|
||||||
|
mutating func unsafeMoveItem(at indexPath: IndexPath, to newIndexPath: IndexPath) {
|
||||||
|
|
||||||
|
self.structure.unsafeMove(itemAt: indexPath, to: newIndexPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
mutating func unsafeReloadItems<C: Collection>(at indexPaths: C) where C.Element == IndexPath {
|
||||||
|
|
||||||
|
self.structure.unsafeUpdate(itemsAt: indexPaths)
|
||||||
|
}
|
||||||
|
|
||||||
|
mutating func unsafeInsertSections<C: Collection>(_ identifiers: C, at sectionIndex: Int) where C.Element == String {
|
||||||
|
|
||||||
|
self.structure.unsafeInsert(identifiers, at: sectionIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
mutating func unsafeDeleteSections<C: Collection>(at sectionIndices: C) where C.Element == Int {
|
||||||
|
|
||||||
|
self.structure.unsafeRemove(sectionsAt: sectionIndices)
|
||||||
|
}
|
||||||
|
|
||||||
|
mutating func unsafeMoveSection(at sectionIndex: Int, to newSectionIndex: Int) {
|
||||||
|
|
||||||
|
self.structure.unsafeMove(sectionAt: sectionIndex, to: newSectionIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
mutating func unsafeReloadSections<C: Collection>(at sectionIndices: C) where C.Element == Int {
|
||||||
|
|
||||||
|
self.structure.unsafeUpdate(sectionsAt: sectionIndices)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// MARK: Private
|
// MARK: Private
|
||||||
@@ -432,13 +477,23 @@ extension Internals {
|
|||||||
}
|
}
|
||||||
return self.sections[sectionIndex].elements.map({ $0.differenceIdentifier })
|
return self.sections[sectionIndex].elements.map({ $0.differenceIdentifier })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func unsafeItem(at indexPath: IndexPath) -> NSManagedObjectID {
|
||||||
|
|
||||||
|
return self.sections[indexPath.section]
|
||||||
|
.elements[indexPath.item]
|
||||||
|
.differenceIdentifier
|
||||||
|
}
|
||||||
|
|
||||||
func section(containing itemID: NSManagedObjectID) -> String? {
|
func section(containing itemID: NSManagedObjectID) -> String? {
|
||||||
|
|
||||||
return self.itemPositionMap(itemID)?.section.differenceIdentifier
|
return self.itemPositionMap(itemID)?.section.differenceIdentifier
|
||||||
}
|
}
|
||||||
|
|
||||||
mutating func append<C: Collection>(itemIDs: C, to sectionID: String?) where C.Element == NSManagedObjectID {
|
mutating func append<C: Collection>(
|
||||||
|
itemIDs: C,
|
||||||
|
to sectionID: String?
|
||||||
|
) where C.Element == NSManagedObjectID {
|
||||||
|
|
||||||
let index: Array<Section>.Index
|
let index: Array<Section>.Index
|
||||||
if let sectionID = sectionID {
|
if let sectionID = sectionID {
|
||||||
@@ -461,8 +516,30 @@ extension Internals {
|
|||||||
let items = itemIDs.lazy.map({ Item(differenceIdentifier: $0) })
|
let items = itemIDs.lazy.map({ Item(differenceIdentifier: $0) })
|
||||||
self.sections[index].elements.append(contentsOf: items)
|
self.sections[index].elements.append(contentsOf: items)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mutating func unsafeAppend<C: Collection>(
|
||||||
|
_ itemIDs: C,
|
||||||
|
toSectionAt sectionIndex: Int?
|
||||||
|
) where C.Element == NSManagedObjectID {
|
||||||
|
|
||||||
|
let index: Array<Section>.Index
|
||||||
|
if let sectionIndex = sectionIndex {
|
||||||
|
|
||||||
mutating func insert<C: Collection>(itemIDs: C, before beforeItemID: NSManagedObjectID) where C.Element == NSManagedObjectID {
|
index = sectionIndex
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
let section = self.sections
|
||||||
|
index = section.index(before: section.endIndex)
|
||||||
|
}
|
||||||
|
let items = itemIDs.lazy.map({ Item(differenceIdentifier: $0) })
|
||||||
|
self.sections[index].elements.append(contentsOf: items)
|
||||||
|
}
|
||||||
|
|
||||||
|
mutating func insert<C: Collection>(
|
||||||
|
itemIDs: C,
|
||||||
|
before beforeItemID: NSManagedObjectID
|
||||||
|
) where C.Element == NSManagedObjectID {
|
||||||
|
|
||||||
guard let itemPosition = self.itemPositionMap(beforeItemID) else {
|
guard let itemPosition = self.itemPositionMap(beforeItemID) else {
|
||||||
|
|
||||||
@@ -473,7 +550,10 @@ extension Internals {
|
|||||||
.insert(contentsOf: items, at: itemPosition.itemRelativeIndex)
|
.insert(contentsOf: items, at: itemPosition.itemRelativeIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
mutating func insert<C: Collection>(itemIDs: C, after afterItemID: NSManagedObjectID) where C.Element == NSManagedObjectID {
|
mutating func insert<C: Collection>(
|
||||||
|
itemIDs: C,
|
||||||
|
after afterItemID: NSManagedObjectID
|
||||||
|
) where C.Element == NSManagedObjectID {
|
||||||
|
|
||||||
guard let itemPosition = self.itemPositionMap(afterItemID) else {
|
guard let itemPosition = self.itemPositionMap(afterItemID) else {
|
||||||
|
|
||||||
@@ -485,6 +565,16 @@ extension Internals {
|
|||||||
self.sections[itemPosition.sectionIndex].elements
|
self.sections[itemPosition.sectionIndex].elements
|
||||||
.insert(contentsOf: items, at: itemIndex)
|
.insert(contentsOf: items, at: itemIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mutating func unsafeInsert<C: Collection>(
|
||||||
|
itemIDs: C,
|
||||||
|
at indexPath: IndexPath
|
||||||
|
) where C.Element == NSManagedObjectID {
|
||||||
|
|
||||||
|
let items = itemIDs.lazy.map({ Item(differenceIdentifier: $0) })
|
||||||
|
self.sections[indexPath.section].elements
|
||||||
|
.insert(contentsOf: items, at: indexPath.item)
|
||||||
|
}
|
||||||
|
|
||||||
mutating func remove<S: Sequence>(itemIDs: S) where S.Element == NSManagedObjectID {
|
mutating func remove<S: Sequence>(itemIDs: S) where S.Element == NSManagedObjectID {
|
||||||
|
|
||||||
@@ -508,6 +598,23 @@ extension Internals {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mutating func unsafeRemove<S: Sequence>(itemsAt indexPaths: S) where S.Element == IndexPath {
|
||||||
|
|
||||||
|
var removeIndexSetMap: [Int: IndexSet] = [:]
|
||||||
|
for indexPath in indexPaths {
|
||||||
|
|
||||||
|
removeIndexSetMap[indexPath.section, default: []]
|
||||||
|
.insert(indexPath.item)
|
||||||
|
}
|
||||||
|
for (sectionIndex, removeIndexSet) in removeIndexSetMap {
|
||||||
|
|
||||||
|
for range in removeIndexSet.rangeView.reversed() {
|
||||||
|
|
||||||
|
self.sections[sectionIndex].elements.removeSubrange(range)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mutating func removeAllItems() {
|
mutating func removeAllItems() {
|
||||||
|
|
||||||
@@ -522,7 +629,10 @@ extension Internals {
|
|||||||
self.sections.removeAll(where: { $0.elements.isEmpty })
|
self.sections.removeAll(where: { $0.elements.isEmpty })
|
||||||
}
|
}
|
||||||
|
|
||||||
mutating func move(itemID: NSManagedObjectID, before beforeItemID: NSManagedObjectID) {
|
mutating func move(
|
||||||
|
itemID: NSManagedObjectID,
|
||||||
|
before beforeItemID: NSManagedObjectID
|
||||||
|
) {
|
||||||
|
|
||||||
guard let removed = self.remove(itemID: itemID) else {
|
guard let removed = self.remove(itemID: itemID) else {
|
||||||
|
|
||||||
@@ -536,7 +646,10 @@ extension Internals {
|
|||||||
.insert(removed, at: itemPosition.itemRelativeIndex)
|
.insert(removed, at: itemPosition.itemRelativeIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
mutating func move(itemID: NSManagedObjectID, after afterItemID: NSManagedObjectID) {
|
mutating func move(
|
||||||
|
itemID: NSManagedObjectID,
|
||||||
|
after afterItemID: NSManagedObjectID
|
||||||
|
) {
|
||||||
|
|
||||||
guard let removed = self.remove(itemID: itemID) else {
|
guard let removed = self.remove(itemID: itemID) else {
|
||||||
|
|
||||||
@@ -551,6 +664,17 @@ extension Internals {
|
|||||||
self.sections[itemPosition.sectionIndex].elements
|
self.sections[itemPosition.sectionIndex].elements
|
||||||
.insert(removed, at: itemIndex)
|
.insert(removed, at: itemIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mutating func unsafeMove(
|
||||||
|
itemAt indexPath: IndexPath,
|
||||||
|
to newIndexPath: IndexPath
|
||||||
|
) {
|
||||||
|
|
||||||
|
let itemID = self.sections[indexPath.section].elements
|
||||||
|
.remove(at: indexPath.item)
|
||||||
|
self.sections[newIndexPath.section].elements
|
||||||
|
.insert(itemID, at: newIndexPath.item)
|
||||||
|
}
|
||||||
|
|
||||||
mutating func update<S: Sequence>(itemIDs: S) where S.Element == NSManagedObjectID {
|
mutating func update<S: Sequence>(itemIDs: S) where S.Element == NSManagedObjectID {
|
||||||
|
|
||||||
@@ -568,6 +692,18 @@ extension Internals {
|
|||||||
}
|
}
|
||||||
self.reloadedItems.formUnion(newItemIDs)
|
self.reloadedItems.formUnion(newItemIDs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mutating func unsafeUpdate<S: Sequence>(itemsAt indexPaths: S) where S.Element == IndexPath {
|
||||||
|
|
||||||
|
var newItemIDs: Set<NSManagedObjectID> = []
|
||||||
|
for indexPath in indexPaths {
|
||||||
|
|
||||||
|
self.sections[indexPath.section]
|
||||||
|
.elements[indexPath.item].isReloaded = true
|
||||||
|
newItemIDs.insert(self.unsafeItem(at: indexPath))
|
||||||
|
}
|
||||||
|
self.reloadedItems.formUnion(newItemIDs)
|
||||||
|
}
|
||||||
|
|
||||||
mutating func append<C: Collection>(sectionIDs: C) where C.Element == String {
|
mutating func append<C: Collection>(sectionIDs: C) where C.Element == String {
|
||||||
|
|
||||||
@@ -582,7 +718,10 @@ extension Internals {
|
|||||||
self.sections.append(contentsOf: newSections)
|
self.sections.append(contentsOf: newSections)
|
||||||
}
|
}
|
||||||
|
|
||||||
mutating func insert<C: Collection>(sectionIDs: C, before beforeSectionID: String) where C.Element == String {
|
mutating func insert<C: Collection>(
|
||||||
|
sectionIDs: C,
|
||||||
|
before beforeSectionID: String
|
||||||
|
) where C.Element == String {
|
||||||
|
|
||||||
guard let sectionIndex = self.sectionIndex(of: beforeSectionID) else {
|
guard let sectionIndex = self.sectionIndex(of: beforeSectionID) else {
|
||||||
|
|
||||||
@@ -599,7 +738,10 @@ extension Internals {
|
|||||||
self.sections.insert(contentsOf: newSections, at: sectionIndex)
|
self.sections.insert(contentsOf: newSections, at: sectionIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
mutating func insert<C: Collection>(sectionIDs: C, after afterSectionID: String) where C.Element == String {
|
mutating func insert<C: Collection>(
|
||||||
|
sectionIDs: C,
|
||||||
|
after afterSectionID: String
|
||||||
|
) where C.Element == String {
|
||||||
|
|
||||||
guard let beforeIndex = self.sectionIndex(of: afterSectionID) else {
|
guard let beforeIndex = self.sectionIndex(of: afterSectionID) else {
|
||||||
|
|
||||||
@@ -616,6 +758,22 @@ extension Internals {
|
|||||||
}
|
}
|
||||||
self.sections.insert(contentsOf: newSections, at: sectionIndex)
|
self.sections.insert(contentsOf: newSections, at: sectionIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mutating func unsafeInsert<C: Collection>(
|
||||||
|
_ sectionIDs: C,
|
||||||
|
at sectionIndex: Int
|
||||||
|
) where C.Element == String {
|
||||||
|
|
||||||
|
let sectionIndexTransformer = self.sectionIndexTransformer
|
||||||
|
let newSections = sectionIDs.lazy.map {
|
||||||
|
|
||||||
|
return Section(
|
||||||
|
differenceIdentifier: $0,
|
||||||
|
indexTitle: sectionIndexTransformer($0)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
self.sections.insert(contentsOf: newSections, at: sectionIndex)
|
||||||
|
}
|
||||||
|
|
||||||
mutating func remove<S: Sequence>(sectionIDs: S) where S.Element == String {
|
mutating func remove<S: Sequence>(sectionIDs: S) where S.Element == String {
|
||||||
|
|
||||||
@@ -624,6 +782,16 @@ extension Internals {
|
|||||||
self.remove(sectionID: sectionID)
|
self.remove(sectionID: sectionID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mutating func unsafeRemove<S: Sequence>(
|
||||||
|
sectionsAt sectionIndices: S
|
||||||
|
) where S.Element == Int {
|
||||||
|
|
||||||
|
for sectionIndex in sectionIndices.sorted(by: >) {
|
||||||
|
|
||||||
|
self.sections.remove(at: sectionIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mutating func move(sectionID: String, before beforeSectionID: String) {
|
mutating func move(sectionID: String, before beforeSectionID: String) {
|
||||||
|
|
||||||
@@ -651,8 +819,21 @@ extension Internals {
|
|||||||
let sectionIndex = self.sections.index(after: beforeIndex)
|
let sectionIndex = self.sections.index(after: beforeIndex)
|
||||||
self.sections.insert(removed, at: sectionIndex)
|
self.sections.insert(removed, at: sectionIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mutating func unsafeMove(
|
||||||
|
sectionAt sectionIndex: Int,
|
||||||
|
to newSectionIndex: Int
|
||||||
|
) {
|
||||||
|
|
||||||
|
self.sections.move(
|
||||||
|
fromOffsets: .init(integer: sectionIndex),
|
||||||
|
toOffset: newSectionIndex
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
mutating func update<S: Sequence>(sectionIDs: S) where S.Element == String {
|
mutating func update<S: Sequence>(
|
||||||
|
sectionIDs: S
|
||||||
|
) where S.Element == String {
|
||||||
|
|
||||||
for sectionID in sectionIDs {
|
for sectionID in sectionIDs {
|
||||||
|
|
||||||
@@ -663,6 +844,16 @@ extension Internals {
|
|||||||
self.sections[sectionIndex].isReloaded = true
|
self.sections[sectionIndex].isReloaded = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mutating func unsafeUpdate<S: Sequence>(
|
||||||
|
sectionsAt sectionIndices: S
|
||||||
|
) where S.Element == Int {
|
||||||
|
|
||||||
|
for sectionIndex in sectionIndices {
|
||||||
|
|
||||||
|
self.sections[sectionIndex].isReloaded = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// MARK: Private
|
// MARK: Private
|
||||||
|
|||||||
@@ -59,16 +59,14 @@ extension Internals {
|
|||||||
Target,
|
Target,
|
||||||
StagedChangeset<[Internals.DiffableDataSourceSnapshot.Section]>,
|
StagedChangeset<[Internals.DiffableDataSourceSnapshot.Section]>,
|
||||||
@escaping ([Internals.DiffableDataSourceSnapshot.Section]) -> Void
|
@escaping ([Internals.DiffableDataSourceSnapshot.Section]) -> Void
|
||||||
) -> Void,
|
) -> Void
|
||||||
completion: @escaping () -> Void
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
self.apply(
|
self.apply(
|
||||||
.init(),
|
.init(),
|
||||||
target: target,
|
target: target,
|
||||||
animatingDifferences: animatingDifferences,
|
animatingDifferences: animatingDifferences,
|
||||||
performUpdates: performUpdates,
|
performUpdates: performUpdates
|
||||||
completion: completion
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,8 +78,7 @@ extension Internals {
|
|||||||
Target,
|
Target,
|
||||||
StagedChangeset<[Internals.DiffableDataSourceSnapshot.Section]>,
|
StagedChangeset<[Internals.DiffableDataSourceSnapshot.Section]>,
|
||||||
@escaping ([Internals.DiffableDataSourceSnapshot.Section]) -> Void
|
@escaping ([Internals.DiffableDataSourceSnapshot.Section]) -> Void
|
||||||
) -> Void,
|
) -> Void
|
||||||
completion: @escaping () -> Void
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
self.dispatcher.dispatch { [weak self] in
|
self.dispatcher.dispatch { [weak self] in
|
||||||
@@ -112,7 +109,6 @@ extension Internals {
|
|||||||
#if canImport(QuartzCore)
|
#if canImport(QuartzCore)
|
||||||
|
|
||||||
CATransaction.begin()
|
CATransaction.begin()
|
||||||
CATransaction.setCompletionBlock(completion)
|
|
||||||
|
|
||||||
if !animatingDifferences {
|
if !animatingDifferences {
|
||||||
|
|
||||||
@@ -122,11 +118,9 @@ extension Internals {
|
|||||||
|
|
||||||
CATransaction.commit()
|
CATransaction.commit()
|
||||||
|
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
performDiffingUpdates()
|
performDiffingUpdates()
|
||||||
completion()
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -43,7 +43,10 @@ internal protocol FetchedDiffableDataSourceSnapshotHandler: AnyObject {
|
|||||||
|
|
||||||
var sectionIndexTransformer: (_ sectionName: KeyPathString?) -> String? { get }
|
var sectionIndexTransformer: (_ sectionName: KeyPathString?) -> String? { get }
|
||||||
|
|
||||||
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChangeContentWith snapshot: Internals.DiffableDataSourceSnapshot)
|
func controller(
|
||||||
|
_ controller: NSFetchedResultsController<NSFetchRequestResult>,
|
||||||
|
didChangeContentWith snapshot: Internals.DiffableDataSourceSnapshot
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -33,13 +33,28 @@ internal protocol FetchedResultsControllerHandler: AnyObject {
|
|||||||
|
|
||||||
var sectionIndexTransformer: (_ sectionName: KeyPathString?) -> String? { get }
|
var sectionIndexTransformer: (_ sectionName: KeyPathString?) -> String? { get }
|
||||||
|
|
||||||
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChangeObject anObject: Any, atIndexPath indexPath: IndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: IndexPath?)
|
func controller(
|
||||||
|
_ controller: NSFetchedResultsController<NSFetchRequestResult>,
|
||||||
|
didChangeObject anObject: Any,
|
||||||
|
atIndexPath indexPath: IndexPath?,
|
||||||
|
forChangeType type: NSFetchedResultsChangeType,
|
||||||
|
newIndexPath: IndexPath?
|
||||||
|
)
|
||||||
|
|
||||||
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType)
|
func controller(
|
||||||
|
_ controller: NSFetchedResultsController<NSFetchRequestResult>,
|
||||||
|
didChangeSection sectionInfo: NSFetchedResultsSectionInfo,
|
||||||
|
atIndex sectionIndex: Int,
|
||||||
|
forChangeType type: NSFetchedResultsChangeType
|
||||||
|
)
|
||||||
|
|
||||||
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>)
|
func controllerWillChangeContent(
|
||||||
|
_ controller: NSFetchedResultsController<NSFetchRequestResult>
|
||||||
|
)
|
||||||
|
|
||||||
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>)
|
func controllerDidChangeContent(
|
||||||
|
_ controller: NSFetchedResultsController<NSFetchRequestResult>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -102,7 +117,6 @@ extension Internals {
|
|||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
self.handler?.controllerDidChangeContent(controller)
|
self.handler?.controllerDidChangeContent(controller)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -393,19 +393,31 @@ public final class ListMonitor<O: DynamicObject>: Hashable {
|
|||||||
observer,
|
observer,
|
||||||
willChange: { (observer, monitor) in
|
willChange: { (observer, monitor) in
|
||||||
|
|
||||||
observer.listMonitorWillChange(monitor)
|
observer.listMonitorWillChange(
|
||||||
|
monitor,
|
||||||
|
sourceIdentifier: monitor.fetchedResultsController.managedObjectContext.saveMetadata?.sourceIdentifier
|
||||||
|
)
|
||||||
},
|
},
|
||||||
didChange: { (observer, monitor) in
|
didChange: { (observer, monitor) in
|
||||||
|
|
||||||
observer.listMonitorDidChange(monitor)
|
observer.listMonitorDidChange(
|
||||||
|
monitor,
|
||||||
|
sourceIdentifier: monitor.fetchedResultsController.managedObjectContext.saveMetadata?.sourceIdentifier
|
||||||
|
)
|
||||||
},
|
},
|
||||||
willRefetch: { (observer, monitor) in
|
willRefetch: { (observer, monitor) in
|
||||||
|
|
||||||
observer.listMonitorWillRefetch(monitor)
|
observer.listMonitorWillRefetch(
|
||||||
|
monitor,
|
||||||
|
sourceIdentifier: monitor.fetchedResultsController.managedObjectContext.saveMetadata?.sourceIdentifier
|
||||||
|
)
|
||||||
},
|
},
|
||||||
didRefetch: { (observer, monitor) in
|
didRefetch: { (observer, monitor) in
|
||||||
|
|
||||||
observer.listMonitorDidRefetch(monitor)
|
observer.listMonitorDidRefetch(
|
||||||
|
monitor,
|
||||||
|
sourceIdentifier: monitor.fetchedResultsController.managedObjectContext.saveMetadata?.sourceIdentifier
|
||||||
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -428,38 +440,71 @@ public final class ListMonitor<O: DynamicObject>: Hashable {
|
|||||||
observer,
|
observer,
|
||||||
willChange: { (observer, monitor) in
|
willChange: { (observer, monitor) in
|
||||||
|
|
||||||
observer.listMonitorWillChange(monitor)
|
observer.listMonitorWillChange(
|
||||||
|
monitor,
|
||||||
|
sourceIdentifier: monitor.fetchedResultsController.managedObjectContext.saveMetadata?.sourceIdentifier
|
||||||
|
)
|
||||||
},
|
},
|
||||||
didChange: { (observer, monitor) in
|
didChange: { (observer, monitor) in
|
||||||
|
|
||||||
observer.listMonitorDidChange(monitor)
|
observer.listMonitorDidChange(
|
||||||
|
monitor,
|
||||||
|
sourceIdentifier: monitor.fetchedResultsController.managedObjectContext.saveMetadata?.sourceIdentifier
|
||||||
|
)
|
||||||
},
|
},
|
||||||
willRefetch: { (observer, monitor) in
|
willRefetch: { (observer, monitor) in
|
||||||
|
|
||||||
observer.listMonitorWillRefetch(monitor)
|
observer.listMonitorWillRefetch(
|
||||||
|
monitor,
|
||||||
|
sourceIdentifier: monitor.fetchedResultsController.managedObjectContext.saveMetadata?.sourceIdentifier
|
||||||
|
)
|
||||||
},
|
},
|
||||||
didRefetch: { (observer, monitor) in
|
didRefetch: { (observer, monitor) in
|
||||||
|
|
||||||
observer.listMonitorDidRefetch(monitor)
|
observer.listMonitorDidRefetch(
|
||||||
|
monitor,
|
||||||
|
sourceIdentifier: monitor.fetchedResultsController.managedObjectContext.saveMetadata?.sourceIdentifier
|
||||||
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
self.registerObserver(
|
self.registerObserver(
|
||||||
observer,
|
observer,
|
||||||
didInsertObject: { (observer, monitor, object, toIndexPath) in
|
didInsertObject: { (observer, monitor, object, toIndexPath) in
|
||||||
|
|
||||||
observer.listMonitor(monitor, didInsertObject: object, toIndexPath: toIndexPath)
|
observer.listMonitor(
|
||||||
|
monitor,
|
||||||
|
didInsertObject: object,
|
||||||
|
toIndexPath: toIndexPath,
|
||||||
|
sourceIdentifier: monitor.fetchedResultsController.managedObjectContext.saveMetadata?.sourceIdentifier
|
||||||
|
)
|
||||||
},
|
},
|
||||||
didDeleteObject: { (observer, monitor, object, fromIndexPath) in
|
didDeleteObject: { (observer, monitor, object, fromIndexPath) in
|
||||||
|
|
||||||
observer.listMonitor(monitor, didDeleteObject: object, fromIndexPath: fromIndexPath)
|
observer.listMonitor(
|
||||||
|
monitor,
|
||||||
|
didDeleteObject: object,
|
||||||
|
fromIndexPath: fromIndexPath,
|
||||||
|
sourceIdentifier: monitor.fetchedResultsController.managedObjectContext.saveMetadata?.sourceIdentifier
|
||||||
|
)
|
||||||
},
|
},
|
||||||
didUpdateObject: { (observer, monitor, object, atIndexPath) in
|
didUpdateObject: { (observer, monitor, object, atIndexPath) in
|
||||||
|
|
||||||
observer.listMonitor(monitor, didUpdateObject: object, atIndexPath: atIndexPath)
|
observer.listMonitor(
|
||||||
|
monitor,
|
||||||
|
didUpdateObject: object,
|
||||||
|
atIndexPath: atIndexPath,
|
||||||
|
sourceIdentifier: monitor.fetchedResultsController.managedObjectContext.saveMetadata?.sourceIdentifier
|
||||||
|
)
|
||||||
},
|
},
|
||||||
didMoveObject: { (observer, monitor, object, fromIndexPath, toIndexPath) in
|
didMoveObject: { (observer, monitor, object, fromIndexPath, toIndexPath) in
|
||||||
|
|
||||||
observer.listMonitor(monitor, didMoveObject: object, fromIndexPath: fromIndexPath, toIndexPath: toIndexPath)
|
observer.listMonitor(
|
||||||
|
monitor,
|
||||||
|
didMoveObject: object,
|
||||||
|
fromIndexPath: fromIndexPath,
|
||||||
|
toIndexPath: toIndexPath,
|
||||||
|
sourceIdentifier: monitor.fetchedResultsController.managedObjectContext.saveMetadata?.sourceIdentifier
|
||||||
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -482,49 +527,92 @@ public final class ListMonitor<O: DynamicObject>: Hashable {
|
|||||||
observer,
|
observer,
|
||||||
willChange: { (observer, monitor) in
|
willChange: { (observer, monitor) in
|
||||||
|
|
||||||
observer.listMonitorWillChange(monitor)
|
observer.listMonitorWillChange(
|
||||||
|
monitor,
|
||||||
|
sourceIdentifier: monitor.fetchedResultsController.managedObjectContext.saveMetadata?.sourceIdentifier
|
||||||
|
)
|
||||||
},
|
},
|
||||||
didChange: { (observer, monitor) in
|
didChange: { (observer, monitor) in
|
||||||
|
|
||||||
observer.listMonitorDidChange(monitor)
|
observer.listMonitorDidChange(
|
||||||
|
monitor,
|
||||||
|
sourceIdentifier: monitor.fetchedResultsController.managedObjectContext.saveMetadata?.sourceIdentifier
|
||||||
|
)
|
||||||
},
|
},
|
||||||
willRefetch: { (observer, monitor) in
|
willRefetch: { (observer, monitor) in
|
||||||
|
|
||||||
observer.listMonitorWillRefetch(monitor)
|
observer.listMonitorWillRefetch(
|
||||||
|
monitor,
|
||||||
|
sourceIdentifier: monitor.fetchedResultsController.managedObjectContext.saveMetadata?.sourceIdentifier
|
||||||
|
)
|
||||||
},
|
},
|
||||||
didRefetch: { (observer, monitor) in
|
didRefetch: { (observer, monitor) in
|
||||||
|
|
||||||
observer.listMonitorDidRefetch(monitor)
|
observer.listMonitorDidRefetch(
|
||||||
|
monitor,
|
||||||
|
sourceIdentifier: monitor.fetchedResultsController.managedObjectContext.saveMetadata?.sourceIdentifier
|
||||||
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
self.registerObserver(
|
self.registerObserver(
|
||||||
observer,
|
observer,
|
||||||
didInsertObject: { (observer, monitor, object, toIndexPath) in
|
didInsertObject: { (observer, monitor, object, toIndexPath) in
|
||||||
|
|
||||||
observer.listMonitor(monitor, didInsertObject: object, toIndexPath: toIndexPath)
|
observer.listMonitor(
|
||||||
|
monitor,
|
||||||
|
didInsertObject: object,
|
||||||
|
toIndexPath: toIndexPath,
|
||||||
|
sourceIdentifier: monitor.fetchedResultsController.managedObjectContext.saveMetadata?.sourceIdentifier
|
||||||
|
)
|
||||||
},
|
},
|
||||||
didDeleteObject: { (observer, monitor, object, fromIndexPath) in
|
didDeleteObject: { (observer, monitor, object, fromIndexPath) in
|
||||||
|
|
||||||
observer.listMonitor(monitor, didDeleteObject: object, fromIndexPath: fromIndexPath)
|
observer.listMonitor(
|
||||||
|
monitor,
|
||||||
|
didDeleteObject: object,
|
||||||
|
fromIndexPath: fromIndexPath,
|
||||||
|
sourceIdentifier: monitor.fetchedResultsController.managedObjectContext.saveMetadata?.sourceIdentifier
|
||||||
|
)
|
||||||
},
|
},
|
||||||
didUpdateObject: { (observer, monitor, object, atIndexPath) in
|
didUpdateObject: { (observer, monitor, object, atIndexPath) in
|
||||||
|
|
||||||
observer.listMonitor(monitor, didUpdateObject: object, atIndexPath: atIndexPath)
|
observer.listMonitor(
|
||||||
|
monitor,
|
||||||
|
didUpdateObject: object,
|
||||||
|
atIndexPath: atIndexPath,
|
||||||
|
sourceIdentifier: monitor.fetchedResultsController.managedObjectContext.saveMetadata?.sourceIdentifier
|
||||||
|
)
|
||||||
},
|
},
|
||||||
didMoveObject: { (observer, monitor, object, fromIndexPath, toIndexPath) in
|
didMoveObject: { (observer, monitor, object, fromIndexPath, toIndexPath) in
|
||||||
|
|
||||||
observer.listMonitor(monitor, didMoveObject: object, fromIndexPath: fromIndexPath, toIndexPath: toIndexPath)
|
observer.listMonitor(
|
||||||
|
monitor,
|
||||||
|
didMoveObject: object,
|
||||||
|
fromIndexPath: fromIndexPath,
|
||||||
|
toIndexPath: toIndexPath,
|
||||||
|
sourceIdentifier: monitor.fetchedResultsController.managedObjectContext.saveMetadata?.sourceIdentifier
|
||||||
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
self.registerObserver(
|
self.registerObserver(
|
||||||
observer,
|
observer,
|
||||||
didInsertSection: { (observer, monitor, sectionInfo, toIndex) in
|
didInsertSection: { (observer, monitor, sectionInfo, toIndex) in
|
||||||
|
|
||||||
observer.listMonitor(monitor, didInsertSection: sectionInfo, toSectionIndex: toIndex)
|
observer.listMonitor(
|
||||||
|
monitor,
|
||||||
|
didInsertSection: sectionInfo,
|
||||||
|
toSectionIndex: toIndex,
|
||||||
|
sourceIdentifier: monitor.fetchedResultsController.managedObjectContext.saveMetadata?.sourceIdentifier
|
||||||
|
)
|
||||||
},
|
},
|
||||||
didDeleteSection: { (observer, monitor, sectionInfo, fromIndex) in
|
didDeleteSection: { (observer, monitor, sectionInfo, fromIndex) in
|
||||||
|
|
||||||
observer.listMonitor(monitor, didDeleteSection: sectionInfo, fromSectionIndex: fromIndex)
|
observer.listMonitor(
|
||||||
|
monitor,
|
||||||
|
didDeleteSection: sectionInfo,
|
||||||
|
fromSectionIndex: fromIndex,
|
||||||
|
sourceIdentifier: monitor.fetchedResultsController.managedObjectContext.saveMetadata?.sourceIdentifier
|
||||||
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -555,11 +643,18 @@ public final class ListMonitor<O: DynamicObject>: Hashable {
|
|||||||
`refetch(...)` broadcasts `listMonitorWillRefetch(...)` to its observers immediately, and then `listMonitorDidRefetch(...)` after the new fetch request completes.
|
`refetch(...)` broadcasts `listMonitorWillRefetch(...)` to its observers immediately, and then `listMonitorDidRefetch(...)` after the new fetch request completes.
|
||||||
|
|
||||||
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||||
|
- parameter sourceIdentifier: an optional value that identifies the source of this transaction. This identifier will be passed to the change notifications and callers can use it for custom handling that depends on the source.
|
||||||
- Important: Starting CoreStore 4.0, all `FetchClause`s required by the `ListMonitor` should be provided in the arguments list of `refetch(...)`.
|
- Important: Starting CoreStore 4.0, all `FetchClause`s required by the `ListMonitor` should be provided in the arguments list of `refetch(...)`.
|
||||||
*/
|
*/
|
||||||
public func refetch(_ fetchClauses: FetchClause...) {
|
public func refetch(
|
||||||
|
_ fetchClauses: FetchClause...,
|
||||||
|
sourceIdentifier: Any?
|
||||||
|
) {
|
||||||
|
|
||||||
self.refetch(fetchClauses)
|
self.refetch(
|
||||||
|
fetchClauses,
|
||||||
|
sourceIdentifier: sourceIdentifier
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -568,14 +663,21 @@ public final class ListMonitor<O: DynamicObject>: Hashable {
|
|||||||
`refetch(...)` broadcasts `listMonitorWillRefetch(...)` to its observers immediately, and then `listMonitorDidRefetch(...)` after the new fetch request completes.
|
`refetch(...)` broadcasts `listMonitorWillRefetch(...)` to its observers immediately, and then `listMonitorDidRefetch(...)` after the new fetch request completes.
|
||||||
|
|
||||||
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
|
||||||
|
- parameter sourceIdentifier: an optional value that identifies the source of this transaction. This identifier will be passed to the change notifications and callers can use it for custom handling that depends on the source.
|
||||||
- Important: Starting CoreStore 4.0, all `FetchClause`s required by the `ListMonitor` should be provided in the arguments list of `refetch(...)`.
|
- Important: Starting CoreStore 4.0, all `FetchClause`s required by the `ListMonitor` should be provided in the arguments list of `refetch(...)`.
|
||||||
*/
|
*/
|
||||||
public func refetch(_ fetchClauses: [FetchClause]) {
|
public func refetch(
|
||||||
|
_ fetchClauses: [FetchClause],
|
||||||
|
sourceIdentifier: Any?
|
||||||
|
) {
|
||||||
|
|
||||||
self.refetch { (fetchRequest) in
|
self.refetch(
|
||||||
|
{ (fetchRequest) in
|
||||||
fetchClauses.forEach { $0.applyToFetchRequest(fetchRequest) }
|
|
||||||
}
|
fetchClauses.forEach { $0.applyToFetchRequest(fetchRequest) }
|
||||||
|
},
|
||||||
|
sourceIdentifier: sourceIdentifier
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -627,7 +729,12 @@ public final class ListMonitor<O: DynamicObject>: Hashable {
|
|||||||
|
|
||||||
// MARK: Internal
|
// MARK: Internal
|
||||||
|
|
||||||
internal convenience init(dataStack: DataStack, from: From<O>, sectionBy: SectionBy<O>?, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void) {
|
internal convenience init(
|
||||||
|
dataStack: DataStack,
|
||||||
|
from: From<O>,
|
||||||
|
sectionBy: SectionBy<O>?,
|
||||||
|
applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void
|
||||||
|
) {
|
||||||
|
|
||||||
self.init(
|
self.init(
|
||||||
context: dataStack.mainContext,
|
context: dataStack.mainContext,
|
||||||
@@ -639,7 +746,13 @@ public final class ListMonitor<O: DynamicObject>: Hashable {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal convenience init(dataStack: DataStack, from: From<O>, sectionBy: SectionBy<O>?, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void, createAsynchronously: @escaping (ListMonitor<O>) -> Void) {
|
internal convenience init(
|
||||||
|
dataStack: DataStack,
|
||||||
|
from: From<O>,
|
||||||
|
sectionBy: SectionBy<O>?,
|
||||||
|
applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void,
|
||||||
|
createAsynchronously: @escaping (ListMonitor<O>) -> Void
|
||||||
|
) {
|
||||||
|
|
||||||
self.init(
|
self.init(
|
||||||
context: dataStack.mainContext,
|
context: dataStack.mainContext,
|
||||||
@@ -651,7 +764,12 @@ public final class ListMonitor<O: DynamicObject>: Hashable {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal convenience init(unsafeTransaction: UnsafeDataTransaction, from: From<O>, sectionBy: SectionBy<O>?, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void) {
|
internal convenience init(
|
||||||
|
unsafeTransaction: UnsafeDataTransaction,
|
||||||
|
from: From<O>,
|
||||||
|
sectionBy: SectionBy<O>?,
|
||||||
|
applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void
|
||||||
|
) {
|
||||||
|
|
||||||
self.init(
|
self.init(
|
||||||
context: unsafeTransaction.context,
|
context: unsafeTransaction.context,
|
||||||
@@ -663,7 +781,13 @@ public final class ListMonitor<O: DynamicObject>: Hashable {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal convenience init(unsafeTransaction: UnsafeDataTransaction, from: From<O>, sectionBy: SectionBy<O>?, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void, createAsynchronously: @escaping (ListMonitor<O>) -> Void) {
|
internal convenience init(
|
||||||
|
unsafeTransaction: UnsafeDataTransaction,
|
||||||
|
from: From<O>,
|
||||||
|
sectionBy: SectionBy<O>?,
|
||||||
|
applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void,
|
||||||
|
createAsynchronously: @escaping (ListMonitor<O>) -> Void
|
||||||
|
) {
|
||||||
|
|
||||||
self.init(
|
self.init(
|
||||||
context: unsafeTransaction.context,
|
context: unsafeTransaction.context,
|
||||||
@@ -675,7 +799,12 @@ public final class ListMonitor<O: DynamicObject>: Hashable {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func registerChangeNotification(_ notificationKey: UnsafeRawPointer, name: Notification.Name, toObserver observer: AnyObject, callback: @escaping (_ monitor: ListMonitor<O>) -> Void) {
|
internal func registerChangeNotification(
|
||||||
|
_ notificationKey: UnsafeRawPointer,
|
||||||
|
name: Notification.Name,
|
||||||
|
toObserver observer: AnyObject,
|
||||||
|
callback: @escaping (_ monitor: ListMonitor<O>) -> Void
|
||||||
|
) {
|
||||||
|
|
||||||
Internals.setAssociatedRetainedObject(
|
Internals.setAssociatedRetainedObject(
|
||||||
Internals.NotificationObserver(
|
Internals.NotificationObserver(
|
||||||
@@ -695,7 +824,16 @@ public final class ListMonitor<O: DynamicObject>: Hashable {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func registerObjectNotification(_ notificationKey: UnsafeRawPointer, name: Notification.Name, toObserver observer: AnyObject, callback: @escaping (_ monitor: ListMonitor<O>, _ object: O, _ indexPath: IndexPath?, _ newIndexPath: IndexPath?) -> Void) {
|
internal func registerObjectNotification(
|
||||||
|
_ notificationKey: UnsafeRawPointer,
|
||||||
|
name: Notification.Name,
|
||||||
|
toObserver observer: AnyObject,
|
||||||
|
callback: @escaping (
|
||||||
|
_ monitor: ListMonitor<O>,
|
||||||
|
_ object: O,
|
||||||
|
_ indexPath: IndexPath?,
|
||||||
|
_ newIndexPath: IndexPath?
|
||||||
|
) -> Void) {
|
||||||
|
|
||||||
Internals.setAssociatedRetainedObject(
|
Internals.setAssociatedRetainedObject(
|
||||||
Internals.NotificationObserver(
|
Internals.NotificationObserver(
|
||||||
@@ -722,7 +860,16 @@ public final class ListMonitor<O: DynamicObject>: Hashable {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func registerSectionNotification(_ notificationKey: UnsafeRawPointer, name: Notification.Name, toObserver observer: AnyObject, callback: @escaping (_ monitor: ListMonitor<O>, _ sectionInfo: NSFetchedResultsSectionInfo, _ sectionIndex: Int) -> Void) {
|
internal func registerSectionNotification(
|
||||||
|
_ notificationKey: UnsafeRawPointer,
|
||||||
|
name: Notification.Name,
|
||||||
|
toObserver observer: AnyObject,
|
||||||
|
callback: @escaping (
|
||||||
|
_ monitor: ListMonitor<O>,
|
||||||
|
_ sectionInfo: NSFetchedResultsSectionInfo,
|
||||||
|
_ sectionIndex: Int
|
||||||
|
) -> Void
|
||||||
|
) {
|
||||||
|
|
||||||
Internals.setAssociatedRetainedObject(
|
Internals.setAssociatedRetainedObject(
|
||||||
Internals.NotificationObserver(
|
Internals.NotificationObserver(
|
||||||
@@ -745,7 +892,24 @@ public final class ListMonitor<O: DynamicObject>: Hashable {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func registerObserver<U: AnyObject>(_ observer: U, willChange: @escaping (_ observer: U, _ monitor: ListMonitor<O>) -> Void, didChange: @escaping (_ observer: U, _ monitor: ListMonitor<O>) -> Void, willRefetch: @escaping (_ observer: U, _ monitor: ListMonitor<O>) -> Void, didRefetch: @escaping (_ observer: U, _ monitor: ListMonitor<O>) -> Void) {
|
internal func registerObserver<U: AnyObject>(
|
||||||
|
_ observer: U,
|
||||||
|
willChange: @escaping (
|
||||||
|
_ observer: U,
|
||||||
|
_ monitor: ListMonitor<O>
|
||||||
|
) -> Void,
|
||||||
|
didChange: @escaping (
|
||||||
|
_ observer: U,
|
||||||
|
_ monitor: ListMonitor<O>
|
||||||
|
) -> Void,
|
||||||
|
willRefetch: @escaping (
|
||||||
|
_ observer: U,
|
||||||
|
_ monitor: ListMonitor<O>
|
||||||
|
) -> Void,
|
||||||
|
didRefetch: @escaping (
|
||||||
|
_ observer: U,
|
||||||
|
_ monitor: ListMonitor<O>
|
||||||
|
) -> Void) {
|
||||||
|
|
||||||
Internals.assert(
|
Internals.assert(
|
||||||
Thread.isMainThread,
|
Thread.isMainThread,
|
||||||
@@ -805,7 +969,33 @@ public final class ListMonitor<O: DynamicObject>: Hashable {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func registerObserver<U: AnyObject>(_ observer: U, didInsertObject: @escaping (_ observer: U, _ monitor: ListMonitor<O>, _ object: O, _ toIndexPath: IndexPath) -> Void, didDeleteObject: @escaping (_ observer: U, _ monitor: ListMonitor<O>, _ object: O, _ fromIndexPath: IndexPath) -> Void, didUpdateObject: @escaping (_ observer: U, _ monitor: ListMonitor<O>, _ object: O, _ atIndexPath: IndexPath) -> Void, didMoveObject: @escaping (_ observer: U, _ monitor: ListMonitor<O>, _ object: O, _ fromIndexPath: IndexPath, _ toIndexPath: IndexPath) -> Void) {
|
internal func registerObserver<U: AnyObject>(
|
||||||
|
_ observer: U,
|
||||||
|
didInsertObject: @escaping (
|
||||||
|
_ observer: U,
|
||||||
|
_ monitor: ListMonitor<O>,
|
||||||
|
_ object: O,
|
||||||
|
_ toIndexPath: IndexPath
|
||||||
|
) -> Void,
|
||||||
|
didDeleteObject: @escaping (
|
||||||
|
_ observer: U,
|
||||||
|
_ monitor: ListMonitor<O>,
|
||||||
|
_ object: O,
|
||||||
|
_ fromIndexPath: IndexPath
|
||||||
|
) -> Void,
|
||||||
|
didUpdateObject: @escaping (
|
||||||
|
_ observer: U,
|
||||||
|
_ monitor: ListMonitor<O>,
|
||||||
|
_ object: O,
|
||||||
|
_ atIndexPath: IndexPath
|
||||||
|
) -> Void,
|
||||||
|
didMoveObject: @escaping (
|
||||||
|
_ observer: U,
|
||||||
|
_ monitor: ListMonitor<O>,
|
||||||
|
_ object: O,
|
||||||
|
_ fromIndexPath: IndexPath,
|
||||||
|
_ toIndexPath: IndexPath
|
||||||
|
) -> Void) {
|
||||||
|
|
||||||
Internals.assert(
|
Internals.assert(
|
||||||
Thread.isMainThread,
|
Thread.isMainThread,
|
||||||
@@ -866,7 +1056,20 @@ public final class ListMonitor<O: DynamicObject>: Hashable {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func registerObserver<U: AnyObject>(_ observer: U, didInsertSection: @escaping (_ observer: U, _ monitor: ListMonitor<O>, _ sectionInfo: NSFetchedResultsSectionInfo, _ toIndex: Int) -> Void, didDeleteSection: @escaping (_ observer: U, _ monitor: ListMonitor<O>, _ sectionInfo: NSFetchedResultsSectionInfo, _ fromIndex: Int) -> Void) {
|
internal func registerObserver<U: AnyObject>(
|
||||||
|
_ observer: U,
|
||||||
|
didInsertSection: @escaping (
|
||||||
|
_ observer: U,
|
||||||
|
_ monitor: ListMonitor<O>,
|
||||||
|
_ sectionInfo: NSFetchedResultsSectionInfo,
|
||||||
|
_ toIndex: Int
|
||||||
|
) -> Void,
|
||||||
|
didDeleteSection: @escaping (
|
||||||
|
_ observer: U,
|
||||||
|
_ monitor: ListMonitor<O>,
|
||||||
|
_ sectionInfo: NSFetchedResultsSectionInfo,
|
||||||
|
_ fromIndex: Int
|
||||||
|
) -> Void) {
|
||||||
|
|
||||||
Internals.assert(
|
Internals.assert(
|
||||||
Thread.isMainThread,
|
Thread.isMainThread,
|
||||||
@@ -922,7 +1125,10 @@ public final class ListMonitor<O: DynamicObject>: Hashable {
|
|||||||
Internals.setAssociatedRetainedObject(nilValue, forKey: &self.didDeleteSectionKey, inObject: observer)
|
Internals.setAssociatedRetainedObject(nilValue, forKey: &self.didDeleteSectionKey, inObject: observer)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func refetch(_ applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void) {
|
internal func refetch(
|
||||||
|
_ applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void,
|
||||||
|
sourceIdentifier: Any?
|
||||||
|
) {
|
||||||
|
|
||||||
Internals.assert(
|
Internals.assert(
|
||||||
Thread.isMainThread,
|
Thread.isMainThread,
|
||||||
@@ -987,10 +1193,15 @@ public final class ListMonitor<O: DynamicObject>: Hashable {
|
|||||||
|
|
||||||
self.isPendingRefetch = false
|
self.isPendingRefetch = false
|
||||||
|
|
||||||
|
newFetchedResultsController.managedObjectContext.saveMetadata = .init(
|
||||||
|
isSavingSynchronously: false,
|
||||||
|
sourceIdentifier: sourceIdentifier
|
||||||
|
)
|
||||||
NotificationCenter.default.post(
|
NotificationCenter.default.post(
|
||||||
name: Notification.Name.listMonitorDidRefetchList,
|
name: Notification.Name.listMonitorDidRefetchList,
|
||||||
object: self
|
object: self
|
||||||
)
|
)
|
||||||
|
newFetchedResultsController.managedObjectContext.saveMetadata = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1051,7 +1262,15 @@ public final class ListMonitor<O: DynamicObject>: Hashable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func recreateFetchedResultsController(context: NSManagedObjectContext, from: From<O>, sectionBy: SectionBy<O>?, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void) -> (controller: Internals.CoreStoreFetchedResultsController, delegate: Internals.FetchedResultsControllerDelegate) {
|
private static func recreateFetchedResultsController(
|
||||||
|
context: NSManagedObjectContext,
|
||||||
|
from: From<O>,
|
||||||
|
sectionBy: SectionBy<O>?,
|
||||||
|
applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void
|
||||||
|
) -> (
|
||||||
|
controller: Internals.CoreStoreFetchedResultsController,
|
||||||
|
delegate: Internals.FetchedResultsControllerDelegate
|
||||||
|
) {
|
||||||
|
|
||||||
let fetchRequest = Internals.CoreStoreFetchRequest<NSManagedObject>()
|
let fetchRequest = Internals.CoreStoreFetchRequest<NSManagedObject>()
|
||||||
fetchRequest.fetchLimit = 0
|
fetchRequest.fetchLimit = 0
|
||||||
@@ -1077,7 +1296,14 @@ public final class ListMonitor<O: DynamicObject>: Hashable {
|
|||||||
private let from: From<O>
|
private let from: From<O>
|
||||||
private let sectionBy: SectionBy<O>?
|
private let sectionBy: SectionBy<O>?
|
||||||
|
|
||||||
private init(context: NSManagedObjectContext, transactionQueue: DispatchQueue, from: From<O>, sectionBy: SectionBy<O>?, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void, createAsynchronously: ((ListMonitor<O>) -> Void)?) {
|
private init(
|
||||||
|
context: NSManagedObjectContext,
|
||||||
|
transactionQueue: DispatchQueue,
|
||||||
|
from: From<O>,
|
||||||
|
sectionBy: SectionBy<O>?,
|
||||||
|
applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void,
|
||||||
|
createAsynchronously: ((ListMonitor<O>) -> Void)?
|
||||||
|
) {
|
||||||
|
|
||||||
self.isSectioned = (sectionBy != nil)
|
self.isSectioned = (sectionBy != nil)
|
||||||
self.from = from
|
self.from = from
|
||||||
@@ -1124,7 +1350,7 @@ public final class ListMonitor<O: DynamicObject>: Hashable {
|
|||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.refetch(self.applyFetchClauses)
|
self.refetch(self.applyFetchClauses, sourceIdentifier: nil)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1148,7 +1374,7 @@ public final class ListMonitor<O: DynamicObject>: Hashable {
|
|||||||
|
|
||||||
if previousStores != currentStores {
|
if previousStores != currentStores {
|
||||||
|
|
||||||
self.refetch(self.applyFetchClauses)
|
self.refetch(self.applyFetchClauses, sourceIdentifier: nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1294,7 +1520,13 @@ extension ListMonitor: FetchedResultsControllerHandler {
|
|||||||
return self.sectionByIndexTransformer
|
return self.sectionByIndexTransformer
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChangeObject anObject: Any, atIndexPath indexPath: IndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
|
internal func controller(
|
||||||
|
_ controller: NSFetchedResultsController<NSFetchRequestResult>,
|
||||||
|
didChangeObject anObject: Any,
|
||||||
|
atIndexPath indexPath: IndexPath?,
|
||||||
|
forChangeType type: NSFetchedResultsChangeType,
|
||||||
|
newIndexPath: IndexPath?
|
||||||
|
) {
|
||||||
|
|
||||||
switch type {
|
switch type {
|
||||||
|
|
||||||
@@ -1344,7 +1576,12 @@ extension ListMonitor: FetchedResultsControllerHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) {
|
internal func controller(
|
||||||
|
_ controller: NSFetchedResultsController<NSFetchRequestResult>,
|
||||||
|
didChangeSection sectionInfo: NSFetchedResultsSectionInfo,
|
||||||
|
atIndex sectionIndex: Int,
|
||||||
|
forChangeType type: NSFetchedResultsChangeType
|
||||||
|
) {
|
||||||
|
|
||||||
switch type {
|
switch type {
|
||||||
|
|
||||||
@@ -1373,7 +1610,9 @@ extension ListMonitor: FetchedResultsControllerHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
|
internal func controllerWillChangeContent(
|
||||||
|
_ controller: NSFetchedResultsController<NSFetchRequestResult>
|
||||||
|
) {
|
||||||
|
|
||||||
self.taskGroup.enter()
|
self.taskGroup.enter()
|
||||||
NotificationCenter.default.post(
|
NotificationCenter.default.post(
|
||||||
@@ -1382,7 +1621,9 @@ extension ListMonitor: FetchedResultsControllerHandler {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
|
internal func controllerDidChangeContent(
|
||||||
|
_ controller: NSFetchedResultsController<NSFetchRequestResult>
|
||||||
|
) {
|
||||||
|
|
||||||
defer {
|
defer {
|
||||||
|
|
||||||
|
|||||||
@@ -51,15 +51,54 @@ public protocol ListObserver: AnyObject {
|
|||||||
The default implementation does nothing.
|
The default implementation does nothing.
|
||||||
|
|
||||||
- parameter monitor: the `ListMonitor` monitoring the list being observed
|
- parameter monitor: the `ListMonitor` monitoring the list being observed
|
||||||
|
- parameter sourceIdentifier: an optional identifier provided by the transaction source
|
||||||
*/
|
*/
|
||||||
func listMonitorWillChange(_ monitor: ListMonitor<ListEntityType>)
|
func listMonitorWillChange(
|
||||||
|
_ monitor: ListMonitor<ListEntityType>,
|
||||||
|
sourceIdentifier: Any?
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
Handles processing just before a change to the observed list occurs. (Optional)
|
||||||
|
The default implementation does nothing.
|
||||||
|
|
||||||
|
- parameter monitor: the `ListMonitor` monitoring the list being observed
|
||||||
|
*/
|
||||||
|
func listMonitorWillChange(
|
||||||
|
_ monitor: ListMonitor<ListEntityType>
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
Handles processing right after a change to the observed list occurs. (Required)
|
||||||
|
|
||||||
|
- parameter monitor: the `ListMonitor` monitoring the object being observed
|
||||||
|
- parameter sourceIdentifier: an optional identifier provided by the transaction source
|
||||||
|
*/
|
||||||
|
func listMonitorDidChange(
|
||||||
|
_ monitor: ListMonitor<ListEntityType>,
|
||||||
|
sourceIdentifier: Any?
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Handles processing right after a change to the observed list occurs. (Required)
|
Handles processing right after a change to the observed list occurs. (Required)
|
||||||
|
|
||||||
- parameter monitor: the `ListMonitor` monitoring the object being observed
|
- parameter monitor: the `ListMonitor` monitoring the object being observed
|
||||||
*/
|
*/
|
||||||
func listMonitorDidChange(_ monitor: ListMonitor<ListEntityType>)
|
func listMonitorDidChange(
|
||||||
|
_ monitor: ListMonitor<ListEntityType>
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
This method is broadcast from within the `ListMonitor`'s `refetch(...)` method to let observers prepare for the internal `NSFetchedResultsController`'s pending change to its predicate, sort descriptors, etc. (Optional)
|
||||||
|
|
||||||
|
- Important: All `ListMonitor` access between `listMonitorWillRefetch(_:)` and `listMonitorDidRefetch(_:)` will raise and assertion. The actual refetch will happen after the `NSFetchedResultsController`'s last `controllerDidChangeContent(_:)` notification completes.
|
||||||
|
- parameter monitor: the `ListMonitor` monitoring the object being observed
|
||||||
|
- parameter sourceIdentifier: an optional identifier provided by the transaction source
|
||||||
|
*/
|
||||||
|
func listMonitorWillRefetch(
|
||||||
|
_ monitor: ListMonitor<ListEntityType>,
|
||||||
|
sourceIdentifier: Any?
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
This method is broadcast from within the `ListMonitor`'s `refetch(...)` method to let observers prepare for the internal `NSFetchedResultsController`'s pending change to its predicate, sort descriptors, etc. (Optional)
|
This method is broadcast from within the `ListMonitor`'s `refetch(...)` method to let observers prepare for the internal `NSFetchedResultsController`'s pending change to its predicate, sort descriptors, etc. (Optional)
|
||||||
@@ -67,7 +106,21 @@ public protocol ListObserver: AnyObject {
|
|||||||
- Important: All `ListMonitor` access between `listMonitorWillRefetch(_:)` and `listMonitorDidRefetch(_:)` will raise and assertion. The actual refetch will happen after the `NSFetchedResultsController`'s last `controllerDidChangeContent(_:)` notification completes.
|
- Important: All `ListMonitor` access between `listMonitorWillRefetch(_:)` and `listMonitorDidRefetch(_:)` will raise and assertion. The actual refetch will happen after the `NSFetchedResultsController`'s last `controllerDidChangeContent(_:)` notification completes.
|
||||||
- parameter monitor: the `ListMonitor` monitoring the object being observed
|
- parameter monitor: the `ListMonitor` monitoring the object being observed
|
||||||
*/
|
*/
|
||||||
func listMonitorWillRefetch(_ monitor: ListMonitor<ListEntityType>)
|
func listMonitorWillRefetch(
|
||||||
|
_ monitor: ListMonitor<ListEntityType>
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
After the `ListMonitor`'s `refetch(...)` method is called, this method is broadcast after the `NSFetchedResultsController`'s last `controllerDidChangeContent(_:)` notification completes. (Required)
|
||||||
|
|
||||||
|
- Important: When `listMonitorDidRefetch(_:)` is called it should be assumed that all `ListMonitor`'s previous data have been reset, including counts, objects, and persistent stores.
|
||||||
|
- parameter monitor: the `ListMonitor` monitoring the object being observed
|
||||||
|
- parameter sourceIdentifier: an optional identifier provided by the transaction source
|
||||||
|
*/
|
||||||
|
func listMonitorDidRefetch(
|
||||||
|
_ monitor: ListMonitor<ListEntityType>,
|
||||||
|
sourceIdentifier: Any?
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
After the `ListMonitor`'s `refetch(...)` method is called, this method is broadcast after the `NSFetchedResultsController`'s last `controllerDidChangeContent(_:)` notification completes. (Required)
|
After the `ListMonitor`'s `refetch(...)` method is called, this method is broadcast after the `NSFetchedResultsController`'s last `controllerDidChangeContent(_:)` notification completes. (Required)
|
||||||
@@ -75,7 +128,9 @@ public protocol ListObserver: AnyObject {
|
|||||||
- Important: When `listMonitorDidRefetch(_:)` is called it should be assumed that all `ListMonitor`'s previous data have been reset, including counts, objects, and persistent stores.
|
- Important: When `listMonitorDidRefetch(_:)` is called it should be assumed that all `ListMonitor`'s previous data have been reset, including counts, objects, and persistent stores.
|
||||||
- parameter monitor: the `ListMonitor` monitoring the object being observed
|
- parameter monitor: the `ListMonitor` monitoring the object being observed
|
||||||
*/
|
*/
|
||||||
func listMonitorDidRefetch(_ monitor: ListMonitor<ListEntityType>)
|
func listMonitorDidRefetch(
|
||||||
|
_ monitor: ListMonitor<ListEntityType>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -83,9 +138,45 @@ public protocol ListObserver: AnyObject {
|
|||||||
|
|
||||||
extension ListObserver {
|
extension ListObserver {
|
||||||
|
|
||||||
public func listMonitorWillChange(_ monitor: ListMonitor<ListEntityType>) { }
|
public func listMonitorWillChange(
|
||||||
|
_ monitor: ListMonitor<ListEntityType>,
|
||||||
|
sourceIdentifier: Any?
|
||||||
|
) {
|
||||||
|
|
||||||
|
self.listMonitorWillChange(monitor)
|
||||||
|
}
|
||||||
|
|
||||||
public func listMonitorWillRefetch(_ monitor: ListMonitor<ListEntityType>) { }
|
public func listMonitorWillChange(
|
||||||
|
_ monitor: ListMonitor<ListEntityType>
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public func listMonitorDidChange(
|
||||||
|
_ monitor: ListMonitor<ListEntityType>,
|
||||||
|
sourceIdentifier: Any?
|
||||||
|
) {
|
||||||
|
|
||||||
|
self.listMonitorDidChange(monitor)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func listMonitorWillRefetch(
|
||||||
|
_ monitor: ListMonitor<ListEntityType>,
|
||||||
|
sourceIdentifier: Any?
|
||||||
|
) {
|
||||||
|
|
||||||
|
self.listMonitorWillRefetch(monitor)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func listMonitorWillRefetch(
|
||||||
|
_ monitor: ListMonitor<ListEntityType>
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public func listMonitorDidRefetch(
|
||||||
|
_ monitor: ListMonitor<ListEntityType>,
|
||||||
|
sourceIdentifier: Any?
|
||||||
|
) {
|
||||||
|
|
||||||
|
self.listMonitorDidRefetch(monitor)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -110,8 +201,44 @@ public protocol ListObjectObserver: ListObserver {
|
|||||||
- parameter monitor: the `ListMonitor` monitoring the list being observed
|
- parameter monitor: the `ListMonitor` monitoring the list being observed
|
||||||
- parameter object: the entity type for the inserted object
|
- parameter object: the entity type for the inserted object
|
||||||
- parameter indexPath: the new `NSIndexPath` for the inserted object
|
- parameter indexPath: the new `NSIndexPath` for the inserted object
|
||||||
|
- parameter sourceIdentifier: an optional identifier provided by the transaction source
|
||||||
*/
|
*/
|
||||||
func listMonitor(_ monitor: ListMonitor<ListEntityType>, didInsertObject object: ListEntityType, toIndexPath indexPath: IndexPath)
|
func listMonitor(
|
||||||
|
_ monitor: ListMonitor<ListEntityType>,
|
||||||
|
didInsertObject object: ListEntityType,
|
||||||
|
toIndexPath indexPath: IndexPath,
|
||||||
|
sourceIdentifier: Any?
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
Notifies that an object was inserted to the specified `NSIndexPath` in the list. (Optional)
|
||||||
|
The default implementation does nothing.
|
||||||
|
|
||||||
|
- parameter monitor: the `ListMonitor` monitoring the list being observed
|
||||||
|
- parameter object: the entity type for the inserted object
|
||||||
|
- parameter indexPath: the new `NSIndexPath` for the inserted object
|
||||||
|
*/
|
||||||
|
func listMonitor(
|
||||||
|
_ monitor: ListMonitor<ListEntityType>,
|
||||||
|
didInsertObject object: ListEntityType,
|
||||||
|
toIndexPath indexPath: IndexPath
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
Notifies that an object was deleted from the specified `NSIndexPath` in the list. (Optional)
|
||||||
|
The default implementation does nothing.
|
||||||
|
|
||||||
|
- parameter monitor: the `ListMonitor` monitoring the list being observed
|
||||||
|
- parameter object: the entity type for the deleted object
|
||||||
|
- parameter indexPath: the `NSIndexPath` for the deleted object
|
||||||
|
- parameter sourceIdentifier: an optional identifier provided by the transaction source
|
||||||
|
*/
|
||||||
|
func listMonitor(
|
||||||
|
_ monitor: ListMonitor<ListEntityType>,
|
||||||
|
didDeleteObject object: ListEntityType,
|
||||||
|
fromIndexPath indexPath: IndexPath,
|
||||||
|
sourceIdentifier: Any?
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Notifies that an object was deleted from the specified `NSIndexPath` in the list. (Optional)
|
Notifies that an object was deleted from the specified `NSIndexPath` in the list. (Optional)
|
||||||
@@ -121,7 +248,27 @@ public protocol ListObjectObserver: ListObserver {
|
|||||||
- parameter object: the entity type for the deleted object
|
- parameter object: the entity type for the deleted object
|
||||||
- parameter indexPath: the `NSIndexPath` for the deleted object
|
- parameter indexPath: the `NSIndexPath` for the deleted object
|
||||||
*/
|
*/
|
||||||
func listMonitor(_ monitor: ListMonitor<ListEntityType>, didDeleteObject object: ListEntityType, fromIndexPath indexPath: IndexPath)
|
func listMonitor(
|
||||||
|
_ monitor: ListMonitor<ListEntityType>,
|
||||||
|
didDeleteObject object: ListEntityType,
|
||||||
|
fromIndexPath indexPath: IndexPath
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
Notifies that an object at the specified `NSIndexPath` was updated. (Optional)
|
||||||
|
The default implementation does nothing.
|
||||||
|
|
||||||
|
- parameter monitor: the `ListMonitor` monitoring the list being observed
|
||||||
|
- parameter object: the entity type for the updated object
|
||||||
|
- parameter indexPath: the `NSIndexPath` for the updated object
|
||||||
|
- parameter sourceIdentifier: an optional identifier provided by the transaction source
|
||||||
|
*/
|
||||||
|
func listMonitor(
|
||||||
|
_ monitor: ListMonitor<ListEntityType>,
|
||||||
|
didUpdateObject object: ListEntityType,
|
||||||
|
atIndexPath indexPath: IndexPath,
|
||||||
|
sourceIdentifier: Any?
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Notifies that an object at the specified `NSIndexPath` was updated. (Optional)
|
Notifies that an object at the specified `NSIndexPath` was updated. (Optional)
|
||||||
@@ -131,7 +278,29 @@ public protocol ListObjectObserver: ListObserver {
|
|||||||
- parameter object: the entity type for the updated object
|
- parameter object: the entity type for the updated object
|
||||||
- parameter indexPath: the `NSIndexPath` for the updated object
|
- parameter indexPath: the `NSIndexPath` for the updated object
|
||||||
*/
|
*/
|
||||||
func listMonitor(_ monitor: ListMonitor<ListEntityType>, didUpdateObject object: ListEntityType, atIndexPath indexPath: IndexPath)
|
func listMonitor(
|
||||||
|
_ monitor: ListMonitor<ListEntityType>,
|
||||||
|
didUpdateObject object: ListEntityType,
|
||||||
|
atIndexPath indexPath: IndexPath
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
Notifies that an object's index changed. (Optional)
|
||||||
|
The default implementation does nothing.
|
||||||
|
|
||||||
|
- parameter monitor: the `ListMonitor` monitoring the list being observed
|
||||||
|
- parameter object: the entity type for the moved object
|
||||||
|
- parameter fromIndexPath: the previous `NSIndexPath` for the moved object
|
||||||
|
- parameter toIndexPath: the new `NSIndexPath` for the moved object
|
||||||
|
- parameter sourceIdentifier: an optional identifier provided by the transaction source
|
||||||
|
*/
|
||||||
|
func listMonitor(
|
||||||
|
_ monitor: ListMonitor<ListEntityType>,
|
||||||
|
didMoveObject object: ListEntityType,
|
||||||
|
fromIndexPath: IndexPath,
|
||||||
|
toIndexPath: IndexPath,
|
||||||
|
sourceIdentifier: Any?
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Notifies that an object's index changed. (Optional)
|
Notifies that an object's index changed. (Optional)
|
||||||
@@ -142,7 +311,12 @@ public protocol ListObjectObserver: ListObserver {
|
|||||||
- parameter fromIndexPath: the previous `NSIndexPath` for the moved object
|
- parameter fromIndexPath: the previous `NSIndexPath` for the moved object
|
||||||
- parameter toIndexPath: the new `NSIndexPath` for the moved object
|
- parameter toIndexPath: the new `NSIndexPath` for the moved object
|
||||||
*/
|
*/
|
||||||
func listMonitor(_ monitor: ListMonitor<ListEntityType>, didMoveObject object: ListEntityType, fromIndexPath: IndexPath, toIndexPath: IndexPath)
|
func listMonitor(
|
||||||
|
_ monitor: ListMonitor<ListEntityType>,
|
||||||
|
didMoveObject object: ListEntityType,
|
||||||
|
fromIndexPath: IndexPath,
|
||||||
|
toIndexPath: IndexPath
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -150,13 +324,88 @@ public protocol ListObjectObserver: ListObserver {
|
|||||||
|
|
||||||
extension ListObjectObserver {
|
extension ListObjectObserver {
|
||||||
|
|
||||||
public func listMonitor(_ monitor: ListMonitor<ListEntityType>, didInsertObject object: ListEntityType, toIndexPath indexPath: IndexPath) { }
|
public func listMonitor(
|
||||||
|
_ monitor: ListMonitor<ListEntityType>,
|
||||||
|
didInsertObject object: ListEntityType,
|
||||||
|
toIndexPath indexPath: IndexPath,
|
||||||
|
sourceIdentifier: Any?
|
||||||
|
) {
|
||||||
|
|
||||||
|
self.listMonitor(
|
||||||
|
monitor,
|
||||||
|
didInsertObject: object,
|
||||||
|
toIndexPath: indexPath
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
public func listMonitor(_ monitor: ListMonitor<ListEntityType>, didDeleteObject object: ListEntityType, fromIndexPath indexPath: IndexPath) { }
|
public func listMonitor(
|
||||||
|
_ monitor: ListMonitor<ListEntityType>,
|
||||||
|
didInsertObject object: ListEntityType,
|
||||||
|
toIndexPath indexPath: IndexPath
|
||||||
|
) {}
|
||||||
|
|
||||||
public func listMonitor(_ monitor: ListMonitor<ListEntityType>, didUpdateObject object: ListEntityType, atIndexPath indexPath: IndexPath) { }
|
public func listMonitor(
|
||||||
|
_ monitor: ListMonitor<ListEntityType>,
|
||||||
|
didDeleteObject object: ListEntityType,
|
||||||
|
fromIndexPath indexPath: IndexPath,
|
||||||
|
sourceIdentifier: Any?
|
||||||
|
) {
|
||||||
|
|
||||||
|
self.listMonitor(
|
||||||
|
monitor,
|
||||||
|
didDeleteObject: object,
|
||||||
|
fromIndexPath: indexPath
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
public func listMonitor(_ monitor: ListMonitor<ListEntityType>, didMoveObject object: ListEntityType, fromIndexPath: IndexPath, toIndexPath: IndexPath) { }
|
public func listMonitor(
|
||||||
|
_ monitor: ListMonitor<ListEntityType>,
|
||||||
|
didDeleteObject object: ListEntityType,
|
||||||
|
fromIndexPath indexPath: IndexPath
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public func listMonitor(
|
||||||
|
_ monitor: ListMonitor<ListEntityType>,
|
||||||
|
didUpdateObject object: ListEntityType,
|
||||||
|
atIndexPath indexPath: IndexPath,
|
||||||
|
sourceIdentifier: Any?
|
||||||
|
) {
|
||||||
|
|
||||||
|
self.listMonitor(
|
||||||
|
monitor,
|
||||||
|
didUpdateObject: object,
|
||||||
|
atIndexPath: indexPath
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func listMonitor(
|
||||||
|
_ monitor: ListMonitor<ListEntityType>,
|
||||||
|
didUpdateObject object: ListEntityType,
|
||||||
|
atIndexPath indexPath: IndexPath
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public func listMonitor(
|
||||||
|
_ monitor: ListMonitor<ListEntityType>,
|
||||||
|
didMoveObject object: ListEntityType,
|
||||||
|
fromIndexPath: IndexPath,
|
||||||
|
toIndexPath: IndexPath,
|
||||||
|
sourceIdentifier: Any?
|
||||||
|
) {
|
||||||
|
|
||||||
|
self.listMonitor(
|
||||||
|
monitor,
|
||||||
|
didMoveObject: object,
|
||||||
|
fromIndexPath: fromIndexPath,
|
||||||
|
toIndexPath: toIndexPath
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func listMonitor(
|
||||||
|
_ monitor: ListMonitor<ListEntityType>,
|
||||||
|
didMoveObject object: ListEntityType,
|
||||||
|
fromIndexPath: IndexPath,
|
||||||
|
toIndexPath: IndexPath
|
||||||
|
) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -182,8 +431,44 @@ public protocol ListSectionObserver: ListObjectObserver {
|
|||||||
- parameter monitor: the `ListMonitor` monitoring the list being observed
|
- parameter monitor: the `ListMonitor` monitoring the list being observed
|
||||||
- parameter sectionInfo: the `NSFetchedResultsSectionInfo` for the inserted section
|
- parameter sectionInfo: the `NSFetchedResultsSectionInfo` for the inserted section
|
||||||
- parameter sectionIndex: the new section index for the new section
|
- parameter sectionIndex: the new section index for the new section
|
||||||
|
- parameter sourceIdentifier: an optional identifier provided by the transaction source
|
||||||
*/
|
*/
|
||||||
func listMonitor(_ monitor: ListMonitor<ListEntityType>, didInsertSection sectionInfo: NSFetchedResultsSectionInfo, toSectionIndex sectionIndex: Int)
|
func listMonitor(
|
||||||
|
_ monitor: ListMonitor<ListEntityType>,
|
||||||
|
didInsertSection sectionInfo: NSFetchedResultsSectionInfo,
|
||||||
|
toSectionIndex sectionIndex: Int,
|
||||||
|
sourceIdentifier: Any?
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
Notifies that a section was inserted at the specified index. (Optional)
|
||||||
|
The default implementation does nothing.
|
||||||
|
|
||||||
|
- parameter monitor: the `ListMonitor` monitoring the list being observed
|
||||||
|
- parameter sectionInfo: the `NSFetchedResultsSectionInfo` for the inserted section
|
||||||
|
- parameter sectionIndex: the new section index for the new section
|
||||||
|
*/
|
||||||
|
func listMonitor(
|
||||||
|
_ monitor: ListMonitor<ListEntityType>,
|
||||||
|
didInsertSection sectionInfo: NSFetchedResultsSectionInfo,
|
||||||
|
toSectionIndex sectionIndex: Int
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
Notifies that a section was inserted at the specified index. (Optional)
|
||||||
|
The default implementation does nothing.
|
||||||
|
|
||||||
|
- parameter monitor: the `ListMonitor` monitoring the list being observed
|
||||||
|
- parameter sectionInfo: the `NSFetchedResultsSectionInfo` for the deleted section
|
||||||
|
- parameter sectionIndex: the previous section index for the deleted section
|
||||||
|
- parameter sourceIdentifier: an optional identifier provided by the transaction source
|
||||||
|
*/
|
||||||
|
func listMonitor(
|
||||||
|
_ monitor: ListMonitor<ListEntityType>,
|
||||||
|
didDeleteSection sectionInfo: NSFetchedResultsSectionInfo,
|
||||||
|
fromSectionIndex sectionIndex: Int,
|
||||||
|
sourceIdentifier: Any?
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Notifies that a section was inserted at the specified index. (Optional)
|
Notifies that a section was inserted at the specified index. (Optional)
|
||||||
@@ -193,7 +478,11 @@ public protocol ListSectionObserver: ListObjectObserver {
|
|||||||
- parameter sectionInfo: the `NSFetchedResultsSectionInfo` for the deleted section
|
- parameter sectionInfo: the `NSFetchedResultsSectionInfo` for the deleted section
|
||||||
- parameter sectionIndex: the previous section index for the deleted section
|
- parameter sectionIndex: the previous section index for the deleted section
|
||||||
*/
|
*/
|
||||||
func listMonitor(_ monitor: ListMonitor<ListEntityType>, didDeleteSection sectionInfo: NSFetchedResultsSectionInfo, fromSectionIndex sectionIndex: Int)
|
func listMonitor(
|
||||||
|
_ monitor: ListMonitor<ListEntityType>,
|
||||||
|
didDeleteSection sectionInfo: NSFetchedResultsSectionInfo,
|
||||||
|
fromSectionIndex sectionIndex: Int
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -201,7 +490,43 @@ public protocol ListSectionObserver: ListObjectObserver {
|
|||||||
|
|
||||||
extension ListSectionObserver {
|
extension ListSectionObserver {
|
||||||
|
|
||||||
public func listMonitor(_ monitor: ListMonitor<ListEntityType>, didInsertSection sectionInfo: NSFetchedResultsSectionInfo, toSectionIndex sectionIndex: Int) { }
|
public func listMonitor(
|
||||||
|
_ monitor: ListMonitor<ListEntityType>,
|
||||||
|
didInsertSection sectionInfo: NSFetchedResultsSectionInfo,
|
||||||
|
toSectionIndex sectionIndex: Int,
|
||||||
|
sourceIdentifier: Any?
|
||||||
|
) {
|
||||||
|
|
||||||
|
self.listMonitor(
|
||||||
|
monitor,
|
||||||
|
didInsertSection: sectionInfo,
|
||||||
|
toSectionIndex: sectionIndex
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
public func listMonitor(_ monitor: ListMonitor<ListEntityType>, didDeleteSection sectionInfo: NSFetchedResultsSectionInfo, fromSectionIndex sectionIndex: Int) { }
|
public func listMonitor(
|
||||||
|
_ monitor: ListMonitor<ListEntityType>,
|
||||||
|
didInsertSection sectionInfo: NSFetchedResultsSectionInfo,
|
||||||
|
toSectionIndex sectionIndex: Int
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public func listMonitor(
|
||||||
|
_ monitor: ListMonitor<ListEntityType>,
|
||||||
|
didDeleteSection sectionInfo: NSFetchedResultsSectionInfo,
|
||||||
|
fromSectionIndex sectionIndex: Int,
|
||||||
|
sourceIdentifier: Any?
|
||||||
|
) {
|
||||||
|
|
||||||
|
self.listMonitor(
|
||||||
|
monitor,
|
||||||
|
didDeleteSection: sectionInfo,
|
||||||
|
fromSectionIndex: sectionIndex
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func listMonitor(
|
||||||
|
_ monitor: ListMonitor<ListEntityType>,
|
||||||
|
didDeleteSection sectionInfo: NSFetchedResultsSectionInfo,
|
||||||
|
fromSectionIndex sectionIndex: Int
|
||||||
|
) {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -135,8 +135,19 @@ extension ListPublisher {
|
|||||||
|
|
||||||
func cancel() {
|
func cancel() {
|
||||||
|
|
||||||
self.publisher.removeObserver(self)
|
|
||||||
self.subscriber = nil
|
self.subscriber = nil
|
||||||
|
|
||||||
|
if Thread.isMainThread {
|
||||||
|
|
||||||
|
self.publisher.removeObserver(self)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
|
||||||
|
self.publisher.removeObserver(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -76,13 +76,7 @@ public final class ListPublisher<O: DynamicObject>: Hashable {
|
|||||||
/**
|
/**
|
||||||
A snapshot of the latest state of this list
|
A snapshot of the latest state of this list
|
||||||
*/
|
*/
|
||||||
public fileprivate(set) var snapshot: ListSnapshot<O> = .init() {
|
public private(set) var snapshot: ListSnapshot<O> = .init()
|
||||||
|
|
||||||
didSet {
|
|
||||||
|
|
||||||
self.notifyObservers()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: Public (Observers)
|
// MARK: Public (Observers)
|
||||||
@@ -111,7 +105,7 @@ public final class ListPublisher<O: DynamicObject>: Hashable {
|
|||||||
"Attempted to add an observer of type \(Internals.typeName(observer)) outside the main thread."
|
"Attempted to add an observer of type \(Internals.typeName(observer)) outside the main thread."
|
||||||
)
|
)
|
||||||
self.observers.setObject(
|
self.observers.setObject(
|
||||||
Internals.Closure(callback),
|
Internals.Closure({ callback($0.listPublisher) }),
|
||||||
forKey: observer
|
forKey: observer
|
||||||
)
|
)
|
||||||
if notifyInitial {
|
if notifyInitial {
|
||||||
@@ -119,6 +113,44 @@ public final class ListPublisher<O: DynamicObject>: Hashable {
|
|||||||
callback(self)
|
callback(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Registers an object as an observer to be notified when changes to the `ListPublisher`'s snapshot occur.
|
||||||
|
|
||||||
|
To prevent retain-cycles, `ListPublisher` only keeps `weak` references to its observers.
|
||||||
|
|
||||||
|
For thread safety, this method needs to be called from the main thread. An assertion failure will occur (on debug builds only) if called from any thread other than the main thread.
|
||||||
|
|
||||||
|
Calling `addObserver(_:_:)` multiple times on the same observer is safe.
|
||||||
|
|
||||||
|
- parameter observer: an object to become owner of the specified `callback`
|
||||||
|
- parameter notifyInitial: if `true`, the callback is executed immediately with the current publisher state. Otherwise only succeeding updates will notify the observer. Default value is `false`.
|
||||||
|
- parameter initialSourceIdentifier: an optional value that identifies the initial callback invocation if `notifyInitial` is `true`.
|
||||||
|
- parameter callback: the closure to execute when changes occur
|
||||||
|
*/
|
||||||
|
public func addObserver<T: AnyObject>(
|
||||||
|
_ observer: T,
|
||||||
|
notifyInitial: Bool = false,
|
||||||
|
initialSourceIdentifier: Any? = nil,
|
||||||
|
_ callback: @escaping (
|
||||||
|
_ listPublisher: ListPublisher<O>,
|
||||||
|
_ sourceIdentifier: Any?
|
||||||
|
) -> Void
|
||||||
|
) {
|
||||||
|
|
||||||
|
Internals.assert(
|
||||||
|
Thread.isMainThread,
|
||||||
|
"Attempted to add an observer of type \(Internals.typeName(observer)) outside the main thread."
|
||||||
|
)
|
||||||
|
self.observers.setObject(
|
||||||
|
Internals.Closure(callback),
|
||||||
|
forKey: observer
|
||||||
|
)
|
||||||
|
if notifyInitial {
|
||||||
|
|
||||||
|
callback(self, initialSourceIdentifier)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Unregisters an object from receiving notifications for changes to the `ListPublisher`'s snapshot.
|
Unregisters an object from receiving notifications for changes to the `ListPublisher`'s snapshot.
|
||||||
@@ -149,8 +181,12 @@ public final class ListPublisher<O: DynamicObject>: Hashable {
|
|||||||
)
|
)
|
||||||
```
|
```
|
||||||
- parameter clauseChain: a `FetchChainableBuilderType` built from a chain of clauses
|
- parameter clauseChain: a `FetchChainableBuilderType` built from a chain of clauses
|
||||||
|
- parameter sourceIdentifier: an optional value that identifies the source of this transaction. This identifier will be passed to the change notifications and callers can use it for custom handling that depends on the source.
|
||||||
*/
|
*/
|
||||||
public func refetch<B: FetchChainableBuilderType>(_ clauseChain: B) throws where B.ObjectType == O {
|
public func refetch<B: FetchChainableBuilderType>(
|
||||||
|
_ clauseChain: B,
|
||||||
|
sourceIdentifier: Any? = nil
|
||||||
|
) throws where B.ObjectType == O {
|
||||||
|
|
||||||
try self.refetch(
|
try self.refetch(
|
||||||
from: clauseChain.from,
|
from: clauseChain.from,
|
||||||
@@ -158,7 +194,8 @@ public final class ListPublisher<O: DynamicObject>: Hashable {
|
|||||||
applyFetchClauses: { (fetchRequest) in
|
applyFetchClauses: { (fetchRequest) in
|
||||||
|
|
||||||
clauseChain.fetchClauses.forEach { $0.applyToFetchRequest(fetchRequest) }
|
clauseChain.fetchClauses.forEach { $0.applyToFetchRequest(fetchRequest) }
|
||||||
}
|
},
|
||||||
|
sourceIdentifier: sourceIdentifier
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,8 +210,12 @@ public final class ListPublisher<O: DynamicObject>: Hashable {
|
|||||||
)
|
)
|
||||||
```
|
```
|
||||||
- parameter clauseChain: a `SectionMonitorBuilderType` built from a chain of clauses
|
- parameter clauseChain: a `SectionMonitorBuilderType` built from a chain of clauses
|
||||||
|
- parameter sourceIdentifier: an optional value that identifies the source of this transaction. This identifier will be passed to the change notifications and callers can use it for custom handling that depends on the source.
|
||||||
*/
|
*/
|
||||||
public func refetch<B: SectionMonitorBuilderType>(_ clauseChain: B) throws where B.ObjectType == O {
|
public func refetch<B: SectionMonitorBuilderType>(
|
||||||
|
_ clauseChain: B,
|
||||||
|
sourceIdentifier: Any? = nil
|
||||||
|
) throws where B.ObjectType == O {
|
||||||
|
|
||||||
try self.refetch(
|
try self.refetch(
|
||||||
from: clauseChain.from,
|
from: clauseChain.from,
|
||||||
@@ -182,7 +223,8 @@ public final class ListPublisher<O: DynamicObject>: Hashable {
|
|||||||
applyFetchClauses: { (fetchRequest) in
|
applyFetchClauses: { (fetchRequest) in
|
||||||
|
|
||||||
clauseChain.fetchClauses.forEach { $0.applyToFetchRequest(fetchRequest) }
|
clauseChain.fetchClauses.forEach { $0.applyToFetchRequest(fetchRequest) }
|
||||||
}
|
},
|
||||||
|
sourceIdentifier: sourceIdentifier
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,7 +272,12 @@ public final class ListPublisher<O: DynamicObject>: Hashable {
|
|||||||
|
|
||||||
internal private(set) lazy var context: NSManagedObjectContext = self.fetchedResultsController.managedObjectContext
|
internal private(set) lazy var context: NSManagedObjectContext = self.fetchedResultsController.managedObjectContext
|
||||||
|
|
||||||
internal convenience init(dataStack: DataStack, from: From<ObjectType>, sectionBy: SectionBy<ObjectType>?, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void) {
|
internal convenience init(
|
||||||
|
dataStack: DataStack,
|
||||||
|
from: From<ObjectType>,
|
||||||
|
sectionBy: SectionBy<ObjectType>?,
|
||||||
|
applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void
|
||||||
|
) {
|
||||||
|
|
||||||
self.init(
|
self.init(
|
||||||
context: dataStack.mainContext,
|
context: dataStack.mainContext,
|
||||||
@@ -241,7 +288,13 @@ public final class ListPublisher<O: DynamicObject>: Hashable {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal convenience init(dataStack: DataStack, from: From<ObjectType>, sectionBy: SectionBy<ObjectType>?, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void, createAsynchronously: @escaping (ListPublisher<ObjectType>) -> Void) {
|
internal convenience init(
|
||||||
|
dataStack: DataStack,
|
||||||
|
from: From<ObjectType>,
|
||||||
|
sectionBy: SectionBy<ObjectType>?,
|
||||||
|
applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void,
|
||||||
|
createAsynchronously: @escaping (ListPublisher<ObjectType>) -> Void
|
||||||
|
) {
|
||||||
|
|
||||||
self.init(
|
self.init(
|
||||||
context: dataStack.mainContext,
|
context: dataStack.mainContext,
|
||||||
@@ -252,7 +305,12 @@ public final class ListPublisher<O: DynamicObject>: Hashable {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal convenience init(unsafeTransaction: UnsafeDataTransaction, from: From<ObjectType>, sectionBy: SectionBy<ObjectType>?, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void) {
|
internal convenience init(
|
||||||
|
unsafeTransaction: UnsafeDataTransaction,
|
||||||
|
from: From<ObjectType>,
|
||||||
|
sectionBy: SectionBy<ObjectType>?,
|
||||||
|
applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void
|
||||||
|
) {
|
||||||
|
|
||||||
self.init(
|
self.init(
|
||||||
context: unsafeTransaction.context,
|
context: unsafeTransaction.context,
|
||||||
@@ -263,7 +321,13 @@ public final class ListPublisher<O: DynamicObject>: Hashable {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal convenience init(unsafeTransaction: UnsafeDataTransaction, from: From<ObjectType>, sectionBy: SectionBy<ObjectType>?, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void, createAsynchronously: @escaping (ListPublisher<ObjectType>) -> Void) {
|
internal convenience init(
|
||||||
|
unsafeTransaction: UnsafeDataTransaction,
|
||||||
|
from: From<ObjectType>,
|
||||||
|
sectionBy: SectionBy<ObjectType>?,
|
||||||
|
applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void,
|
||||||
|
createAsynchronously: @escaping (ListPublisher<ObjectType>) -> Void
|
||||||
|
) {
|
||||||
|
|
||||||
self.init(
|
self.init(
|
||||||
context: unsafeTransaction.context,
|
context: unsafeTransaction.context,
|
||||||
@@ -274,7 +338,12 @@ public final class ListPublisher<O: DynamicObject>: Hashable {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func refetch(from: From<O>, sectionBy: SectionBy<O>?, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void) throws {
|
internal func refetch(
|
||||||
|
from: From<O>,
|
||||||
|
sectionBy: SectionBy<O>?,
|
||||||
|
applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void,
|
||||||
|
sourceIdentifier: Any?
|
||||||
|
) throws {
|
||||||
|
|
||||||
let (newFetchedResultsController, newFetchedResultsControllerDelegate) = Self.recreateFetchedResultsController(
|
let (newFetchedResultsController, newFetchedResultsControllerDelegate) = Self.recreateFetchedResultsController(
|
||||||
context: self.fetchedResultsController.managedObjectContext,
|
context: self.fetchedResultsController.managedObjectContext,
|
||||||
@@ -291,7 +360,13 @@ public final class ListPublisher<O: DynamicObject>: Hashable {
|
|||||||
(self.fetchedResultsController, self.fetchedResultsControllerDelegate) = (newFetchedResultsController, newFetchedResultsControllerDelegate)
|
(self.fetchedResultsController, self.fetchedResultsControllerDelegate) = (newFetchedResultsController, newFetchedResultsControllerDelegate)
|
||||||
|
|
||||||
newFetchedResultsControllerDelegate.handler = self
|
newFetchedResultsControllerDelegate.handler = self
|
||||||
|
|
||||||
|
newFetchedResultsController.managedObjectContext.saveMetadata = .init(
|
||||||
|
isSavingSynchronously: true,
|
||||||
|
sourceIdentifier: sourceIdentifier
|
||||||
|
)
|
||||||
try newFetchedResultsController.performFetchFromSpecifiedStores()
|
try newFetchedResultsController.performFetchFromSpecifiedStores()
|
||||||
|
newFetchedResultsController.managedObjectContext.saveMetadata = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
@@ -301,6 +376,20 @@ public final class ListPublisher<O: DynamicObject>: Hashable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: FilePrivate
|
||||||
|
|
||||||
|
fileprivate typealias ObserverClosureType = Internals.Closure<(listPublisher: ListPublisher<O>, sourceIdentifier: Any?), Void>
|
||||||
|
|
||||||
|
fileprivate func set(
|
||||||
|
snapshot: ListSnapshot<O>,
|
||||||
|
sourceIdentifier: Any?
|
||||||
|
) {
|
||||||
|
|
||||||
|
self.snapshot = snapshot
|
||||||
|
self.notifyObservers(sourceIdentifier: sourceIdentifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// MARK: Private
|
// MARK: Private
|
||||||
|
|
||||||
private var query: (
|
private var query: (
|
||||||
@@ -315,9 +404,17 @@ public final class ListPublisher<O: DynamicObject>: Hashable {
|
|||||||
private var observerForWillChangePersistentStore: Internals.NotificationObserver!
|
private var observerForWillChangePersistentStore: Internals.NotificationObserver!
|
||||||
private var observerForDidChangePersistentStore: Internals.NotificationObserver!
|
private var observerForDidChangePersistentStore: Internals.NotificationObserver!
|
||||||
|
|
||||||
private lazy var observers: NSMapTable<AnyObject, Internals.Closure<ListPublisher<O>, Void>> = .weakToStrongObjects()
|
private lazy var observers: NSMapTable<AnyObject, ObserverClosureType> = .weakToStrongObjects()
|
||||||
|
|
||||||
private static func recreateFetchedResultsController(context: NSManagedObjectContext, from: From<ObjectType>, sectionBy: SectionBy<ObjectType>?, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void) -> (controller: Internals.CoreStoreFetchedResultsController, delegate: Internals.FetchedDiffableDataSourceSnapshotDelegate) {
|
private static func recreateFetchedResultsController(
|
||||||
|
context: NSManagedObjectContext,
|
||||||
|
from: From<ObjectType>,
|
||||||
|
sectionBy: SectionBy<ObjectType>?,
|
||||||
|
applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void
|
||||||
|
) -> (
|
||||||
|
controller: Internals.CoreStoreFetchedResultsController,
|
||||||
|
delegate: Internals.FetchedDiffableDataSourceSnapshotDelegate
|
||||||
|
) {
|
||||||
|
|
||||||
let fetchRequest = Internals.CoreStoreFetchRequest<NSManagedObject>()
|
let fetchRequest = Internals.CoreStoreFetchRequest<NSManagedObject>()
|
||||||
fetchRequest.fetchLimit = 0
|
fetchRequest.fetchLimit = 0
|
||||||
@@ -339,7 +436,13 @@ public final class ListPublisher<O: DynamicObject>: Hashable {
|
|||||||
return (fetchedResultsController, fetchedResultsControllerDelegate)
|
return (fetchedResultsController, fetchedResultsControllerDelegate)
|
||||||
}
|
}
|
||||||
|
|
||||||
private init(context: NSManagedObjectContext, from: From<ObjectType>, sectionBy: SectionBy<ObjectType>?, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void, createAsynchronously: ((ListPublisher<ObjectType>) -> Void)?) {
|
private init(
|
||||||
|
context: NSManagedObjectContext,
|
||||||
|
from: From<ObjectType>,
|
||||||
|
sectionBy: SectionBy<ObjectType>?,
|
||||||
|
applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void,
|
||||||
|
createAsynchronously: ((ListPublisher<ObjectType>) -> Void)?
|
||||||
|
) {
|
||||||
|
|
||||||
self.query = (
|
self.query = (
|
||||||
from: from,
|
from: from,
|
||||||
@@ -359,15 +462,19 @@ public final class ListPublisher<O: DynamicObject>: Hashable {
|
|||||||
try! self.fetchedResultsController.performFetchFromSpecifiedStores()
|
try! self.fetchedResultsController.performFetchFromSpecifiedStores()
|
||||||
}
|
}
|
||||||
|
|
||||||
private func notifyObservers() {
|
private func notifyObservers(sourceIdentifier: Any?) {
|
||||||
|
|
||||||
guard let enumerator = self.observers.objectEnumerator() else {
|
guard let enumerator = self.observers.objectEnumerator() else {
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
let arguments: ObserverClosureType.Arguments = (
|
||||||
|
listPublisher: self,
|
||||||
|
sourceIdentifier: sourceIdentifier
|
||||||
|
)
|
||||||
for closure in enumerator {
|
for closure in enumerator {
|
||||||
|
|
||||||
(closure as! Internals.Closure<ListPublisher<O>, Void>).invoke(with: self)
|
(closure as! ObserverClosureType).invoke(with: arguments)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -384,11 +491,18 @@ extension ListPublisher: FetchedDiffableDataSourceSnapshotHandler {
|
|||||||
return self.query.sectionIndexTransformer
|
return self.query.sectionIndexTransformer
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChangeContentWith snapshot: Internals.DiffableDataSourceSnapshot) {
|
internal func controller(
|
||||||
|
_ controller: NSFetchedResultsController<NSFetchRequestResult>,
|
||||||
|
didChangeContentWith snapshot: Internals.DiffableDataSourceSnapshot
|
||||||
|
) {
|
||||||
|
|
||||||
self.snapshot = .init(
|
let context = controller.managedObjectContext
|
||||||
diffableSnapshot: snapshot,
|
self.set(
|
||||||
context: controller.managedObjectContext
|
snapshot: .init(
|
||||||
|
diffableSnapshot: snapshot,
|
||||||
|
context: context
|
||||||
|
),
|
||||||
|
sourceIdentifier: context.saveMetadata?.sourceIdentifier
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,7 +100,10 @@ public struct ListSnapshot<O: DynamicObject>: RandomAccessCollection, Hashable {
|
|||||||
- parameter itemIndex: the index for the object within the section. Using an `itemIndex` with an invalid range will raise an exception.
|
- parameter itemIndex: the index for the object within the section. Using an `itemIndex` with an invalid range will raise an exception.
|
||||||
- returns: the `ObjectPublisher<O>` interfacing the object at the specified section and item index
|
- returns: the `ObjectPublisher<O>` interfacing the object at the specified section and item index
|
||||||
*/
|
*/
|
||||||
public subscript(sectionIndex: Int, itemIndex: Int) -> ObjectPublisher<O> {
|
public subscript(
|
||||||
|
sectionIndex: Int,
|
||||||
|
itemIndex: Int
|
||||||
|
) -> ObjectPublisher<O> {
|
||||||
|
|
||||||
let context = self.context!
|
let context = self.context!
|
||||||
let snapshot = self.diffableSnapshot
|
let snapshot = self.diffableSnapshot
|
||||||
@@ -116,7 +119,10 @@ public struct ListSnapshot<O: DynamicObject>: RandomAccessCollection, Hashable {
|
|||||||
- parameter itemIndex: the index for the object within the section. Using an `itemIndex` with an invalid range will return `nil`.
|
- parameter itemIndex: the index for the object within the section. Using an `itemIndex` with an invalid range will return `nil`.
|
||||||
- returns: the `ObjectPublisher<O>` interfacing the object at the specified section and item index, or `nil` if out of bounds
|
- returns: the `ObjectPublisher<O>` interfacing the object at the specified section and item index, or `nil` if out of bounds
|
||||||
*/
|
*/
|
||||||
public subscript(safeSectionIndex sectionIndex: Int, safeItemIndex itemIndex: Int) -> ObjectPublisher<O>? {
|
public subscript(
|
||||||
|
safeSectionIndex sectionIndex: Int,
|
||||||
|
safeItemIndex itemIndex: Int
|
||||||
|
) -> ObjectPublisher<O>? {
|
||||||
|
|
||||||
guard let context = self.context else {
|
guard let context = self.context else {
|
||||||
|
|
||||||
@@ -333,7 +339,10 @@ public struct ListSnapshot<O: DynamicObject>: RandomAccessCollection, Hashable {
|
|||||||
- parameter indices: the positions of the itemIDs to return. Specifying an invalid value will raise an exception.
|
- parameter indices: the positions of the itemIDs to return. Specifying an invalid value will raise an exception.
|
||||||
- returns: the `ItemID` array belonging to the given `SectionID` at the specified indices
|
- returns: the `ItemID` array belonging to the given `SectionID` at the specified indices
|
||||||
*/
|
*/
|
||||||
public func itemIDs<S: Sequence>(inSectionWithID sectionID: SectionID, atIndices indices: S) -> [ItemID] where S.Element == Int {
|
public func itemIDs<S: Sequence>(
|
||||||
|
inSectionWithID sectionID: SectionID,
|
||||||
|
atIndices indices: S
|
||||||
|
) -> [ItemID] where S.Element == Int {
|
||||||
|
|
||||||
let itemIDs = self.diffableSnapshot.itemIdentifiers(inSection: sectionID)
|
let itemIDs = self.diffableSnapshot.itemIdentifiers(inSection: sectionID)
|
||||||
return indices.map({ itemIDs[$0] })
|
return indices.map({ itemIDs[$0] })
|
||||||
@@ -398,7 +407,10 @@ public struct ListSnapshot<O: DynamicObject>: RandomAccessCollection, Hashable {
|
|||||||
- parameter itemIndices: the positions of items within the section. Specifying an invalid value will raise an exception.
|
- parameter itemIndices: the positions of items within the section. Specifying an invalid value will raise an exception.
|
||||||
- returns: an array of `ObjectPublisher`s for the items in the specified `SectionID` and indices
|
- returns: an array of `ObjectPublisher`s for the items in the specified `SectionID` and indices
|
||||||
*/
|
*/
|
||||||
public func items<S: Sequence>(inSectionWithID sectionID: SectionID, atIndices itemIndices: S) -> [ObjectPublisher<O>] where S.Element == Int {
|
public func items<S: Sequence>(
|
||||||
|
inSectionWithID sectionID: SectionID,
|
||||||
|
atIndices itemIndices: S
|
||||||
|
) -> [ObjectPublisher<O>] where S.Element == Int {
|
||||||
|
|
||||||
let context = self.context!
|
let context = self.context!
|
||||||
let itemIDs = self.diffableSnapshot.itemIdentifiers(inSection: sectionID)
|
let itemIDs = self.diffableSnapshot.itemIdentifiers(inSection: sectionID)
|
||||||
@@ -446,7 +458,10 @@ public struct ListSnapshot<O: DynamicObject>: RandomAccessCollection, Hashable {
|
|||||||
- parameter itemIndices: the positions of items within the section. Specifying an invalid value will raise an exception.
|
- parameter itemIndices: the positions of items within the section. Specifying an invalid value will raise an exception.
|
||||||
- returns: a lazy sequence of `ObjectPublisher`s for the items in the specified `SectionID` and indices
|
- returns: a lazy sequence of `ObjectPublisher`s for the items in the specified `SectionID` and indices
|
||||||
*/
|
*/
|
||||||
public func lazy<S: Sequence>(inSectionWithID sectionID: SectionID, atIndices itemIndices: S) -> LazyMapSequence<S, ObjectPublisher<O>> where S.Element == Int {
|
public func lazy<S: Sequence>(
|
||||||
|
inSectionWithID sectionID: SectionID,
|
||||||
|
atIndices itemIndices: S
|
||||||
|
) -> LazyMapSequence<S, ObjectPublisher<O>> where S.Element == Int {
|
||||||
|
|
||||||
let context = self.context!
|
let context = self.context!
|
||||||
let itemIDs = self.diffableSnapshot.itemIdentifiers(inSection: sectionID)
|
let itemIDs = self.diffableSnapshot.itemIdentifiers(inSection: sectionID)
|
||||||
@@ -466,9 +481,32 @@ public struct ListSnapshot<O: DynamicObject>: RandomAccessCollection, Hashable {
|
|||||||
- parameter itemIDs: the object identifiers for the objects to append
|
- parameter itemIDs: the object identifiers for the objects to append
|
||||||
- parameter sectionID: the section to append the items to
|
- parameter sectionID: the section to append the items to
|
||||||
*/
|
*/
|
||||||
public mutating func appendItems<C: Collection>(withIDs itemIDs: C, toSectionWithID sectionID: SectionID? = nil) where C.Element == ItemID {
|
public mutating func appendItems<C: Collection>(
|
||||||
|
withIDs itemIDs: C,
|
||||||
|
toSectionWithID sectionID: SectionID? = nil
|
||||||
|
) where C.Element == ItemID {
|
||||||
|
|
||||||
self.diffableSnapshot.appendItems(itemIDs, toSection: sectionID)
|
self.mutate {
|
||||||
|
|
||||||
|
$0.appendItems(itemIDs, toSection: sectionID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Appends extra items to the specified section index
|
||||||
|
|
||||||
|
- parameter itemIDs: the object identifiers for the objects to append
|
||||||
|
- parameter sectionIndex: the section index to append the items to. Specifying an invalid value will raise an exception.
|
||||||
|
*/
|
||||||
|
public mutating func appendItems<C: Collection>(
|
||||||
|
with itemIDs: C,
|
||||||
|
toSectionAt sectionIndex: Int
|
||||||
|
) where C.Element == ItemID {
|
||||||
|
|
||||||
|
self.mutate {
|
||||||
|
|
||||||
|
$0.unsafeAppendItems(itemIDs, toSectionAt: sectionIndex)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -477,9 +515,15 @@ public struct ListSnapshot<O: DynamicObject>: RandomAccessCollection, Hashable {
|
|||||||
- parameter itemIDs: the object identifiers for the objects to insert
|
- parameter itemIDs: the object identifiers for the objects to insert
|
||||||
- parameter beforeItemID: an existing identifier to insert items before of. Specifying an invalid value will raise an exception.
|
- parameter beforeItemID: an existing identifier to insert items before of. Specifying an invalid value will raise an exception.
|
||||||
*/
|
*/
|
||||||
public mutating func insertItems<C: Collection>(withIDs itemIDs: C, beforeItemID: ItemID) where C.Element == ItemID {
|
public mutating func insertItems<C: Collection>(
|
||||||
|
withIDs itemIDs: C,
|
||||||
|
beforeItemID: ItemID
|
||||||
|
) where C.Element == ItemID {
|
||||||
|
|
||||||
self.diffableSnapshot.insertItems(itemIDs, beforeItem: beforeItemID)
|
self.mutate {
|
||||||
|
|
||||||
|
$0.insertItems(itemIDs, beforeItem: beforeItemID)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -488,9 +532,32 @@ public struct ListSnapshot<O: DynamicObject>: RandomAccessCollection, Hashable {
|
|||||||
- parameter itemIDs: the object identifiers for the objects to insert
|
- parameter itemIDs: the object identifiers for the objects to insert
|
||||||
- parameter beforeItemID: an existing identifier to insert items after of. Specifying an invalid value will raise an exception.
|
- parameter beforeItemID: an existing identifier to insert items after of. Specifying an invalid value will raise an exception.
|
||||||
*/
|
*/
|
||||||
public mutating func insertItems<C: Collection>(withIDs itemIDs: C, afterItemID: ItemID) where C.Element == ItemID {
|
public mutating func insertItems<C: Collection>(
|
||||||
|
withIDs itemIDs: C,
|
||||||
|
afterItemID: ItemID
|
||||||
|
) where C.Element == ItemID {
|
||||||
|
|
||||||
self.diffableSnapshot.insertItems(itemIDs, afterItem: afterItemID)
|
self.mutate {
|
||||||
|
|
||||||
|
$0.insertItems(itemIDs, afterItem: afterItemID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Inserts extra items at a specified index path
|
||||||
|
|
||||||
|
- parameter itemIDs: the object identifiers for the objects to insert
|
||||||
|
- parameter indexPath: an indexPath to insert the items into. Specifying an invalid value will raise an exception.
|
||||||
|
*/
|
||||||
|
public mutating func insertItems<C: Collection>(
|
||||||
|
withIDs itemIDs: C,
|
||||||
|
at indexPath: IndexPath
|
||||||
|
) where C.Element == ItemID {
|
||||||
|
|
||||||
|
self.mutate {
|
||||||
|
|
||||||
|
$0.unsafeInsertItems(itemIDs, at: indexPath)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -498,9 +565,25 @@ public struct ListSnapshot<O: DynamicObject>: RandomAccessCollection, Hashable {
|
|||||||
|
|
||||||
- parameter itemIDs: the object identifiers for the objects to delete
|
- parameter itemIDs: the object identifiers for the objects to delete
|
||||||
*/
|
*/
|
||||||
public mutating func deleteItems<S: Sequence>(withIDs itemIDs: S) where S.Element == ItemID {
|
public mutating func deleteItems<C: Collection>(withIDs itemIDs: C) where C.Element == ItemID {
|
||||||
|
|
||||||
self.diffableSnapshot.deleteItems(itemIDs)
|
self.mutate {
|
||||||
|
|
||||||
|
$0.deleteItems(itemIDs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Deletes the items at the specified index paths
|
||||||
|
|
||||||
|
- parameter itemIndexPaths: the index paths for the objects to delete. Specifying an invalid value will raise an exception.
|
||||||
|
*/
|
||||||
|
public mutating func deleteItems<C: Collection>(at itemIndexPaths: C) where C.Element == IndexPath {
|
||||||
|
|
||||||
|
self.mutate {
|
||||||
|
|
||||||
|
$0.unsafeDeleteItems(at: itemIndexPaths)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -508,7 +591,10 @@ public struct ListSnapshot<O: DynamicObject>: RandomAccessCollection, Hashable {
|
|||||||
*/
|
*/
|
||||||
public mutating func deleteAllItems() {
|
public mutating func deleteAllItems() {
|
||||||
|
|
||||||
self.diffableSnapshot.deleteAllItems()
|
self.mutate {
|
||||||
|
|
||||||
|
$0.deleteAllItems()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -517,9 +603,15 @@ public struct ListSnapshot<O: DynamicObject>: RandomAccessCollection, Hashable {
|
|||||||
- parameter itemID: an object identifier in the list to move. Specifying an invalid value will raise an exception.
|
- parameter itemID: an object identifier in the list to move. Specifying an invalid value will raise an exception.
|
||||||
- parameter beforeItemID: another identifier to move the item before of. Specifying an invalid value will raise an exception.
|
- parameter beforeItemID: another identifier to move the item before of. Specifying an invalid value will raise an exception.
|
||||||
*/
|
*/
|
||||||
public mutating func moveItem(withID itemID: ItemID, beforeItemID: ItemID) {
|
public mutating func moveItem(
|
||||||
|
withID itemID: ItemID,
|
||||||
|
beforeItemID: ItemID
|
||||||
|
) {
|
||||||
|
|
||||||
self.diffableSnapshot.moveItem(itemID, beforeItem: beforeItemID)
|
self.mutate {
|
||||||
|
|
||||||
|
$0.moveItem(itemID, beforeItem: beforeItemID)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -528,9 +620,32 @@ public struct ListSnapshot<O: DynamicObject>: RandomAccessCollection, Hashable {
|
|||||||
- parameter itemID: an object identifier in the list to move. Specifying an invalid value will raise an exception.
|
- parameter itemID: an object identifier in the list to move. Specifying an invalid value will raise an exception.
|
||||||
- parameter beforeItemID: another identifier to move the item after of. Specifying an invalid value will raise an exception.
|
- parameter beforeItemID: another identifier to move the item after of. Specifying an invalid value will raise an exception.
|
||||||
*/
|
*/
|
||||||
public mutating func moveItem(withID itemID: ItemID, afterItemID: ItemID) {
|
public mutating func moveItem(
|
||||||
|
withID itemID: ItemID,
|
||||||
|
afterItemID: ItemID
|
||||||
|
) {
|
||||||
|
|
||||||
self.diffableSnapshot.moveItem(itemID, afterItem: afterItemID)
|
self.mutate {
|
||||||
|
|
||||||
|
$0.moveItem(itemID, afterItem: afterItemID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Moves an item at an index path to a new index path
|
||||||
|
|
||||||
|
- parameter itemIndexPath: an index path in the list to move. Specifying an invalid value will raise an exception.
|
||||||
|
- parameter newIndexPath: the new index path to move the item into. Specifying an invalid value will raise an exception.
|
||||||
|
*/
|
||||||
|
public mutating func moveItem(
|
||||||
|
at itemIndexPath: IndexPath,
|
||||||
|
to newIndexPath: IndexPath
|
||||||
|
) {
|
||||||
|
|
||||||
|
self.mutate {
|
||||||
|
|
||||||
|
$0.unsafeMoveItem(at: itemIndexPath, to: newIndexPath)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -538,9 +653,25 @@ public struct ListSnapshot<O: DynamicObject>: RandomAccessCollection, Hashable {
|
|||||||
|
|
||||||
- parameter itemIDs: the object identifiers to reload
|
- parameter itemIDs: the object identifiers to reload
|
||||||
*/
|
*/
|
||||||
public mutating func reloadItems<S: Sequence>(withIDs itemIDs: S) where S.Element == ItemID {
|
public mutating func reloadItems<C: Collection>(withIDs itemIDs: C) where C.Element == ItemID {
|
||||||
|
|
||||||
self.diffableSnapshot.reloadItems(itemIDs)
|
self.mutate {
|
||||||
|
|
||||||
|
$0.reloadItems(itemIDs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Marks the specified index paths as reloaded
|
||||||
|
|
||||||
|
- parameter itemIndexPaths: the index paths to reload. Specifying an invalid value will raise an exception.
|
||||||
|
*/
|
||||||
|
public mutating func reloadItems<C: Collection>(at itemIndexPaths: C) where C.Element == IndexPath {
|
||||||
|
|
||||||
|
self.mutate {
|
||||||
|
|
||||||
|
$0.unsafeReloadItems(at: itemIndexPaths)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -550,7 +681,10 @@ public struct ListSnapshot<O: DynamicObject>: RandomAccessCollection, Hashable {
|
|||||||
*/
|
*/
|
||||||
public mutating func appendSections<C: Collection>(withIDs sectionIDs: C) where C.Element == SectionID {
|
public mutating func appendSections<C: Collection>(withIDs sectionIDs: C) where C.Element == SectionID {
|
||||||
|
|
||||||
self.diffableSnapshot.appendSections(sectionIDs)
|
self.mutate {
|
||||||
|
|
||||||
|
$0.appendSections(sectionIDs)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -559,9 +693,15 @@ public struct ListSnapshot<O: DynamicObject>: RandomAccessCollection, Hashable {
|
|||||||
- parameter sectionIDs: the section identifiers for the sections to insert
|
- parameter sectionIDs: the section identifiers for the sections to insert
|
||||||
- parameter beforeSectionID: an existing identifier to insert items before of. Specifying an invalid value will raise an exception.
|
- parameter beforeSectionID: an existing identifier to insert items before of. Specifying an invalid value will raise an exception.
|
||||||
*/
|
*/
|
||||||
public mutating func insertSections<C: Collection>(withIDs sectionIDs: C, beforeSectionID: SectionID) where C.Element == SectionID {
|
public mutating func insertSections<C: Collection>(
|
||||||
|
withIDs sectionIDs: C,
|
||||||
|
beforeSectionID: SectionID
|
||||||
|
) where C.Element == SectionID {
|
||||||
|
|
||||||
self.diffableSnapshot.insertSections(sectionIDs, beforeSection: beforeSectionID)
|
self.mutate {
|
||||||
|
|
||||||
|
$0.insertSections(sectionIDs, beforeSection: beforeSectionID)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -570,9 +710,32 @@ public struct ListSnapshot<O: DynamicObject>: RandomAccessCollection, Hashable {
|
|||||||
- parameter sectionIDs: the section identifiers for the sections to insert
|
- parameter sectionIDs: the section identifiers for the sections to insert
|
||||||
- parameter beforeSectionID: an existing identifier to insert items after of. Specifying an invalid value will raise an exception.
|
- parameter beforeSectionID: an existing identifier to insert items after of. Specifying an invalid value will raise an exception.
|
||||||
*/
|
*/
|
||||||
public mutating func insertSections<C: Collection>(withIDs sectionIDs: C, afterSectionID: SectionID) where C.Element == SectionID {
|
public mutating func insertSections<C: Collection>(
|
||||||
|
withIDs sectionIDs: C,
|
||||||
|
afterSectionID: SectionID
|
||||||
|
) where C.Element == SectionID {
|
||||||
|
|
||||||
self.diffableSnapshot.insertSections(sectionIDs, afterSection: afterSectionID)
|
self.mutate {
|
||||||
|
|
||||||
|
$0.insertSections(sectionIDs, afterSection: afterSectionID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Inserts new sections into an existing section index
|
||||||
|
|
||||||
|
- parameter sectionIDs: the section identifiers for the sections to insert
|
||||||
|
- parameter sectionIndex: an existing section index to insert items into. Specifying an invalid value will raise an exception.
|
||||||
|
*/
|
||||||
|
public mutating func insertSections<C: Collection>(
|
||||||
|
_ sectionIDs: C,
|
||||||
|
at sectionIndex: Int
|
||||||
|
) where C.Element == String {
|
||||||
|
|
||||||
|
self.mutate {
|
||||||
|
|
||||||
|
$0.unsafeInsertSections(sectionIDs, at: sectionIndex)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -580,9 +743,25 @@ public struct ListSnapshot<O: DynamicObject>: RandomAccessCollection, Hashable {
|
|||||||
|
|
||||||
- parameter sectionIDs: the section identifiers for the sections to delete
|
- parameter sectionIDs: the section identifiers for the sections to delete
|
||||||
*/
|
*/
|
||||||
public mutating func deleteSections<S: Sequence>(withIDs sectionIDs: S) where S.Element == SectionID {
|
public mutating func deleteSections<C: Collection>(withIDs sectionIDs: C) where C.Element == SectionID {
|
||||||
|
|
||||||
self.diffableSnapshot.deleteSections(sectionIDs)
|
self.mutate {
|
||||||
|
|
||||||
|
$0.deleteSections(sectionIDs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Deletes the specified section indices
|
||||||
|
|
||||||
|
- parameter sectionIndices: the section indices to delete. Specifying an invalid value will raise an exception.
|
||||||
|
*/
|
||||||
|
public mutating func deleteSections<C: Collection>(at sectionIndices: C) where C.Element == Int {
|
||||||
|
|
||||||
|
self.mutate {
|
||||||
|
|
||||||
|
$0.unsafeDeleteSections(at: sectionIndices)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -591,9 +770,15 @@ public struct ListSnapshot<O: DynamicObject>: RandomAccessCollection, Hashable {
|
|||||||
- parameter sectionID: a section identifier in the list to move. Specifying an invalid value will raise an exception.
|
- parameter sectionID: a section identifier in the list to move. Specifying an invalid value will raise an exception.
|
||||||
- parameter beforeSectionID: another identifier to move the section before of. Specifying an invalid value will raise an exception.
|
- parameter beforeSectionID: another identifier to move the section before of. Specifying an invalid value will raise an exception.
|
||||||
*/
|
*/
|
||||||
public mutating func moveSection(withID sectionID: SectionID, beforeSectionID: SectionID) {
|
public mutating func moveSection(
|
||||||
|
withID sectionID: SectionID,
|
||||||
|
beforeSectionID: SectionID
|
||||||
|
) {
|
||||||
|
|
||||||
self.diffableSnapshot.moveSection(sectionID, beforeSection: beforeSectionID)
|
self.mutate {
|
||||||
|
|
||||||
|
$0.moveSection(sectionID, beforeSection: beforeSectionID)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -602,9 +787,32 @@ public struct ListSnapshot<O: DynamicObject>: RandomAccessCollection, Hashable {
|
|||||||
- parameter sectionID: a section identifier in the list to move. Specifying an invalid value will raise an exception.
|
- parameter sectionID: a section identifier in the list to move. Specifying an invalid value will raise an exception.
|
||||||
- parameter afterSectionID: another identifier to move the section after of. Specifying an invalid value will raise an exception.
|
- parameter afterSectionID: another identifier to move the section after of. Specifying an invalid value will raise an exception.
|
||||||
*/
|
*/
|
||||||
public mutating func moveSection(withID sectionID: SectionID, afterSectionID: SectionID) {
|
public mutating func moveSection(
|
||||||
|
withID sectionID: SectionID,
|
||||||
|
afterSectionID: SectionID
|
||||||
|
) {
|
||||||
|
|
||||||
self.diffableSnapshot.moveSection(sectionID, afterSection: afterSectionID)
|
self.mutate {
|
||||||
|
|
||||||
|
$0.moveSection(sectionID, afterSection: afterSectionID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Moves a section at a specified index to a new index
|
||||||
|
|
||||||
|
- parameter sectionIndex: a section index in the list to move. Specifying an invalid value will raise an exception.
|
||||||
|
- parameter newSectionIndex: the new section index to move into. Specifying an invalid value will raise an exception.
|
||||||
|
*/
|
||||||
|
public mutating func moveSection(
|
||||||
|
at sectionIndex: Int,
|
||||||
|
to newSectionIndex: Int
|
||||||
|
) {
|
||||||
|
|
||||||
|
self.mutate {
|
||||||
|
|
||||||
|
$0.unsafeMoveSection(at: sectionIndex, to: newSectionIndex)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -612,9 +820,25 @@ public struct ListSnapshot<O: DynamicObject>: RandomAccessCollection, Hashable {
|
|||||||
|
|
||||||
- parameter sectionIDs: the section identifiers to reload
|
- parameter sectionIDs: the section identifiers to reload
|
||||||
*/
|
*/
|
||||||
public mutating func reloadSections<S: Sequence>(withIDs sectionIDs: S) where S.Element == SectionID {
|
public mutating func reloadSections<C: Collection>(withIDs sectionIDs: C) where C.Element == SectionID {
|
||||||
|
|
||||||
self.diffableSnapshot.reloadSections(sectionIDs)
|
self.mutate {
|
||||||
|
|
||||||
|
$0.reloadSections(sectionIDs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Marks the specified section indices as reloaded
|
||||||
|
|
||||||
|
- parameter sectionIndices: the section indices to reload. Specifying an invalid value will raise an exception.
|
||||||
|
*/
|
||||||
|
public mutating func reloadSections<C: Collection>(at sectionIndices: C) where C.Element == Int {
|
||||||
|
|
||||||
|
self.mutate {
|
||||||
|
|
||||||
|
$0.unsafeReloadSections(at: sectionIndices)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -696,16 +920,18 @@ public struct ListSnapshot<O: DynamicObject>: RandomAccessCollection, Hashable {
|
|||||||
// MARK: Equatable
|
// MARK: Equatable
|
||||||
|
|
||||||
public static func == (_ lhs: Self, _ rhs: Self) -> Bool {
|
public static func == (_ lhs: Self, _ rhs: Self) -> Bool {
|
||||||
|
|
||||||
return lhs.id == rhs.id
|
return lhs.context == rhs.context
|
||||||
|
&& lhs.generation == rhs.generation
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// MARK: Hashable
|
// MARK: Hashable
|
||||||
|
|
||||||
public func hash(into hasher: inout Hasher) {
|
public func hash(into hasher: inout Hasher) {
|
||||||
|
|
||||||
hasher.combine(self.id)
|
hasher.combine(self.context)
|
||||||
|
hasher.combine(self.generation)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -732,6 +958,12 @@ public struct ListSnapshot<O: DynamicObject>: RandomAccessCollection, Hashable {
|
|||||||
|
|
||||||
// MARK: Private
|
// MARK: Private
|
||||||
|
|
||||||
private let id: UUID = .init()
|
private var generation: UUID = .init()
|
||||||
|
|
||||||
|
@inline(__always)
|
||||||
|
private mutating func mutate<T>(_ mutation: (inout Internals.DiffableDataSourceSnapshot) -> T) -> T {
|
||||||
|
|
||||||
|
self.generation = .init()
|
||||||
|
return mutation(&self.diffableSnapshot)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,6 +113,10 @@ extension NSManagedObjectContext {
|
|||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let saveMetadata = rootContext.saveMetadata
|
||||||
|
context.saveMetadata = saveMetadata
|
||||||
|
|
||||||
let mergeChanges = { () -> Void in
|
let mergeChanges = { () -> Void in
|
||||||
|
|
||||||
if let updatedObjects = (note.userInfo?[NSUpdatedObjectsKey] as? Set<NSManagedObject>) {
|
if let updatedObjects = (note.userInfo?[NSUpdatedObjectsKey] as? Set<NSManagedObject>) {
|
||||||
@@ -123,8 +127,9 @@ extension NSManagedObjectContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
context.mergeChanges(fromContextDidSave: note)
|
context.mergeChanges(fromContextDidSave: note)
|
||||||
|
context.saveMetadata = nil
|
||||||
}
|
}
|
||||||
if rootContext.isSavingSynchronously == true {
|
if case true? = saveMetadata?.isSavingSynchronously {
|
||||||
|
|
||||||
context.performAndWait(mergeChanges)
|
context.performAndWait(mergeChanges)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,21 +54,21 @@ extension NSManagedObjectContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@nonobjc
|
@nonobjc
|
||||||
internal var isSavingSynchronously: Bool? {
|
internal var saveMetadata: SaveMetadata? {
|
||||||
|
|
||||||
get {
|
get {
|
||||||
|
|
||||||
let value: NSNumber? = Internals.getAssociatedObjectForKey(
|
let value: SaveMetadata? = Internals.getAssociatedObjectForKey(
|
||||||
&PropertyKeys.isSavingSynchronously,
|
&PropertyKeys.saveMetadata,
|
||||||
inObject: self
|
inObject: self
|
||||||
)
|
)
|
||||||
return value?.boolValue
|
return value
|
||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
|
|
||||||
Internals.setAssociatedWeakObject(
|
Internals.setAssociatedRetainedObject(
|
||||||
newValue.flatMap { NSNumber(value: $0) },
|
newValue,
|
||||||
forKey: &PropertyKeys.isSavingSynchronously,
|
forKey: &PropertyKeys.saveMetadata,
|
||||||
inObject: self
|
inObject: self
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -140,7 +140,10 @@ extension NSManagedObjectContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@nonobjc
|
@nonobjc
|
||||||
internal func saveSynchronously(waitForMerge: Bool) -> (hasChanges: Bool, error: CoreStoreError?) {
|
internal func saveSynchronously(
|
||||||
|
waitForMerge: Bool,
|
||||||
|
sourceIdentifier: Any?
|
||||||
|
) -> (hasChanges: Bool, error: CoreStoreError?) {
|
||||||
|
|
||||||
var result: (hasChanges: Bool, error: CoreStoreError?) = (false, nil)
|
var result: (hasChanges: Bool, error: CoreStoreError?) = (false, nil)
|
||||||
self.performAndWait {
|
self.performAndWait {
|
||||||
@@ -151,9 +154,12 @@ extension NSManagedObjectContext {
|
|||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
|
|
||||||
self.isSavingSynchronously = waitForMerge
|
self.saveMetadata = .init(
|
||||||
|
isSavingSynchronously: waitForMerge,
|
||||||
|
sourceIdentifier: sourceIdentifier
|
||||||
|
)
|
||||||
try self.save()
|
try self.save()
|
||||||
self.isSavingSynchronously = nil
|
self.saveMetadata = nil
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
|
|
||||||
@@ -167,7 +173,10 @@ extension NSManagedObjectContext {
|
|||||||
}
|
}
|
||||||
if let parentContext = self.parent, self.shouldCascadeSavesToParent {
|
if let parentContext = self.parent, self.shouldCascadeSavesToParent {
|
||||||
|
|
||||||
let (_, error) = parentContext.saveSynchronously(waitForMerge: waitForMerge)
|
let (_, error) = parentContext.saveSynchronously(
|
||||||
|
waitForMerge: waitForMerge,
|
||||||
|
sourceIdentifier: sourceIdentifier
|
||||||
|
)
|
||||||
result = (true, error)
|
result = (true, error)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -179,7 +188,10 @@ extension NSManagedObjectContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@nonobjc
|
@nonobjc
|
||||||
internal func saveAsynchronouslyWithCompletion(_ completion: @escaping (_ hasChanges: Bool, _ error: CoreStoreError?) -> Void = { (_, _) in }) {
|
internal func saveAsynchronously(
|
||||||
|
sourceIdentifier: Any?,
|
||||||
|
completion: @escaping (_ hasChanges: Bool, _ error: CoreStoreError?) -> Void = { (_, _) in }
|
||||||
|
) {
|
||||||
|
|
||||||
self.perform {
|
self.perform {
|
||||||
|
|
||||||
@@ -193,9 +205,12 @@ extension NSManagedObjectContext {
|
|||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
|
|
||||||
self.isSavingSynchronously = false
|
self.saveMetadata = .init(
|
||||||
|
isSavingSynchronously: false,
|
||||||
|
sourceIdentifier: sourceIdentifier
|
||||||
|
)
|
||||||
try self.save()
|
try self.save()
|
||||||
self.isSavingSynchronously = nil
|
self.saveMetadata = nil
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
|
|
||||||
@@ -212,10 +227,13 @@ extension NSManagedObjectContext {
|
|||||||
}
|
}
|
||||||
if self.shouldCascadeSavesToParent, let parentContext = self.parent {
|
if self.shouldCascadeSavesToParent, let parentContext = self.parent {
|
||||||
|
|
||||||
parentContext.saveAsynchronouslyWithCompletion { (_, error) in
|
parentContext.saveAsynchronously(
|
||||||
|
sourceIdentifier: sourceIdentifier,
|
||||||
completion(true, error)
|
completion: { (_, error) in
|
||||||
}
|
|
||||||
|
completion(true, error)
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
||||||
@@ -234,12 +252,32 @@ extension NSManagedObjectContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - SaveMetadata
|
||||||
|
|
||||||
|
internal final class SaveMetadata {
|
||||||
|
|
||||||
|
// MARK: Internal
|
||||||
|
|
||||||
|
internal let isSavingSynchronously: Bool
|
||||||
|
internal let sourceIdentifier: Any?
|
||||||
|
|
||||||
|
internal init(
|
||||||
|
isSavingSynchronously: Bool,
|
||||||
|
sourceIdentifier: Any?
|
||||||
|
) {
|
||||||
|
|
||||||
|
self.isSavingSynchronously = isSavingSynchronously
|
||||||
|
self.sourceIdentifier = sourceIdentifier
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// MARK: Private
|
// MARK: Private
|
||||||
|
|
||||||
private struct PropertyKeys {
|
private struct PropertyKeys {
|
||||||
|
|
||||||
static var parentTransaction: Void?
|
static var parentTransaction: Void?
|
||||||
static var isSavingSynchronously: Void?
|
static var saveMetadata: Void?
|
||||||
static var isTransactionContext: Void?
|
static var isTransactionContext: Void?
|
||||||
static var isDataStackContext: Void?
|
static var isDataStackContext: Void?
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,15 +78,28 @@ public final class ObjectMonitor<O: DynamicObject>: Hashable, ObjectRepresentati
|
|||||||
observer,
|
observer,
|
||||||
willChangeObject: { (observer, monitor, object) in
|
willChangeObject: { (observer, monitor, object) in
|
||||||
|
|
||||||
observer.objectMonitor(monitor, willUpdateObject: object)
|
observer.objectMonitor(
|
||||||
|
monitor,
|
||||||
|
willUpdateObject: object,
|
||||||
|
sourceIdentifier: monitor.context.saveMetadata?.sourceIdentifier
|
||||||
|
)
|
||||||
},
|
},
|
||||||
didDeleteObject: { (observer, monitor, object) in
|
didDeleteObject: { (observer, monitor, object) in
|
||||||
|
|
||||||
observer.objectMonitor(monitor, didDeleteObject: object)
|
observer.objectMonitor(
|
||||||
|
monitor,
|
||||||
|
didDeleteObject: object,
|
||||||
|
sourceIdentifier: monitor.context.saveMetadata?.sourceIdentifier
|
||||||
|
)
|
||||||
},
|
},
|
||||||
didUpdateObject: { (observer, monitor, object, changedPersistentKeys) in
|
didUpdateObject: { (observer, monitor, object, changedPersistentKeys) in
|
||||||
|
|
||||||
observer.objectMonitor(monitor, didUpdateObject: object, changedPersistentKeys: changedPersistentKeys)
|
observer.objectMonitor(
|
||||||
|
monitor,
|
||||||
|
didUpdateObject: object,
|
||||||
|
changedPersistentKeys: changedPersistentKeys,
|
||||||
|
sourceIdentifier: monitor.context.saveMetadata?.sourceIdentifier
|
||||||
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -197,7 +210,10 @@ public final class ObjectMonitor<O: DynamicObject>: Hashable, ObjectRepresentati
|
|||||||
|
|
||||||
// MARK: Internal
|
// MARK: Internal
|
||||||
|
|
||||||
internal init(objectID: O.ObjectID, context: NSManagedObjectContext) {
|
internal init(
|
||||||
|
objectID: O.ObjectID,
|
||||||
|
context: NSManagedObjectContext
|
||||||
|
) {
|
||||||
|
|
||||||
let fetchRequest = Internals.CoreStoreFetchRequest<NSManagedObject>()
|
let fetchRequest = Internals.CoreStoreFetchRequest<NSManagedObject>()
|
||||||
fetchRequest.entity = objectID.entity
|
fetchRequest.entity = objectID.entity
|
||||||
@@ -227,7 +243,25 @@ public final class ObjectMonitor<O: DynamicObject>: Hashable, ObjectRepresentati
|
|||||||
self.lastCommittedAttributes = (self.object?.cs_toRaw().committedValues(forKeys: nil) as? [String: NSObject]) ?? [:]
|
self.lastCommittedAttributes = (self.object?.cs_toRaw().committedValues(forKeys: nil) as? [String: NSObject]) ?? [:]
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func registerObserver<U: AnyObject>(_ observer: U, willChangeObject: @escaping (_ observer: U, _ monitor: ObjectMonitor<O>, _ object: O) -> Void, didDeleteObject: @escaping (_ observer: U, _ monitor: ObjectMonitor<O>, _ object: O) -> Void, didUpdateObject: @escaping (_ observer: U, _ monitor: ObjectMonitor<O>, _ object: O, _ changedPersistentKeys: Set<String>) -> Void) {
|
internal func registerObserver<U: AnyObject>(
|
||||||
|
_ observer: U,
|
||||||
|
willChangeObject: @escaping (
|
||||||
|
_ observer: U,
|
||||||
|
_ monitor: ObjectMonitor<O>,
|
||||||
|
_ object: O
|
||||||
|
) -> Void,
|
||||||
|
didDeleteObject: @escaping (
|
||||||
|
_ observer: U,
|
||||||
|
_ monitor: ObjectMonitor<O>,
|
||||||
|
_ object: O
|
||||||
|
) -> Void,
|
||||||
|
didUpdateObject: @escaping (
|
||||||
|
_ observer: U,
|
||||||
|
_ monitor: ObjectMonitor<O>,
|
||||||
|
_ object: O,
|
||||||
|
_ changedPersistentKeys: Set<String>
|
||||||
|
) -> Void
|
||||||
|
) {
|
||||||
|
|
||||||
Internals.assert(
|
Internals.assert(
|
||||||
Thread.isMainThread,
|
Thread.isMainThread,
|
||||||
@@ -323,7 +357,12 @@ public final class ObjectMonitor<O: DynamicObject>: Hashable, ObjectRepresentati
|
|||||||
return self.fetchedResultsController.managedObjectContext
|
return self.fetchedResultsController.managedObjectContext
|
||||||
}
|
}
|
||||||
|
|
||||||
private func registerChangeNotification(_ notificationKey: UnsafeRawPointer, name: Notification.Name, toObserver observer: AnyObject, callback: @escaping (_ monitor: ObjectMonitor<O>) -> Void) {
|
private func registerChangeNotification(
|
||||||
|
_ notificationKey: UnsafeRawPointer,
|
||||||
|
name: Notification.Name,
|
||||||
|
toObserver observer: AnyObject,
|
||||||
|
callback: @escaping (_ monitor: ObjectMonitor<O>) -> Void
|
||||||
|
) {
|
||||||
|
|
||||||
Internals.setAssociatedRetainedObject(
|
Internals.setAssociatedRetainedObject(
|
||||||
Internals.NotificationObserver(
|
Internals.NotificationObserver(
|
||||||
@@ -343,7 +382,12 @@ public final class ObjectMonitor<O: DynamicObject>: Hashable, ObjectRepresentati
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func registerObjectNotification(_ notificationKey: UnsafeRawPointer, name: Notification.Name, toObserver observer: AnyObject, callback: @escaping (_ monitor: ObjectMonitor<O>, _ object: O) -> Void) {
|
private func registerObjectNotification(
|
||||||
|
_ notificationKey: UnsafeRawPointer,
|
||||||
|
name: Notification.Name,
|
||||||
|
toObserver observer: AnyObject,
|
||||||
|
callback: @escaping (_ monitor: ObjectMonitor<O>, _ object: O) -> Void
|
||||||
|
) {
|
||||||
|
|
||||||
Internals.setAssociatedRetainedObject(
|
Internals.setAssociatedRetainedObject(
|
||||||
Internals.NotificationObserver(
|
Internals.NotificationObserver(
|
||||||
@@ -384,7 +428,9 @@ extension ObjectMonitor: FetchedResultsControllerHandler {
|
|||||||
return { _ in nil }
|
return { _ in nil }
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
|
internal func controllerWillChangeContent(
|
||||||
|
_ controller: NSFetchedResultsController<NSFetchRequestResult>
|
||||||
|
) {
|
||||||
|
|
||||||
NotificationCenter.default.post(
|
NotificationCenter.default.post(
|
||||||
name: Notification.Name.objectMonitorWillChangeObject,
|
name: Notification.Name.objectMonitorWillChangeObject,
|
||||||
@@ -392,9 +438,17 @@ extension ObjectMonitor: FetchedResultsControllerHandler {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) { }
|
internal func controllerDidChangeContent(
|
||||||
|
_ controller: NSFetchedResultsController<NSFetchRequestResult>
|
||||||
|
) {}
|
||||||
|
|
||||||
internal func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChangeObject anObject: Any, atIndexPath indexPath: IndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
|
internal func controller(
|
||||||
|
_ controller: NSFetchedResultsController<NSFetchRequestResult>,
|
||||||
|
didChangeObject anObject: Any,
|
||||||
|
atIndexPath indexPath: IndexPath?,
|
||||||
|
forChangeType type: NSFetchedResultsChangeType,
|
||||||
|
newIndexPath: IndexPath?
|
||||||
|
) {
|
||||||
|
|
||||||
switch type {
|
switch type {
|
||||||
|
|
||||||
@@ -418,7 +472,12 @@ extension ObjectMonitor: FetchedResultsControllerHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) { }
|
internal func controller(
|
||||||
|
_ controller: NSFetchedResultsController<NSFetchRequestResult>,
|
||||||
|
didChangeSection sectionInfo: NSFetchedResultsSectionInfo,
|
||||||
|
atIndex sectionIndex: Int,
|
||||||
|
forChangeType type: NSFetchedResultsChangeType
|
||||||
|
) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -49,8 +49,41 @@ public protocol ObjectObserver: AnyObject {
|
|||||||
|
|
||||||
- parameter monitor: the `ObjectMonitor` monitoring the object being observed
|
- parameter monitor: the `ObjectMonitor` monitoring the object being observed
|
||||||
- parameter object: the `DynamicObject` instance being observed
|
- parameter object: the `DynamicObject` instance being observed
|
||||||
|
- parameter sourceIdentifier: an optional identifier provided by the transaction source
|
||||||
*/
|
*/
|
||||||
func objectMonitor(_ monitor: ObjectMonitor<ObjectEntityType>, willUpdateObject object: ObjectEntityType)
|
func objectMonitor(
|
||||||
|
_ monitor: ObjectMonitor<ObjectEntityType>,
|
||||||
|
willUpdateObject object: ObjectEntityType,
|
||||||
|
sourceIdentifier: Any?
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
Handles processing just before a change to the observed `object` occurs. (Optional)
|
||||||
|
The default implementation does nothing.
|
||||||
|
|
||||||
|
- parameter monitor: the `ObjectMonitor` monitoring the object being observed
|
||||||
|
- parameter object: the `DynamicObject` instance being observed
|
||||||
|
*/
|
||||||
|
func objectMonitor(
|
||||||
|
_ monitor: ObjectMonitor<ObjectEntityType>,
|
||||||
|
willUpdateObject object: ObjectEntityType
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
Handles processing right after a change to the observed `object` occurs. (Optional)
|
||||||
|
The default implementation does nothing.
|
||||||
|
|
||||||
|
- parameter monitor: the `ObjectMonitor` monitoring the object being observed
|
||||||
|
- parameter object: the `DynamicObject` instance being observed
|
||||||
|
- parameter changedPersistentKeys: a `Set` of key paths for the attributes that were changed. Note that `changedPersistentKeys` only contains keys for attributes/relationships present in the persistent store, thus transient properties will not be reported.
|
||||||
|
- parameter sourceIdentifier: an optional identifier provided by the transaction source
|
||||||
|
*/
|
||||||
|
func objectMonitor(
|
||||||
|
_ monitor: ObjectMonitor<ObjectEntityType>,
|
||||||
|
didUpdateObject object: ObjectEntityType,
|
||||||
|
changedPersistentKeys: Set<KeyPathString>,
|
||||||
|
sourceIdentifier: Any?
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Handles processing right after a change to the observed `object` occurs. (Optional)
|
Handles processing right after a change to the observed `object` occurs. (Optional)
|
||||||
@@ -60,7 +93,25 @@ public protocol ObjectObserver: AnyObject {
|
|||||||
- parameter object: the `DynamicObject` instance being observed
|
- parameter object: the `DynamicObject` instance being observed
|
||||||
- parameter changedPersistentKeys: a `Set` of key paths for the attributes that were changed. Note that `changedPersistentKeys` only contains keys for attributes/relationships present in the persistent store, thus transient properties will not be reported.
|
- parameter changedPersistentKeys: a `Set` of key paths for the attributes that were changed. Note that `changedPersistentKeys` only contains keys for attributes/relationships present in the persistent store, thus transient properties will not be reported.
|
||||||
*/
|
*/
|
||||||
func objectMonitor(_ monitor: ObjectMonitor<ObjectEntityType>, didUpdateObject object: ObjectEntityType, changedPersistentKeys: Set<KeyPathString>)
|
func objectMonitor(
|
||||||
|
_ monitor: ObjectMonitor<ObjectEntityType>,
|
||||||
|
didUpdateObject object: ObjectEntityType,
|
||||||
|
changedPersistentKeys: Set<KeyPathString>
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
Handles processing right after `object` is deleted. (Optional)
|
||||||
|
The default implementation does nothing.
|
||||||
|
|
||||||
|
- parameter monitor: the `ObjectMonitor` monitoring the object being observed
|
||||||
|
- parameter object: the `DynamicObject` instance being observed
|
||||||
|
- parameter sourceIdentifier: an optional identifier provided by the transaction source
|
||||||
|
*/
|
||||||
|
func objectMonitor(
|
||||||
|
_ monitor: ObjectMonitor<ObjectEntityType>,
|
||||||
|
didDeleteObject object: ObjectEntityType,
|
||||||
|
sourceIdentifier: Any?
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Handles processing right after `object` is deleted. (Optional)
|
Handles processing right after `object` is deleted. (Optional)
|
||||||
@@ -69,7 +120,10 @@ public protocol ObjectObserver: AnyObject {
|
|||||||
- parameter monitor: the `ObjectMonitor` monitoring the object being observed
|
- parameter monitor: the `ObjectMonitor` monitoring the object being observed
|
||||||
- parameter object: the `DynamicObject` instance being observed
|
- parameter object: the `DynamicObject` instance being observed
|
||||||
*/
|
*/
|
||||||
func objectMonitor(_ monitor: ObjectMonitor<ObjectEntityType>, didDeleteObject object: ObjectEntityType)
|
func objectMonitor(
|
||||||
|
_ monitor: ObjectMonitor<ObjectEntityType>,
|
||||||
|
didDeleteObject object: ObjectEntityType
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -77,9 +131,57 @@ public protocol ObjectObserver: AnyObject {
|
|||||||
|
|
||||||
extension ObjectObserver {
|
extension ObjectObserver {
|
||||||
|
|
||||||
public func objectMonitor(_ monitor: ObjectMonitor<ObjectEntityType>, willUpdateObject object: ObjectEntityType) { }
|
public func objectMonitor(
|
||||||
|
_ monitor: ObjectMonitor<ObjectEntityType>,
|
||||||
|
willUpdateObject object: ObjectEntityType,
|
||||||
|
sourceIdentifier: Any?
|
||||||
|
) {
|
||||||
|
|
||||||
|
self.objectMonitor(
|
||||||
|
monitor,
|
||||||
|
willUpdateObject: object
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
public func objectMonitor(_ monitor: ObjectMonitor<ObjectEntityType>, didUpdateObject object: ObjectEntityType, changedPersistentKeys: Set<KeyPathString>) { }
|
public func objectMonitor(
|
||||||
|
_ monitor: ObjectMonitor<ObjectEntityType>,
|
||||||
|
willUpdateObject object: ObjectEntityType
|
||||||
|
) {}
|
||||||
|
|
||||||
public func objectMonitor(_ monitor: ObjectMonitor<ObjectEntityType>, didDeleteObject object: ObjectEntityType) { }
|
public func objectMonitor(
|
||||||
|
_ monitor: ObjectMonitor<ObjectEntityType>,
|
||||||
|
didUpdateObject object: ObjectEntityType,
|
||||||
|
changedPersistentKeys: Set<KeyPathString>,
|
||||||
|
sourceIdentifier: Any?
|
||||||
|
) {
|
||||||
|
|
||||||
|
self.objectMonitor(
|
||||||
|
monitor,
|
||||||
|
didUpdateObject: object,
|
||||||
|
changedPersistentKeys: changedPersistentKeys
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func objectMonitor(
|
||||||
|
_ monitor: ObjectMonitor<ObjectEntityType>,
|
||||||
|
didUpdateObject object: ObjectEntityType,
|
||||||
|
changedPersistentKeys: Set<KeyPathString>
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public func objectMonitor(
|
||||||
|
_ monitor: ObjectMonitor<ObjectEntityType>,
|
||||||
|
didDeleteObject object: ObjectEntityType,
|
||||||
|
sourceIdentifier: Any?
|
||||||
|
) {
|
||||||
|
|
||||||
|
self.objectMonitor(
|
||||||
|
monitor,
|
||||||
|
didDeleteObject: object
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func objectMonitor(
|
||||||
|
_ monitor: ObjectMonitor<ObjectEntityType>,
|
||||||
|
didDeleteObject object: ObjectEntityType
|
||||||
|
) {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
#if canImport(Combine)
|
#if canImport(Combine)
|
||||||
|
|
||||||
import Combine
|
import Combine
|
||||||
|
import CoreData
|
||||||
|
|
||||||
|
|
||||||
// MARK: - ObjectPublisher
|
// MARK: - ObjectPublisher
|
||||||
|
|||||||
@@ -135,8 +135,19 @@ extension ObjectPublisher {
|
|||||||
|
|
||||||
func cancel() {
|
func cancel() {
|
||||||
|
|
||||||
self.publisher.removeObserver(self)
|
|
||||||
self.subscriber = nil
|
self.subscriber = nil
|
||||||
|
|
||||||
|
if Thread.isMainThread {
|
||||||
|
|
||||||
|
self.publisher.removeObserver(self)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
|
||||||
|
self.publisher.removeObserver(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ public final class ObjectPublisher<O: DynamicObject>: ObjectRepresentation, Hash
|
|||||||
"Attempted to add an observer of type \(Internals.typeName(observer)) outside the main thread."
|
"Attempted to add an observer of type \(Internals.typeName(observer)) outside the main thread."
|
||||||
)
|
)
|
||||||
self.observers.setObject(
|
self.observers.setObject(
|
||||||
Internals.Closure(callback),
|
Internals.Closure({ callback($0.objectPublisher) }),
|
||||||
forKey: observer
|
forKey: observer
|
||||||
)
|
)
|
||||||
_ = self.lazySnapshot
|
_ = self.lazySnapshot
|
||||||
@@ -97,6 +97,46 @@ public final class ObjectPublisher<O: DynamicObject>: ObjectRepresentation, Hash
|
|||||||
callback(self)
|
callback(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Registers an object as an observer to be notified when changes to the `ObjectPublisher`'s snapshot occur.
|
||||||
|
|
||||||
|
To prevent retain-cycles, `ObjectPublisher` only keeps `weak` references to its observers.
|
||||||
|
|
||||||
|
For thread safety, this method needs to be called from the main thread. An assertion failure will occur (on debug builds only) if called from any thread other than the main thread.
|
||||||
|
|
||||||
|
Calling `addObserver(_:_:)` multiple times on the same observer is safe.
|
||||||
|
|
||||||
|
- parameter observer: an object to become owner of the specified `callback`
|
||||||
|
- parameter notifyInitial: if `true`, the callback is executed immediately with the current publisher state. Otherwise only succeeding updates will notify the observer. Default value is `false`.
|
||||||
|
- parameter initialSourceIdentifier: an optional value that identifies the initial callback invocation if `notifyInitial` is `true`.
|
||||||
|
- parameter callback: the closure to execute when changes occur
|
||||||
|
*/
|
||||||
|
public func addObserver<T: AnyObject>(
|
||||||
|
_ observer: T,
|
||||||
|
notifyInitial: Bool = false,
|
||||||
|
initialSourceIdentifier: Any? = nil,
|
||||||
|
_ callback: @escaping (
|
||||||
|
_ objectPublisher: ObjectPublisher<O>,
|
||||||
|
_ sourceIdentifier: Any?
|
||||||
|
) -> Void
|
||||||
|
) {
|
||||||
|
|
||||||
|
Internals.assert(
|
||||||
|
Thread.isMainThread,
|
||||||
|
"Attempted to add an observer of type \(Internals.typeName(observer)) outside the main thread."
|
||||||
|
)
|
||||||
|
self.observers.setObject(
|
||||||
|
Internals.Closure(callback),
|
||||||
|
forKey: observer
|
||||||
|
)
|
||||||
|
_ = self.lazySnapshot
|
||||||
|
|
||||||
|
if notifyInitial {
|
||||||
|
|
||||||
|
callback(self, initialSourceIdentifier)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Unregisters an object from receiving notifications for changes to the `ObjectPublisher`'s snapshot.
|
Unregisters an object from receiving notifications for changes to the `ObjectPublisher`'s snapshot.
|
||||||
@@ -215,6 +255,8 @@ public final class ObjectPublisher<O: DynamicObject>: ObjectRepresentation, Hash
|
|||||||
|
|
||||||
|
|
||||||
// MARK: FilePrivate
|
// MARK: FilePrivate
|
||||||
|
|
||||||
|
fileprivate typealias ObserverClosureType = Internals.Closure<(objectPublisher: ObjectPublisher<O>, sourceIdentifier: Any?), Void>
|
||||||
|
|
||||||
fileprivate init(objectID: O.ObjectID, context: NSManagedObjectContext, initializer: @escaping (NSManagedObjectID, NSManagedObjectContext) -> ObjectSnapshot<O>?) {
|
fileprivate init(objectID: O.ObjectID, context: NSManagedObjectContext, initializer: @escaping (NSManagedObjectID, NSManagedObjectContext) -> ObjectSnapshot<O>?) {
|
||||||
|
|
||||||
@@ -237,12 +279,12 @@ public final class ObjectPublisher<O: DynamicObject>: ObjectRepresentation, Hash
|
|||||||
self.object = nil
|
self.object = nil
|
||||||
|
|
||||||
self.$lazySnapshot.reset({ nil })
|
self.$lazySnapshot.reset({ nil })
|
||||||
self.notifyObservers()
|
self.notifyObservers(sourceIdentifier: self.context.saveMetadata)
|
||||||
}
|
}
|
||||||
else if updatedIDs.contains(objectID) {
|
else if updatedIDs.contains(objectID) {
|
||||||
|
|
||||||
self.$lazySnapshot.reset({ initializer(objectID, context) })
|
self.$lazySnapshot.reset({ initializer(objectID, context) })
|
||||||
self.notifyObservers()
|
self.notifyObservers(sourceIdentifier: self.context.saveMetadata)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return initializer(objectID, context)
|
return initializer(objectID, context)
|
||||||
@@ -258,18 +300,21 @@ public final class ObjectPublisher<O: DynamicObject>: ObjectRepresentation, Hash
|
|||||||
@Internals.LazyNonmutating(uninitialized: ())
|
@Internals.LazyNonmutating(uninitialized: ())
|
||||||
private var lazySnapshot: ObjectSnapshot<O>?
|
private var lazySnapshot: ObjectSnapshot<O>?
|
||||||
|
|
||||||
private lazy var observers: NSMapTable<AnyObject, Internals.Closure<ObjectPublisher<O>, Void>> = .weakToStrongObjects()
|
private lazy var observers: NSMapTable<AnyObject, ObserverClosureType> = .weakToStrongObjects()
|
||||||
|
|
||||||
private func notifyObservers() {
|
private func notifyObservers(sourceIdentifier: Any?) {
|
||||||
|
|
||||||
guard let enumerator = self.observers.objectEnumerator() else {
|
guard let enumerator = self.observers.objectEnumerator() else {
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
let arguments: ObserverClosureType.Arguments = (
|
||||||
|
objectPublisher: self,
|
||||||
|
sourceIdentifier: sourceIdentifier
|
||||||
|
)
|
||||||
for closure in enumerator {
|
for closure in enumerator {
|
||||||
|
|
||||||
(closure as! Internals.Closure<ObjectPublisher
|
(closure as! ObserverClosureType).invoke(with: arguments)
|
||||||
<O>, Void>).invoke(with: self)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -147,15 +147,28 @@ public final class SynchronousDataTransaction: BaseDataTransaction {
|
|||||||
|
|
||||||
// MARK: Internal
|
// MARK: Internal
|
||||||
|
|
||||||
internal init(mainContext: NSManagedObjectContext, queue: DispatchQueue) {
|
internal init(
|
||||||
|
mainContext: NSManagedObjectContext,
|
||||||
|
queue: DispatchQueue,
|
||||||
|
sourceIdentifier: Any?
|
||||||
|
) {
|
||||||
|
|
||||||
super.init(mainContext: mainContext, queue: queue, supportsUndo: false, bypassesQueueing: false)
|
super.init(
|
||||||
|
mainContext: mainContext,
|
||||||
|
queue: queue,
|
||||||
|
supportsUndo: false,
|
||||||
|
bypassesQueueing: false,
|
||||||
|
sourceIdentifier: sourceIdentifier
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func autoCommit(waitForMerge: Bool) -> (hasChanges: Bool, error: CoreStoreError?) {
|
internal func autoCommit(waitForMerge: Bool) -> (hasChanges: Bool, error: CoreStoreError?) {
|
||||||
|
|
||||||
self.isCommitted = true
|
self.isCommitted = true
|
||||||
let result = self.context.saveSynchronously(waitForMerge: waitForMerge)
|
let result = self.context.saveSynchronously(
|
||||||
|
waitForMerge: waitForMerge,
|
||||||
|
sourceIdentifier: self.sourceIdentifier
|
||||||
|
)
|
||||||
self.result = result
|
self.result = result
|
||||||
defer {
|
defer {
|
||||||
|
|
||||||
|
|||||||
@@ -43,11 +43,14 @@ public final class UnsafeDataTransaction: BaseDataTransaction {
|
|||||||
*/
|
*/
|
||||||
public func commit(_ completion: @escaping (_ error: CoreStoreError?) -> Void) {
|
public func commit(_ completion: @escaping (_ error: CoreStoreError?) -> Void) {
|
||||||
|
|
||||||
self.context.saveAsynchronouslyWithCompletion { (_, error) in
|
self.context.saveAsynchronously(
|
||||||
|
sourceIdentifier: self.sourceIdentifier,
|
||||||
completion(error)
|
completion: { (_, error) in
|
||||||
withExtendedLifetime(self, {})
|
|
||||||
}
|
completion(error)
|
||||||
|
withExtendedLifetime(self, {})
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -57,7 +60,10 @@ public final class UnsafeDataTransaction: BaseDataTransaction {
|
|||||||
*/
|
*/
|
||||||
public func commitAndWait() throws {
|
public func commitAndWait() throws {
|
||||||
|
|
||||||
if case (_, let error?) = self.context.saveSynchronously(waitForMerge: true) {
|
if case (_, let error?) = self.context.saveSynchronously(
|
||||||
|
waitForMerge: true,
|
||||||
|
sourceIdentifier: self.sourceIdentifier
|
||||||
|
) {
|
||||||
|
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
@@ -126,23 +132,39 @@ public final class UnsafeDataTransaction: BaseDataTransaction {
|
|||||||
/**
|
/**
|
||||||
Begins a child transaction where `NSManagedObject` or `CoreStoreObject` creates, updates, and deletes can be made. This is useful for making temporary changes, such as partially filled forms.
|
Begins a child transaction where `NSManagedObject` or `CoreStoreObject` 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.
|
- parameter 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.
|
||||||
|
- parameter sourceIdentifier: an optional value that identifies the source of this transaction. This identifier will be passed to the change notifications and callers can use it for custom handling that depends on the source.
|
||||||
- returns: an `UnsafeDataTransaction` instance where creates, updates, and deletes can be made.
|
- returns: an `UnsafeDataTransaction` instance where creates, updates, and deletes can be made.
|
||||||
*/
|
*/
|
||||||
public func beginUnsafe(supportsUndo: Bool = false) -> UnsafeDataTransaction {
|
public func beginUnsafe(
|
||||||
|
supportsUndo: Bool = false,
|
||||||
|
sourceIdentifier: Any? = nil
|
||||||
|
) -> UnsafeDataTransaction {
|
||||||
|
|
||||||
return UnsafeDataTransaction(
|
return UnsafeDataTransaction(
|
||||||
mainContext: self.context,
|
mainContext: self.context,
|
||||||
queue: self.transactionQueue,
|
queue: self.transactionQueue,
|
||||||
supportsUndo: supportsUndo
|
supportsUndo: supportsUndo,
|
||||||
|
sourceIdentifier: sourceIdentifier
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// MARK: Internal
|
// MARK: Internal
|
||||||
|
|
||||||
internal init(mainContext: NSManagedObjectContext, queue: DispatchQueue, supportsUndo: Bool) {
|
internal init(
|
||||||
|
mainContext: NSManagedObjectContext,
|
||||||
|
queue: DispatchQueue,
|
||||||
|
supportsUndo: Bool,
|
||||||
|
sourceIdentifier: Any?
|
||||||
|
) {
|
||||||
|
|
||||||
super.init(mainContext: mainContext, queue: queue, supportsUndo: supportsUndo, bypassesQueueing: true)
|
super.init(
|
||||||
|
mainContext: mainContext,
|
||||||
|
queue: queue,
|
||||||
|
supportsUndo: supportsUndo,
|
||||||
|
bypassesQueueing: true,
|
||||||
|
sourceIdentifier: sourceIdentifier
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user