mirror of
https://github.com/JohnEstropia/CoreStore.git
synced 2026-01-13 04:40:32 +01:00
Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f119a3adec | ||
|
|
c951cb87a3 | ||
|
|
08147806a0 | ||
|
|
4beb11519e | ||
|
|
b7ebda4487 | ||
|
|
b4489301ac | ||
|
|
c025e5acc6 | ||
|
|
57745f36a8 | ||
|
|
eef1c99f11 | ||
|
|
9a19919392 | ||
|
|
3e2d62fe67 | ||
|
|
6f275eb63a | ||
|
|
b12dba4d15 | ||
|
|
4ee1b04523 | ||
|
|
b1decc9853 | ||
|
|
c2e4c033ef | ||
|
|
e12223df85 | ||
|
|
468922d5ed | ||
|
|
6b9a4b480b | ||
|
|
81b482e28b | ||
|
|
c112a84c0a | ||
|
|
88ab0b5e15 | ||
|
|
717cb75720 | ||
|
|
998938490c |
@@ -1,6 +1,6 @@
|
||||
Pod::Spec.new do |s|
|
||||
s.name = "CoreStore"
|
||||
s.version = "7.0.1"
|
||||
s.version = "7.0.3"
|
||||
s.swift_version = "5.1"
|
||||
s.license = "MIT"
|
||||
s.homepage = "https://github.com/JohnEstropia/CoreStore"
|
||||
|
||||
@@ -141,6 +141,13 @@
|
||||
B51260941E9B28F100402229 /* Internals.EntityIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = B51260921E9B28F100402229 /* Internals.EntityIdentifier.swift */; };
|
||||
B51260951E9B28F100402229 /* Internals.EntityIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = B51260921E9B28F100402229 /* Internals.EntityIdentifier.swift */; };
|
||||
B51260961E9B28F100402229 /* Internals.EntityIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = B51260921E9B28F100402229 /* Internals.EntityIdentifier.swift */; };
|
||||
B514EF0E23A8BEEA0093DBA4 /* DiffableDataSource.Target.swift in Sources */ = {isa = PBXBuildFile; fileRef = B514EF0D23A8BEEA0093DBA4 /* DiffableDataSource.Target.swift */; };
|
||||
B514EF0F23A8DB180093DBA4 /* DiffableDataSource.Target.swift in Sources */ = {isa = PBXBuildFile; fileRef = B514EF0D23A8BEEA0093DBA4 /* DiffableDataSource.Target.swift */; };
|
||||
B514EF1023A8DB190093DBA4 /* DiffableDataSource.Target.swift in Sources */ = {isa = PBXBuildFile; fileRef = B514EF0D23A8BEEA0093DBA4 /* DiffableDataSource.Target.swift */; };
|
||||
B514EF1123A8DB190093DBA4 /* DiffableDataSource.Target.swift in Sources */ = {isa = PBXBuildFile; fileRef = B514EF0D23A8BEEA0093DBA4 /* DiffableDataSource.Target.swift */; };
|
||||
B514EF1223A8DB1D0093DBA4 /* DiffableDataSource.BaseAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D4A6B623A236DC00D7373F /* DiffableDataSource.BaseAdapter.swift */; };
|
||||
B514EF1323A8DB1D0093DBA4 /* DiffableDataSource.BaseAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D4A6B623A236DC00D7373F /* DiffableDataSource.BaseAdapter.swift */; };
|
||||
B514EF1423A8DB1E0093DBA4 /* DiffableDataSource.BaseAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D4A6B623A236DC00D7373F /* DiffableDataSource.BaseAdapter.swift */; };
|
||||
B51B5C2B22D43931009FA3BA /* String+KeyPaths.swift in Sources */ = {isa = PBXBuildFile; fileRef = B51B5C2A22D43931009FA3BA /* String+KeyPaths.swift */; };
|
||||
B51B5C2D22D43E38009FA3BA /* KeyPath+KeyPaths.swift in Sources */ = {isa = PBXBuildFile; fileRef = B51B5C2C22D43E38009FA3BA /* KeyPath+KeyPaths.swift */; };
|
||||
B51FE5AB1CD4D00300E54258 /* CoreStore+CustomDebugStringConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = B51FE5AA1CD4D00300E54258 /* CoreStore+CustomDebugStringConvertible.swift */; };
|
||||
@@ -465,10 +472,10 @@
|
||||
B56321B31BD6521C006C9394 /* NSManagedObjectContext+Setup.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84F321AFF85470064E85B /* NSManagedObjectContext+Setup.swift */; };
|
||||
B56321B41BD6521C006C9394 /* NSManagedObjectContext+Transaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84F331AFF85470064E85B /* NSManagedObjectContext+Transaction.swift */; };
|
||||
B56321B61BD6521C006C9394 /* Internals.WeakObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84F2D1AFF849C0064E85B /* Internals.WeakObject.swift */; };
|
||||
B5635D142356C39500B80E6B /* DiffableDataSource.CollectionView-UIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5635D132356C39500B80E6B /* DiffableDataSource.CollectionView-UIKit.swift */; };
|
||||
B5635D152356C39500B80E6B /* DiffableDataSource.CollectionView-UIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5635D132356C39500B80E6B /* DiffableDataSource.CollectionView-UIKit.swift */; };
|
||||
B5635D162356C39500B80E6B /* DiffableDataSource.CollectionView-UIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5635D132356C39500B80E6B /* DiffableDataSource.CollectionView-UIKit.swift */; };
|
||||
B5635D172356C39500B80E6B /* DiffableDataSource.CollectionView-UIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5635D132356C39500B80E6B /* DiffableDataSource.CollectionView-UIKit.swift */; };
|
||||
B5635D142356C39500B80E6B /* DiffableDataSource.CollectionViewAdapter-UIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5635D132356C39500B80E6B /* DiffableDataSource.CollectionViewAdapter-UIKit.swift */; };
|
||||
B5635D152356C39500B80E6B /* DiffableDataSource.CollectionViewAdapter-UIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5635D132356C39500B80E6B /* DiffableDataSource.CollectionViewAdapter-UIKit.swift */; };
|
||||
B5635D162356C39500B80E6B /* DiffableDataSource.CollectionViewAdapter-UIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5635D132356C39500B80E6B /* DiffableDataSource.CollectionViewAdapter-UIKit.swift */; };
|
||||
B5635D172356C39500B80E6B /* DiffableDataSource.CollectionViewAdapter-UIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5635D132356C39500B80E6B /* DiffableDataSource.CollectionViewAdapter-UIKit.swift */; };
|
||||
B56507941D3930BC000596DA /* CoreData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B56507931D3930BC000596DA /* CoreData.framework */; };
|
||||
B56507961D3930C1000596DA /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B56507951D3930C1000596DA /* Foundation.framework */; };
|
||||
B56507981D3930CC000596DA /* CoreData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B56507971D3930CC000596DA /* CoreData.framework */; };
|
||||
@@ -583,10 +590,10 @@
|
||||
B5A992201EA898720091A2E3 /* UserInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A9921E1EA898710091A2E3 /* UserInfo.swift */; };
|
||||
B5A992211EA898720091A2E3 /* UserInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A9921E1EA898710091A2E3 /* UserInfo.swift */; };
|
||||
B5A992221EA898720091A2E3 /* UserInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A9921E1EA898710091A2E3 /* UserInfo.swift */; };
|
||||
B5AA37F1235C28EE00FFD4B9 /* diffableDataSource.CollectionView-AppKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5AA37F0235C28EE00FFD4B9 /* diffableDataSource.CollectionView-AppKit.swift */; };
|
||||
B5AA37F2235C28EE00FFD4B9 /* diffableDataSource.CollectionView-AppKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5AA37F0235C28EE00FFD4B9 /* diffableDataSource.CollectionView-AppKit.swift */; };
|
||||
B5AA37F3235C28EE00FFD4B9 /* diffableDataSource.CollectionView-AppKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5AA37F0235C28EE00FFD4B9 /* diffableDataSource.CollectionView-AppKit.swift */; };
|
||||
B5AA37F4235C28EE00FFD4B9 /* diffableDataSource.CollectionView-AppKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5AA37F0235C28EE00FFD4B9 /* diffableDataSource.CollectionView-AppKit.swift */; };
|
||||
B5AA37F1235C28EE00FFD4B9 /* DiffableDataSource.CollectionViewAdapter-AppKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5AA37F0235C28EE00FFD4B9 /* DiffableDataSource.CollectionViewAdapter-AppKit.swift */; };
|
||||
B5AA37F2235C28EE00FFD4B9 /* DiffableDataSource.CollectionViewAdapter-AppKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5AA37F0235C28EE00FFD4B9 /* DiffableDataSource.CollectionViewAdapter-AppKit.swift */; };
|
||||
B5AA37F3235C28EE00FFD4B9 /* DiffableDataSource.CollectionViewAdapter-AppKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5AA37F0235C28EE00FFD4B9 /* DiffableDataSource.CollectionViewAdapter-AppKit.swift */; };
|
||||
B5AA37F4235C28EE00FFD4B9 /* DiffableDataSource.CollectionViewAdapter-AppKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5AA37F0235C28EE00FFD4B9 /* DiffableDataSource.CollectionViewAdapter-AppKit.swift */; };
|
||||
B5AA37FD235C3D1A00FFD4B9 /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B5AA37FC235C3D1A00FFD4B9 /* SwiftUI.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
|
||||
B5AEFAB51C9962AE00AD137F /* CoreStoreBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5AEFAB41C9962AE00AD137F /* CoreStoreBridge.swift */; };
|
||||
B5AEFAB61C9962AE00AD137F /* CoreStoreBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5AEFAB41C9962AE00AD137F /* CoreStoreBridge.swift */; };
|
||||
@@ -600,10 +607,10 @@
|
||||
B5BF7FB3234C97910070E741 /* DiffableDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FB1234C97910070E741 /* DiffableDataSource.swift */; };
|
||||
B5BF7FB4234C97910070E741 /* DiffableDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FB1234C97910070E741 /* DiffableDataSource.swift */; };
|
||||
B5BF7FB5234C97910070E741 /* DiffableDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FB1234C97910070E741 /* DiffableDataSource.swift */; };
|
||||
B5BF7FB7234C97CE0070E741 /* DiffableDataSource.TableView-UIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FB6234C97CE0070E741 /* DiffableDataSource.TableView-UIKit.swift */; };
|
||||
B5BF7FB8234C97CE0070E741 /* DiffableDataSource.TableView-UIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FB6234C97CE0070E741 /* DiffableDataSource.TableView-UIKit.swift */; };
|
||||
B5BF7FB9234C97CE0070E741 /* DiffableDataSource.TableView-UIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FB6234C97CE0070E741 /* DiffableDataSource.TableView-UIKit.swift */; };
|
||||
B5BF7FBA234C97CE0070E741 /* DiffableDataSource.TableView-UIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FB6234C97CE0070E741 /* DiffableDataSource.TableView-UIKit.swift */; };
|
||||
B5BF7FB7234C97CE0070E741 /* DiffableDataSource.TableViewAdapter-UIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FB6234C97CE0070E741 /* DiffableDataSource.TableViewAdapter-UIKit.swift */; };
|
||||
B5BF7FB8234C97CE0070E741 /* DiffableDataSource.TableViewAdapter-UIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FB6234C97CE0070E741 /* DiffableDataSource.TableViewAdapter-UIKit.swift */; };
|
||||
B5BF7FB9234C97CE0070E741 /* DiffableDataSource.TableViewAdapter-UIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FB6234C97CE0070E741 /* DiffableDataSource.TableViewAdapter-UIKit.swift */; };
|
||||
B5BF7FBA234C97CE0070E741 /* DiffableDataSource.TableViewAdapter-UIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FB6234C97CE0070E741 /* DiffableDataSource.TableViewAdapter-UIKit.swift */; };
|
||||
B5BF7FBC234C99190070E741 /* Internals.DiffableDataUIDispatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FBB234C99190070E741 /* Internals.DiffableDataUIDispatcher.swift */; };
|
||||
B5BF7FBD234C99190070E741 /* Internals.DiffableDataUIDispatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FBB234C99190070E741 /* Internals.DiffableDataUIDispatcher.swift */; };
|
||||
B5BF7FBE234C99190070E741 /* Internals.DiffableDataUIDispatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FBB234C99190070E741 /* Internals.DiffableDataUIDispatcher.swift */; };
|
||||
@@ -668,6 +675,7 @@
|
||||
B5D33A041E96012400C880DE /* Relationship.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D33A001E96012400C880DE /* Relationship.swift */; };
|
||||
B5D372841A39CD6900F583D9 /* Model.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = B5D372821A39CD6900F583D9 /* Model.xcdatamodeld */; };
|
||||
B5D39A0219FD00C9000E91BB /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B5D39A0119FD00C9000E91BB /* Foundation.framework */; };
|
||||
B5D4A6B723A236DC00D7373F /* DiffableDataSource.BaseAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D4A6B623A236DC00D7373F /* DiffableDataSource.BaseAdapter.swift */; };
|
||||
B5D7A5B61CA3BF8F005C752B /* CSInto.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D7A5B51CA3BF8F005C752B /* CSInto.swift */; };
|
||||
B5D7A5B81CA3BF8F005C752B /* CSInto.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D7A5B51CA3BF8F005C752B /* CSInto.swift */; };
|
||||
B5D7A5B91CA3BF8F005C752B /* CSInto.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D7A5B51CA3BF8F005C752B /* CSInto.swift */; };
|
||||
@@ -906,6 +914,7 @@
|
||||
B512607E1E97A18000402229 /* CoreStoreObject+Convenience.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CoreStoreObject+Convenience.swift"; sourceTree = "<group>"; };
|
||||
B51260881E9B252B00402229 /* NSEntityDescription+DynamicModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSEntityDescription+DynamicModel.swift"; sourceTree = "<group>"; };
|
||||
B51260921E9B28F100402229 /* Internals.EntityIdentifier.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Internals.EntityIdentifier.swift; sourceTree = "<group>"; };
|
||||
B514EF0D23A8BEEA0093DBA4 /* DiffableDataSource.Target.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiffableDataSource.Target.swift; sourceTree = "<group>"; };
|
||||
B51B5C2A22D43931009FA3BA /* String+KeyPaths.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+KeyPaths.swift"; sourceTree = "<group>"; };
|
||||
B51B5C2C22D43E38009FA3BA /* KeyPath+KeyPaths.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KeyPath+KeyPaths.swift"; sourceTree = "<group>"; };
|
||||
B51FE5AA1CD4D00300E54258 /* CoreStore+CustomDebugStringConvertible.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CoreStore+CustomDebugStringConvertible.swift"; sourceTree = "<group>"; };
|
||||
@@ -976,7 +985,7 @@
|
||||
B563216F1BD65082006C9394 /* CoreStore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CoreStore.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
B56321791BD650DE006C9394 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS2.0.sdk/System/Library/Frameworks/CoreData.framework; sourceTree = DEVELOPER_DIR; };
|
||||
B563217B1BD650E3006C9394 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS2.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; };
|
||||
B5635D132356C39500B80E6B /* DiffableDataSource.CollectionView-UIKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DiffableDataSource.CollectionView-UIKit.swift"; sourceTree = "<group>"; };
|
||||
B5635D132356C39500B80E6B /* DiffableDataSource.CollectionViewAdapter-UIKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DiffableDataSource.CollectionViewAdapter-UIKit.swift"; sourceTree = "<group>"; };
|
||||
B56507931D3930BC000596DA /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS10.0.sdk/System/Library/Frameworks/CoreData.framework; sourceTree = DEVELOPER_DIR; };
|
||||
B56507951D3930C1000596DA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS10.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; };
|
||||
B56507971D3930CC000596DA /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS3.0.sdk/System/Library/Frameworks/CoreData.framework; sourceTree = DEVELOPER_DIR; };
|
||||
@@ -1014,14 +1023,14 @@
|
||||
B5A80DF52212C1BC006096AA /* Playground_iOS.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = Playground_iOS.playground; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
|
||||
B5A991EB1E9DC2CE0091A2E3 /* VersionLock.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VersionLock.swift; sourceTree = "<group>"; };
|
||||
B5A9921E1EA898710091A2E3 /* UserInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserInfo.swift; sourceTree = "<group>"; };
|
||||
B5AA37F0235C28EE00FFD4B9 /* diffableDataSource.CollectionView-AppKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "diffableDataSource.CollectionView-AppKit.swift"; sourceTree = "<group>"; };
|
||||
B5AA37F0235C28EE00FFD4B9 /* DiffableDataSource.CollectionViewAdapter-AppKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DiffableDataSource.CollectionViewAdapter-AppKit.swift"; sourceTree = "<group>"; };
|
||||
B5AA37FA235C3D1300FFD4B9 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS6.0.sdk/System/Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; };
|
||||
B5AA37FC235C3D1A00FFD4B9 /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS6.0.sdk/System/Library/Frameworks/SwiftUI.framework; sourceTree = DEVELOPER_DIR; };
|
||||
B5AD60CD1C90141E00F2B2E8 /* Package.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = SOURCE_ROOT; };
|
||||
B5AEFAB41C9962AE00AD137F /* CoreStoreBridge.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreStoreBridge.swift; sourceTree = "<group>"; };
|
||||
B5BF7FAC234C41E90070E741 /* Internals.DiffableDataSourceSnapshot.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Internals.DiffableDataSourceSnapshot.swift; sourceTree = "<group>"; };
|
||||
B5BF7FB1234C97910070E741 /* DiffableDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiffableDataSource.swift; sourceTree = "<group>"; };
|
||||
B5BF7FB6234C97CE0070E741 /* DiffableDataSource.TableView-UIKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DiffableDataSource.TableView-UIKit.swift"; sourceTree = "<group>"; };
|
||||
B5BF7FB6234C97CE0070E741 /* DiffableDataSource.TableViewAdapter-UIKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DiffableDataSource.TableViewAdapter-UIKit.swift"; sourceTree = "<group>"; };
|
||||
B5BF7FBB234C99190070E741 /* Internals.DiffableDataUIDispatcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Internals.DiffableDataUIDispatcher.swift; sourceTree = "<group>"; };
|
||||
B5BF7FC0234D7B2E0070E741 /* ObjectPublisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObjectPublisher.swift; sourceTree = "<group>"; };
|
||||
B5BF7FC5234D7E460070E741 /* ObjectSnapshot.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObjectSnapshot.swift; sourceTree = "<group>"; };
|
||||
@@ -1042,6 +1051,7 @@
|
||||
B5D33A001E96012400C880DE /* Relationship.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Relationship.swift; sourceTree = "<group>"; };
|
||||
B5D372831A39CD6900F583D9 /* Model.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Model.xcdatamodel; sourceTree = "<group>"; };
|
||||
B5D39A0119FD00C9000E91BB /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
|
||||
B5D4A6B623A236DC00D7373F /* DiffableDataSource.BaseAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiffableDataSource.BaseAdapter.swift; sourceTree = "<group>"; };
|
||||
B5D7A5B51CA3BF8F005C752B /* CSInto.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSInto.swift; sourceTree = "<group>"; };
|
||||
B5D8CA752346E7590055D7D1 /* DataStack+DataSources.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DataStack+DataSources.swift"; sourceTree = "<group>"; };
|
||||
B5D8CA7A2346EC550055D7D1 /* ListPublisherTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListPublisherTests.swift; sourceTree = "<group>"; };
|
||||
@@ -1607,9 +1617,11 @@
|
||||
B50EE14123473C92009B8C47 /* CoreStoreObject+DataSources.swift */,
|
||||
B5D8CA752346E7590055D7D1 /* DataStack+DataSources.swift */,
|
||||
B5BF7FB1234C97910070E741 /* DiffableDataSource.swift */,
|
||||
B5BF7FB6234C97CE0070E741 /* DiffableDataSource.TableView-UIKit.swift */,
|
||||
B5635D132356C39500B80E6B /* DiffableDataSource.CollectionView-UIKit.swift */,
|
||||
B5AA37F0235C28EE00FFD4B9 /* diffableDataSource.CollectionView-AppKit.swift */,
|
||||
B514EF0D23A8BEEA0093DBA4 /* DiffableDataSource.Target.swift */,
|
||||
B5D4A6B623A236DC00D7373F /* DiffableDataSource.BaseAdapter.swift */,
|
||||
B5BF7FB6234C97CE0070E741 /* DiffableDataSource.TableViewAdapter-UIKit.swift */,
|
||||
B5635D132356C39500B80E6B /* DiffableDataSource.CollectionViewAdapter-UIKit.swift */,
|
||||
B5AA37F0235C28EE00FFD4B9 /* DiffableDataSource.CollectionViewAdapter-AppKit.swift */,
|
||||
);
|
||||
name = DataSources;
|
||||
sourceTree = "<group>";
|
||||
@@ -2089,6 +2101,7 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
B5BF7FB7234C97CE0070E741 /* DiffableDataSource.TableViewAdapter-UIKit.swift in Sources */,
|
||||
B5DE5230230BDA1300A22534 /* CoreStoreDefaults.swift in Sources */,
|
||||
B5E84F221AFF84860064E85B /* ObjectMonitor.swift in Sources */,
|
||||
B5ECDBF91CA804FD00C7F112 /* NSManagedObjectContext+ObjectiveC.swift in Sources */,
|
||||
@@ -2121,12 +2134,11 @@
|
||||
B5A261211B64BFDB006EB6D3 /* MigrationType.swift in Sources */,
|
||||
B53FBA0B1CAB5E6500F0D40A /* CSCoreStore+Migrating.swift in Sources */,
|
||||
B5E84F141AFF847B0064E85B /* DataStack+Querying.swift in Sources */,
|
||||
B5AA37F1235C28EE00FFD4B9 /* diffableDataSource.CollectionView-AppKit.swift in Sources */,
|
||||
B5AA37F1235C28EE00FFD4B9 /* DiffableDataSource.CollectionViewAdapter-AppKit.swift in Sources */,
|
||||
B5D7A5B61CA3BF8F005C752B /* CSInto.swift in Sources */,
|
||||
B56007141B3F6C2800A9A8F9 /* SectionBy.swift in Sources */,
|
||||
B5DE522B230BD7CC00A22534 /* Internals.swift in Sources */,
|
||||
B5E84F371AFF85470064E85B /* NSManagedObjectContext+Transaction.swift in Sources */,
|
||||
B5BF7FB7234C97CE0070E741 /* DiffableDataSource.TableView-UIKit.swift in Sources */,
|
||||
B5ECDC1D1CA81A2100C7F112 /* CSDataStack+Querying.swift in Sources */,
|
||||
B5C976E31C6C9F6A00B1AF90 /* UnsafeDataTransaction+Observing.swift in Sources */,
|
||||
B53FBA121CAB63CB00F0D40A /* Progress+ObjectiveC.swift in Sources */,
|
||||
@@ -2151,7 +2163,7 @@
|
||||
B56923F01EB827F6007C4DC9 /* XcodeSchemaMappingProvider.swift in Sources */,
|
||||
B5E84F121AFF847B0064E85B /* OrderBy.swift in Sources */,
|
||||
B546F9581C99B17400D5AC55 /* CSCoreStore+Setup.swift in Sources */,
|
||||
B5635D142356C39500B80E6B /* DiffableDataSource.CollectionView-UIKit.swift in Sources */,
|
||||
B5635D142356C39500B80E6B /* DiffableDataSource.CollectionViewAdapter-UIKit.swift in Sources */,
|
||||
B5E84F361AFF85470064E85B /* NSManagedObjectContext+Setup.swift in Sources */,
|
||||
B5FAD6AE1B518DCB00714891 /* CoreStore+Migration.swift in Sources */,
|
||||
B50E175223517C6B004F033C /* Internals.DiffableDataUIDispatcher.Changeset.swift in Sources */,
|
||||
@@ -2224,6 +2236,7 @@
|
||||
B5BF7FBC234C99190070E741 /* Internals.DiffableDataUIDispatcher.swift in Sources */,
|
||||
B5215CA41FA47DFD00139E3A /* FetchChainBuilder.swift in Sources */,
|
||||
B5D33A011E96012400C880DE /* Relationship.swift in Sources */,
|
||||
B514EF0E23A8BEEA0093DBA4 /* DiffableDataSource.Target.swift in Sources */,
|
||||
B5E84EE81AFF84610064E85B /* CoreStoreLogger.swift in Sources */,
|
||||
B56923C91EB82410007C4DC9 /* NSManagedObjectModel+Migration.swift in Sources */,
|
||||
B56923E41EB827F5007C4DC9 /* CustomSchemaMappingProvider.swift in Sources */,
|
||||
@@ -2240,6 +2253,7 @@
|
||||
B512607F1E97A18000402229 /* CoreStoreObject+Convenience.swift in Sources */,
|
||||
B5E84EF81AFF846E0064E85B /* CoreStore+Transaction.swift in Sources */,
|
||||
B5E84F301AFF849C0064E85B /* NSManagedObjectContext+CoreStore.swift in Sources */,
|
||||
B5D4A6B723A236DC00D7373F /* DiffableDataSource.BaseAdapter.swift in Sources */,
|
||||
B5831B7A1F34ACBA00A9F647 /* Transformable.swift in Sources */,
|
||||
B5BF7FB2234C97910070E741 /* DiffableDataSource.swift in Sources */,
|
||||
B546F9691C9AF26D00D5AC55 /* CSInMemoryStore.swift in Sources */,
|
||||
@@ -2316,7 +2330,7 @@
|
||||
files = (
|
||||
82BA18B61C4BBD3F00A0916E /* DataStack+Querying.swift in Sources */,
|
||||
B5ECDBFB1CA804FD00C7F112 /* NSManagedObjectContext+ObjectiveC.swift in Sources */,
|
||||
B5635D152356C39500B80E6B /* DiffableDataSource.CollectionView-UIKit.swift in Sources */,
|
||||
B5635D152356C39500B80E6B /* DiffableDataSource.CollectionViewAdapter-UIKit.swift in Sources */,
|
||||
B5CA2B091F7E5ACA004B1936 /* WhereClauseType.swift in Sources */,
|
||||
B5C976E81C6E3A5D00B1AF90 /* Internals.CoreStoreFetchedResultsController.swift in Sources */,
|
||||
B56923F61EB828BF007C4DC9 /* CSDynamicSchema.swift in Sources */,
|
||||
@@ -2387,6 +2401,7 @@
|
||||
B53FBA001CAB2D2F00F0D40A /* CSMigrationResult.swift in Sources */,
|
||||
B5DBE2D31C991B3E00B5CEFA /* CSDataStack.swift in Sources */,
|
||||
82BA18B41C4BBD3900A0916E /* BaseDataTransaction+Importing.swift in Sources */,
|
||||
B514EF1223A8DB1D0093DBA4 /* DiffableDataSource.BaseAdapter.swift in Sources */,
|
||||
B53FBA1A1CAB63E200F0D40A /* NSManagedObject+ObjectiveC.swift in Sources */,
|
||||
82BA18CA1C4BBD5900A0916E /* MigrationResult.swift in Sources */,
|
||||
B5519A5A1CA2008C002BEF78 /* CSBaseDataTransaction.swift in Sources */,
|
||||
@@ -2435,6 +2450,7 @@
|
||||
B5215CAA1FA4810300139E3A /* QueryChainBuilder.swift in Sources */,
|
||||
B50E175823517DE4004F033C /* Differentiable.swift in Sources */,
|
||||
82BA18BA1C4BBD4A00A0916E /* Select.swift in Sources */,
|
||||
B514EF0F23A8DB180093DBA4 /* DiffableDataSource.Target.swift in Sources */,
|
||||
B52F744B1E9B8740005F3DAC /* CoreStoreSchema.swift in Sources */,
|
||||
B5AEFAB61C9962AE00AD137F /* CoreStoreBridge.swift in Sources */,
|
||||
B5E2222C1CA51B6E00BA2E95 /* CSUnsafeDataTransaction.swift in Sources */,
|
||||
@@ -2474,7 +2490,7 @@
|
||||
B52F743E1E9B8724005F3DAC /* DynamicSchema.swift in Sources */,
|
||||
18166884232B9ED00097C275 /* KeyPath+KeyPaths.swift in Sources */,
|
||||
B5E8A72121C1015300EF006A /* CoreStoreObject+Observing.swift in Sources */,
|
||||
B5AA37F2235C28EE00FFD4B9 /* diffableDataSource.CollectionView-AppKit.swift in Sources */,
|
||||
B5AA37F2235C28EE00FFD4B9 /* DiffableDataSource.CollectionViewAdapter-AppKit.swift in Sources */,
|
||||
B50E175D2351848E004F033C /* Internals.DiffableDataUIDispatcher.DiffResult.swift in Sources */,
|
||||
B5474D162227C08700B21FEC /* Internals.CoreStoreFetchRequest.swift in Sources */,
|
||||
B501322B2346A9AE00FC238B /* ListPublisher.swift in Sources */,
|
||||
@@ -2492,7 +2508,7 @@
|
||||
B5D339E81E9493A500C880DE /* Entity.swift in Sources */,
|
||||
B5BF7FC7234D7E460070E741 /* ObjectSnapshot.swift in Sources */,
|
||||
82BA18CC1C4BBD6400A0916E /* Progress+Convenience.swift in Sources */,
|
||||
B5BF7FB8234C97CE0070E741 /* DiffableDataSource.TableView-UIKit.swift in Sources */,
|
||||
B5BF7FB8234C97CE0070E741 /* DiffableDataSource.TableViewAdapter-UIKit.swift in Sources */,
|
||||
82BA18C01C4BBD5300A0916E /* DataStack+Observing.swift in Sources */,
|
||||
82BA18A61C4BBD2900A0916E /* DefaultLogger.swift in Sources */,
|
||||
);
|
||||
@@ -2538,9 +2554,12 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
B5BF7FB5234C97910070E741 /* DiffableDataSource.swift in Sources */,
|
||||
B5220E1E1D13080D009BC71E /* CSListMonitor.swift in Sources */,
|
||||
B5AA37F4235C28EE00FFD4B9 /* DiffableDataSource.CollectionViewAdapter-AppKit.swift in Sources */,
|
||||
B5BF7FBA234C97CE0070E741 /* DiffableDataSource.TableViewAdapter-UIKit.swift in Sources */,
|
||||
B5635D172356C39500B80E6B /* DiffableDataSource.CollectionViewAdapter-UIKit.swift in Sources */,
|
||||
B5DBE2D01C9914A900B5CEFA /* CSCoreStore.swift in Sources */,
|
||||
B5635D172356C39500B80E6B /* DiffableDataSource.CollectionView-UIKit.swift in Sources */,
|
||||
B5CA2B0B1F7E5ACA004B1936 /* WhereClauseType.swift in Sources */,
|
||||
B56923F81EB828BF007C4DC9 /* CSDynamicSchema.swift in Sources */,
|
||||
B52DD1BE1BE1F94300949AFE /* Progress+Convenience.swift in Sources */,
|
||||
@@ -2581,7 +2600,6 @@
|
||||
B50564D62350CC3100482308 /* PropertyProtocol.swift in Sources */,
|
||||
B5831B781F34AC7A00A9F647 /* RelationshipProtocol.swift in Sources */,
|
||||
B52DD1981BE1F92500949AFE /* CoreStore+Setup.swift in Sources */,
|
||||
B5BF7FB5234C97910070E741 /* DiffableDataSource.swift in Sources */,
|
||||
B5D339F41E94AF5800C880DE /* CoreStoreStrings.swift in Sources */,
|
||||
B5220E241D13085E009BC71E /* NSFetchedResultsController+Convenience.swift in Sources */,
|
||||
B559CD471CAA8B6300E4D58B /* CSSetupResult.swift in Sources */,
|
||||
@@ -2609,6 +2627,7 @@
|
||||
B53FBA1C1CAB63E200F0D40A /* NSManagedObject+ObjectiveC.swift in Sources */,
|
||||
B5F8496F234898240029D57B /* ListSnapshot.swift in Sources */,
|
||||
B5831F452212700500D8604C /* Where.Expression.swift in Sources */,
|
||||
B514EF1423A8DB1E0093DBA4 /* DiffableDataSource.BaseAdapter.swift in Sources */,
|
||||
B51260961E9B28F100402229 /* Internals.EntityIdentifier.swift in Sources */,
|
||||
B5ECDBE31CA6BB2B00C7F112 /* CSBaseDataTransaction+Querying.swift in Sources */,
|
||||
B5ECDC031CA80CBA00C7F112 /* CSWhere.swift in Sources */,
|
||||
@@ -2657,6 +2676,7 @@
|
||||
B5220E1A1D130791009BC71E /* Internals.CoreStoreFetchedResultsController.swift in Sources */,
|
||||
B5215CAC1FA4810300139E3A /* QueryChainBuilder.swift in Sources */,
|
||||
B53FBA0F1CAB5E6500F0D40A /* CSCoreStore+Migrating.swift in Sources */,
|
||||
B514EF1123A8DB190093DBA4 /* DiffableDataSource.Target.swift in Sources */,
|
||||
B50E175A23517DE4004F033C /* Differentiable.swift in Sources */,
|
||||
B52F744D1E9B8740005F3DAC /* CoreStoreSchema.swift in Sources */,
|
||||
B52DD19A1BE1F92800949AFE /* CoreStore+Logging.swift in Sources */,
|
||||
@@ -2698,7 +2718,6 @@
|
||||
B52F74401E9B8724005F3DAC /* DynamicSchema.swift in Sources */,
|
||||
18166886232B9ED20097C275 /* KeyPath+KeyPaths.swift in Sources */,
|
||||
B5E8A72321C1015300EF006A /* CoreStoreObject+Observing.swift in Sources */,
|
||||
B5AA37F4235C28EE00FFD4B9 /* diffableDataSource.CollectionView-AppKit.swift in Sources */,
|
||||
B50E175F2351848E004F033C /* Internals.DiffableDataUIDispatcher.DiffResult.swift in Sources */,
|
||||
B5474D182227C08700B21FEC /* Internals.CoreStoreFetchRequest.swift in Sources */,
|
||||
B501322E2346A9B100FC238B /* ListPublisher.swift in Sources */,
|
||||
@@ -2716,7 +2735,6 @@
|
||||
B5D339EA1E9493A500C880DE /* Entity.swift in Sources */,
|
||||
B5BF7FC9234D7E460070E741 /* ObjectSnapshot.swift in Sources */,
|
||||
B52DD1AA1BE1F93500949AFE /* TypeErasedClauses.swift in Sources */,
|
||||
B5BF7FBA234C97CE0070E741 /* DiffableDataSource.TableView-UIKit.swift in Sources */,
|
||||
B53FBA021CAB2D2F00F0D40A /* CSMigrationResult.swift in Sources */,
|
||||
B51FE5AF1CD4D00300E54258 /* CoreStore+CustomDebugStringConvertible.swift in Sources */,
|
||||
);
|
||||
@@ -2764,7 +2782,7 @@
|
||||
files = (
|
||||
B56321A91BD65219006C9394 /* Progress+Convenience.swift in Sources */,
|
||||
B5ECDBFC1CA804FD00C7F112 /* NSManagedObjectContext+ObjectiveC.swift in Sources */,
|
||||
B5635D162356C39500B80E6B /* DiffableDataSource.CollectionView-UIKit.swift in Sources */,
|
||||
B5635D162356C39500B80E6B /* DiffableDataSource.CollectionViewAdapter-UIKit.swift in Sources */,
|
||||
B5CA2B0A1F7E5ACA004B1936 /* WhereClauseType.swift in Sources */,
|
||||
B5C976E91C6E3A5E00B1AF90 /* Internals.CoreStoreFetchedResultsController.swift in Sources */,
|
||||
B56923F71EB828BF007C4DC9 /* CSDynamicSchema.swift in Sources */,
|
||||
@@ -2835,6 +2853,7 @@
|
||||
B51260951E9B28F100402229 /* Internals.EntityIdentifier.swift in Sources */,
|
||||
B53FBA011CAB2D2F00F0D40A /* CSMigrationResult.swift in Sources */,
|
||||
B5DBE2D41C991B3E00B5CEFA /* CSDataStack.swift in Sources */,
|
||||
B514EF1323A8DB1D0093DBA4 /* DiffableDataSource.BaseAdapter.swift in Sources */,
|
||||
B50392FA1C47963F009900CA /* NSManagedObject+Transaction.swift in Sources */,
|
||||
B53FBA1B1CAB63E200F0D40A /* NSManagedObject+ObjectiveC.swift in Sources */,
|
||||
B5519A5B1CA2008C002BEF78 /* CSBaseDataTransaction.swift in Sources */,
|
||||
@@ -2883,6 +2902,7 @@
|
||||
B56321A21BD65216006C9394 /* ListObserver.swift in Sources */,
|
||||
B50E175923517DE4004F033C /* Differentiable.swift in Sources */,
|
||||
B5215CAB1FA4810300139E3A /* QueryChainBuilder.swift in Sources */,
|
||||
B514EF1023A8DB190093DBA4 /* DiffableDataSource.Target.swift in Sources */,
|
||||
B563218A1BD65216006C9394 /* SynchronousDataTransaction.swift in Sources */,
|
||||
B52F744C1E9B8740005F3DAC /* CoreStoreSchema.swift in Sources */,
|
||||
B5AEFAB71C9962AE00AD137F /* CoreStoreBridge.swift in Sources */,
|
||||
@@ -2922,7 +2942,7 @@
|
||||
B52F743F1E9B8724005F3DAC /* DynamicSchema.swift in Sources */,
|
||||
18166885232B9ED10097C275 /* KeyPath+KeyPaths.swift in Sources */,
|
||||
B5E8A72221C1015300EF006A /* CoreStoreObject+Observing.swift in Sources */,
|
||||
B5AA37F3235C28EE00FFD4B9 /* diffableDataSource.CollectionView-AppKit.swift in Sources */,
|
||||
B5AA37F3235C28EE00FFD4B9 /* DiffableDataSource.CollectionViewAdapter-AppKit.swift in Sources */,
|
||||
B50E175E2351848E004F033C /* Internals.DiffableDataUIDispatcher.DiffResult.swift in Sources */,
|
||||
B5474D172227C08700B21FEC /* Internals.CoreStoreFetchRequest.swift in Sources */,
|
||||
B501322D2346A9B000FC238B /* ListPublisher.swift in Sources */,
|
||||
@@ -2940,7 +2960,7 @@
|
||||
B5D339E91E9493A500C880DE /* Entity.swift in Sources */,
|
||||
B5BF7FC8234D7E460070E741 /* ObjectSnapshot.swift in Sources */,
|
||||
B56321A41BD65216006C9394 /* CoreStore+Migration.swift in Sources */,
|
||||
B5BF7FB9234C97CE0070E741 /* DiffableDataSource.TableView-UIKit.swift in Sources */,
|
||||
B5BF7FB9234C97CE0070E741 /* DiffableDataSource.TableViewAdapter-UIKit.swift in Sources */,
|
||||
B56321A01BD65216006C9394 /* ObjectObserver.swift in Sources */,
|
||||
B56321951BD65216006C9394 /* TypeErasedClauses.swift in Sources */,
|
||||
);
|
||||
@@ -3113,7 +3133,7 @@
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
MARKETING_VERSION = 7.0.1;
|
||||
MARKETING_VERSION = 7.0.3;
|
||||
OTHER_LDFLAGS = (
|
||||
"-weak_framework",
|
||||
Combine,
|
||||
@@ -3136,7 +3156,7 @@
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
MARKETING_VERSION = 7.0.1;
|
||||
MARKETING_VERSION = 7.0.3;
|
||||
OTHER_LDFLAGS = (
|
||||
"-weak_framework",
|
||||
Combine,
|
||||
|
||||
@@ -51,7 +51,7 @@ final class CollectionViewDemoViewController: UICollectionViewController {
|
||||
]
|
||||
self.filterBarButton = filterBarButton
|
||||
|
||||
self.dataSource = DiffableDataSource.CollectionView<Palette>(
|
||||
self.dataSource = DiffableDataSource.CollectionViewAdapter<Palette>(
|
||||
collectionView: self.collectionView,
|
||||
dataStack: ColorsDemo.stack,
|
||||
cellProvider: { (collectionView, indexPath, palette) in
|
||||
@@ -118,7 +118,7 @@ final class CollectionViewDemoViewController: UICollectionViewController {
|
||||
// MARK: Private
|
||||
|
||||
private var filterBarButton: UIBarButtonItem?
|
||||
private var dataSource: DiffableDataSource.CollectionView<Palette>?
|
||||
private var dataSource: DiffableDataSource.CollectionViewAdapter<Palette>?
|
||||
|
||||
deinit {
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ final class ListObserverDemoViewController: UITableViewController {
|
||||
|
||||
// MARK: - EditableDataSource
|
||||
|
||||
final class EditableDataSource: DiffableDataSource.TableView<Palette> {
|
||||
final class EditableDataSource: DiffableDataSource.TableViewAdapter<Palette> {
|
||||
|
||||
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
|
||||
|
||||
@@ -130,7 +130,7 @@ final class ListObserverDemoViewController: UITableViewController {
|
||||
// MARK: Private
|
||||
|
||||
private var filterBarButton: UIBarButtonItem?
|
||||
private var dataSource: DiffableDataSource.TableView<Palette>?
|
||||
private var dataSource: DiffableDataSource.TableViewAdapter<Palette>?
|
||||
|
||||
deinit {
|
||||
|
||||
|
||||
@@ -30,6 +30,10 @@
|
||||
|
||||
@import CoreData;
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
|
||||
|
||||
// MARK: - BridgingTests
|
||||
|
||||
@implementation BridgingTests
|
||||
@@ -261,3 +265,5 @@
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
@@ -31,6 +31,7 @@ import CoreStore
|
||||
|
||||
// MARK: - ErrorTests
|
||||
|
||||
@available(*, deprecated, message: "CoreStore Objective-C API will be removed soon.")
|
||||
final class ErrorTests: XCTestCase {
|
||||
|
||||
@objc
|
||||
|
||||
@@ -45,8 +45,9 @@ let package = Package(
|
||||
.testTarget(
|
||||
name: "CoreStoreTests",
|
||||
dependencies: ["CoreStore"],
|
||||
path: "CoreStoreTests"
|
||||
path: "CoreStoreTests",
|
||||
exclude: ["BridgingTests.h", "BridgingTests.m"]
|
||||
)
|
||||
],
|
||||
swiftLanguageVersions: [.v5_1]
|
||||
swiftLanguageVersions: [.v5]
|
||||
)
|
||||
|
||||
11
README.md
11
README.md
@@ -1506,9 +1506,9 @@ Note that the owner instance will not be retained. You may call `ListPublisher.r
|
||||
|
||||
The `ListSnapshot` returned from the `ListPublisher.snapshot` property returns a full-copy `struct` of all sections and `NSManagedObject` items in the list. This is ideal for managing states as they are thread-safe and are not affected by further changes to the result set. `ListPublisher` automatically updates its `snapshot` value to the latest state of the fetch.
|
||||
|
||||
Unlike `ListMonitor`s (See [`ListMonitor` examples](#observe-detailed-list-changes) below), a `ListPublisher` does not track detailed inserts, deletes, and moves. In return, a `ListPublisher` is a lot more lightweight and are designed to work well with `DiffableDataSource.TableView`s and `DiffableDataSource.CollectionView`s:
|
||||
Unlike `ListMonitor`s (See [`ListMonitor` examples](#observe-detailed-list-changes) below), a `ListPublisher` does not track detailed inserts, deletes, and moves. In return, a `ListPublisher` is a lot more lightweight and are designed to work well with `DiffableDataSource.TableViewAdapter`s and `DiffableDataSource.CollectionViewAdapter`s:
|
||||
```swift
|
||||
self.dataSource = DiffableDataSource.CollectionView<Person>(
|
||||
self.dataSource = DiffableDataSource.CollectionViewAdapter<Person>(
|
||||
collectionView: self.collectionView,
|
||||
dataStack: CoreStoreDefaults.dataStack,
|
||||
cellProvider: { (collectionView, indexPath, person) in
|
||||
@@ -1883,6 +1883,13 @@ git submodule add https://github.com/JohnEstropia/CoreStore.git <destination dir
|
||||
```
|
||||
Drag and drop **CoreStore.xcodeproj** to your project.
|
||||
|
||||
### Install through Xcode's Swift Package Manager
|
||||
From the **File** - **Swift Packages** - **Add Package Dependency…** menu, search for
|
||||
```
|
||||
CoreStore
|
||||
```
|
||||
where `JohnEstropia` is the *Owner*. Then add to your project.
|
||||
|
||||
|
||||
### Objective-C support
|
||||
|
||||
|
||||
@@ -43,5 +43,5 @@ internal protocol AttributeProtocol: PropertyProtocol {
|
||||
var rawObject: CoreStoreManagedObject? { get set }
|
||||
var getter: CoreStoreManagedObject.CustomGetter? { get }
|
||||
var setter: CoreStoreManagedObject.CustomSetter? { get }
|
||||
var valueForSnapshot: Any { get }
|
||||
var valueForSnapshot: Any? { get }
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ import CoreData
|
||||
|
||||
- SeeAlso: `AsynchronousDataTransaction`
|
||||
*/
|
||||
@available(*, deprecated, message: "CoreStore Objective-C API will be removed soon.")
|
||||
@objc
|
||||
public final class CSAsynchronousDataTransaction: CSBaseDataTransaction, CoreStoreObjectiveCType {
|
||||
|
||||
@@ -161,6 +162,7 @@ public final class CSAsynchronousDataTransaction: CSBaseDataTransaction, CoreSto
|
||||
|
||||
// MARK: - AsynchronousDataTransaction
|
||||
|
||||
@available(*, deprecated, message: "CoreStore Objective-C API will be removed soon.")
|
||||
extension AsynchronousDataTransaction: CoreStoreSwiftType {
|
||||
|
||||
// MARK: CoreStoreSwiftType
|
||||
|
||||
@@ -29,6 +29,7 @@ import CoreData
|
||||
|
||||
// MARK: - CSDataStack
|
||||
|
||||
@available(*, deprecated, message: "CoreStore Objective-C API will be removed soon.")
|
||||
extension CSDataStack {
|
||||
|
||||
/**
|
||||
|
||||
@@ -29,6 +29,7 @@ import CoreData
|
||||
|
||||
// MARK: - CSDataStack
|
||||
|
||||
@available(*, deprecated, message: "CoreStore Objective-C API will be removed soon.")
|
||||
@available(macOS 10.12, *)
|
||||
extension CSDataStack {
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ import CoreData
|
||||
|
||||
// MARK: - CSDataStack
|
||||
|
||||
@available(*, deprecated, message: "CoreStore Objective-C API will be removed soon.")
|
||||
extension CSDataStack {
|
||||
|
||||
/**
|
||||
|
||||
@@ -28,6 +28,7 @@ import Foundation
|
||||
|
||||
// MARK: - CSDataStack
|
||||
|
||||
@available(*, deprecated, message: "CoreStore Objective-C API will be removed soon.")
|
||||
extension CSDataStack {
|
||||
|
||||
/**
|
||||
|
||||
@@ -34,6 +34,7 @@ import CoreData
|
||||
|
||||
- SeeAlso: `DataStack`
|
||||
*/
|
||||
@available(*, deprecated, message: "CoreStore Objective-C API will be removed soon.")
|
||||
@objc
|
||||
public final class CSDataStack: NSObject, CoreStoreObjectiveCType {
|
||||
|
||||
@@ -209,6 +210,7 @@ public final class CSDataStack: NSObject, CoreStoreObjectiveCType {
|
||||
|
||||
// MARK: - DataStack
|
||||
|
||||
@available(*, deprecated, message: "CoreStore Objective-C API will be removed soon.")
|
||||
extension DataStack: CoreStoreSwiftType {
|
||||
|
||||
// MARK: CoreStoreSwiftType
|
||||
|
||||
@@ -35,7 +35,7 @@ import CoreData
|
||||
- SeeAlso: `CoreStoreError`
|
||||
*/
|
||||
@objc
|
||||
public final class CSError: NSError, CoreStoreObjectiveCType {
|
||||
public final class CSError: NSError {
|
||||
|
||||
/**
|
||||
The `NSError` error domain for `CSError`.
|
||||
@@ -67,20 +67,6 @@ public final class CSError: NSError, CoreStoreObjectiveCType {
|
||||
return "(\(String(reflecting: Self.self))) \(self.bridgeToSwift.coreStoreDumpString)"
|
||||
}
|
||||
|
||||
|
||||
// MARK: CoreStoreObjectiveCType
|
||||
|
||||
public var bridgeToSwift: CoreStoreError {
|
||||
|
||||
if let swift = self.swiftError {
|
||||
|
||||
return swift
|
||||
}
|
||||
let swift = CoreStoreError(_bridgedNSError: self) ?? .unknown
|
||||
self.swiftError = swift
|
||||
return swift
|
||||
}
|
||||
|
||||
/**
|
||||
Do not call directly!
|
||||
*/
|
||||
@@ -101,6 +87,23 @@ public final class CSError: NSError, CoreStoreObjectiveCType {
|
||||
private var swiftError: CoreStoreError?
|
||||
}
|
||||
|
||||
@available(*, deprecated, message: "CoreStore Objective-C API will be removed soon.")
|
||||
extension CSError: CoreStoreObjectiveCType {
|
||||
|
||||
// MARK: CoreStoreObjectiveCType
|
||||
|
||||
public var bridgeToSwift: CoreStoreError {
|
||||
|
||||
if let swift = self.swiftError {
|
||||
|
||||
return swift
|
||||
}
|
||||
let swift = CoreStoreError(_bridgedNSError: self) ?? .unknown
|
||||
self.swiftError = swift
|
||||
return swift
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - CSErrorCode
|
||||
|
||||
@@ -110,6 +113,7 @@ public final class CSError: NSError, CoreStoreObjectiveCType {
|
||||
- SeeAlso: `CSError`
|
||||
- SeeAlso: `CoreStoreError`
|
||||
*/
|
||||
@available(*, deprecated, message: "CoreStore Objective-C API will be removed soon.")
|
||||
@objc
|
||||
public enum CSErrorCode: Int {
|
||||
|
||||
@@ -152,6 +156,7 @@ public enum CSErrorCode: Int {
|
||||
|
||||
// MARK: - CoreStoreError
|
||||
|
||||
@available(*, deprecated, message: "CoreStore Objective-C API will be removed soon.")
|
||||
extension CoreStoreError: CoreStoreSwiftType, _ObjectiveCBridgeableError {
|
||||
|
||||
// MARK: CoreStoreSwiftType
|
||||
@@ -281,7 +286,8 @@ extension Error {
|
||||
return .unknown
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@available(*, deprecated, message: "CoreStore Objective-C API will be removed soon.")
|
||||
internal var bridgeToObjectiveC: NSError {
|
||||
|
||||
switch self {
|
||||
|
||||
@@ -34,6 +34,7 @@ import CoreData
|
||||
|
||||
- SeeAlso: `InMemoryStore`
|
||||
*/
|
||||
@available(*, deprecated, message: "CoreStore Objective-C API will be removed soon.")
|
||||
@objc
|
||||
public final class CSInMemoryStore: NSObject, CSStorageInterface, CoreStoreObjectiveCType {
|
||||
|
||||
@@ -121,6 +122,7 @@ public final class CSInMemoryStore: NSObject, CSStorageInterface, CoreStoreObjec
|
||||
|
||||
// MARK: - InMemoryStore
|
||||
|
||||
@available(*, deprecated, message: "CoreStore Objective-C API will be removed soon.")
|
||||
extension InMemoryStore: CoreStoreSwiftType {
|
||||
|
||||
// MARK: CoreStoreSwiftType
|
||||
|
||||
@@ -34,6 +34,7 @@ import CoreData
|
||||
|
||||
- SeeAlso: `MigrationResult`
|
||||
*/
|
||||
@available(*, deprecated, message: "CoreStore Objective-C API will be removed soon.")
|
||||
@objc
|
||||
public final class CSMigrationResult: NSObject, CoreStoreObjectiveCType {
|
||||
|
||||
@@ -173,6 +174,7 @@ public final class CSMigrationResult: NSObject, CoreStoreObjectiveCType {
|
||||
|
||||
// MARK: - MigrationResult
|
||||
|
||||
@available(*, deprecated, message: "CoreStore Objective-C API will be removed soon.")
|
||||
extension MigrationResult {
|
||||
|
||||
// MARK: CoreStoreSwiftType
|
||||
|
||||
@@ -34,6 +34,7 @@ import CoreData
|
||||
|
||||
- SeeAlso: `MigrationType`
|
||||
*/
|
||||
@available(*, deprecated, message: "CoreStore Objective-C API will be removed soon.")
|
||||
@objc
|
||||
public final class CSMigrationType: NSObject, CoreStoreObjectiveCType {
|
||||
|
||||
@@ -119,6 +120,7 @@ public final class CSMigrationType: NSObject, CoreStoreObjectiveCType {
|
||||
|
||||
// MARK: - MigrationType
|
||||
|
||||
@available(*, deprecated, message: "CoreStore Objective-C API will be removed soon.")
|
||||
extension MigrationType: CoreStoreSwiftType {
|
||||
|
||||
// MARK: CoreStoreSwiftType
|
||||
|
||||
@@ -34,6 +34,7 @@ import CoreData
|
||||
|
||||
- SeeAlso: `SQLiteStore`
|
||||
*/
|
||||
@available(*, deprecated, message: "CoreStore Objective-C API will be removed soon.")
|
||||
@objc
|
||||
public final class CSSQLiteStore: NSObject, CSLocalStorage, CoreStoreObjectiveCType {
|
||||
|
||||
@@ -196,6 +197,7 @@ public final class CSSQLiteStore: NSObject, CSLocalStorage, CoreStoreObjectiveCT
|
||||
|
||||
// MARK: - SQLiteStore
|
||||
|
||||
@available(*, deprecated, message: "CoreStore Objective-C API will be removed soon.")
|
||||
extension SQLiteStore: CoreStoreSwiftType {
|
||||
|
||||
// MARK: CoreStoreSwiftType
|
||||
|
||||
@@ -34,6 +34,7 @@ import CoreData
|
||||
|
||||
- SeeAlso: `SetupResult`
|
||||
*/
|
||||
@available(*, deprecated, message: "CoreStore Objective-C API will be removed soon.")
|
||||
@objc
|
||||
public final class CSSetupResult: NSObject {
|
||||
|
||||
@@ -177,6 +178,7 @@ public final class CSSetupResult: NSObject {
|
||||
|
||||
// MARK: - SetupResult
|
||||
|
||||
@available(*, deprecated, message: "CoreStore Objective-C API will be removed soon.")
|
||||
extension SetupResult where Success: StorageInterface, Success: CoreStoreSwiftType, Success.ObjectiveCType: CSStorageInterface, Failure == CoreStoreError {
|
||||
|
||||
// MARK: CoreStoreSwiftType
|
||||
|
||||
@@ -34,6 +34,7 @@ import CoreData
|
||||
|
||||
- SeeAlso: `SynchronousDataTransaction`
|
||||
*/
|
||||
@available(*, deprecated, message: "CoreStore Objective-C API will be removed soon.")
|
||||
@objc
|
||||
public final class CSSynchronousDataTransaction: CSBaseDataTransaction, CoreStoreObjectiveCType {
|
||||
|
||||
@@ -148,6 +149,7 @@ public final class CSSynchronousDataTransaction: CSBaseDataTransaction, CoreStor
|
||||
|
||||
// MARK: - SynchronousDataTransaction
|
||||
|
||||
@available(*, deprecated, message: "CoreStore Objective-C API will be removed soon.")
|
||||
extension SynchronousDataTransaction: CoreStoreSwiftType {
|
||||
|
||||
// MARK: CoreStoreSwiftType
|
||||
|
||||
@@ -34,6 +34,7 @@ import CoreData
|
||||
|
||||
- SeeAlso: `Tweak`
|
||||
*/
|
||||
@available(*, deprecated, message: "CoreStore Objective-C API will be removed soon.")
|
||||
@objc
|
||||
public final class CSTweak: NSObject, CSFetchClause, CSQueryClause, CSDeleteClause, CoreStoreObjectiveCType {
|
||||
|
||||
@@ -90,6 +91,7 @@ public final class CSTweak: NSObject, CSFetchClause, CSQueryClause, CSDeleteClau
|
||||
|
||||
// MARK: - Tweak
|
||||
|
||||
@available(*, deprecated, message: "CoreStore Objective-C API will be removed soon.")
|
||||
extension Tweak: CoreStoreSwiftType {
|
||||
|
||||
// MARK: CoreStoreSwiftType
|
||||
|
||||
@@ -34,6 +34,7 @@ import Foundation
|
||||
|
||||
- SeeAlso: `UnsafeDataModelSchema`
|
||||
*/
|
||||
@available(*, deprecated, message: "CoreStore Objective-C API will be removed soon.")
|
||||
@objc
|
||||
public final class CSUnsafeDataModelSchema: NSObject, CSDynamicSchema, CoreStoreObjectiveCType {
|
||||
|
||||
@@ -104,6 +105,7 @@ public final class CSUnsafeDataModelSchema: NSObject, CSDynamicSchema, CoreStore
|
||||
|
||||
// MARK: - UnsafeDataModelSchema
|
||||
|
||||
@available(*, deprecated, message: "CoreStore Objective-C API will be removed soon.")
|
||||
extension UnsafeDataModelSchema: CoreStoreSwiftType {
|
||||
|
||||
// MARK: CoreStoreSwiftType
|
||||
|
||||
@@ -34,6 +34,7 @@ import CoreData
|
||||
|
||||
- SeeAlso: `UnsafeDataTransaction`
|
||||
*/
|
||||
@available(*, deprecated, message: "CoreStore Objective-C API will be removed soon.")
|
||||
@objc
|
||||
public final class CSUnsafeDataTransaction: CSBaseDataTransaction, CoreStoreObjectiveCType {
|
||||
/**
|
||||
@@ -208,6 +209,7 @@ public final class CSUnsafeDataTransaction: CSBaseDataTransaction, CoreStoreObje
|
||||
|
||||
// MARK: - UnsafeDataTransaction
|
||||
|
||||
@available(*, deprecated, message: "CoreStore Objective-C API will be removed soon.")
|
||||
extension UnsafeDataTransaction: CoreStoreSwiftType {
|
||||
|
||||
// MARK: CoreStoreSwiftType
|
||||
|
||||
@@ -35,6 +35,7 @@ import Foundation
|
||||
- SeeAlso: `XcodeDataModelSchema`
|
||||
*/
|
||||
@objc
|
||||
@available(*, deprecated, message: "CoreStore Objective-C API will be removed soon.")
|
||||
public final class CSXcodeDataModelSchema: NSObject, CSDynamicSchema, CoreStoreObjectiveCType {
|
||||
|
||||
/**
|
||||
@@ -104,6 +105,7 @@ public final class CSXcodeDataModelSchema: NSObject, CSDynamicSchema, CoreStoreO
|
||||
|
||||
// MARK: - XcodeDataModelSchema
|
||||
|
||||
@available(*, deprecated, message: "CoreStore Objective-C API will be removed soon.")
|
||||
extension XcodeDataModelSchema: CoreStoreSwiftType {
|
||||
|
||||
// MARK: CoreStoreSwiftType
|
||||
|
||||
@@ -373,15 +373,34 @@ fileprivate struct CoreStoreFetchedSectionInfoWrapper: CoreStoreDebugStringConve
|
||||
var coreStoreDumpString: String {
|
||||
|
||||
return createFormattedString(
|
||||
"\"\(self.sectionInfo.name)\" (", ")",
|
||||
("numberOfObjects", self.sectionInfo.numberOfObjects),
|
||||
("indexTitle", self.sectionInfo.indexTitle as Any)
|
||||
"\"\(self.sectionName)\" (", ")",
|
||||
("numberOfObjects", self.numberOfObjects),
|
||||
("indexTitle", self.sectionIndexTitle as Any)
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: FilePrivate
|
||||
|
||||
let sectionInfo: NSFetchedResultsSectionInfo
|
||||
fileprivate init(_ sectionInfo: NSFetchedResultsSectionInfo) {
|
||||
|
||||
self.sectionName = sectionInfo.name
|
||||
self.numberOfObjects = sectionInfo.numberOfObjects
|
||||
self.sectionIndexTitle = sectionInfo.indexTitle
|
||||
}
|
||||
|
||||
fileprivate init(_ section: Internals.DiffableDataSourceSnapshot.Section) {
|
||||
|
||||
self.sectionName = section.differenceIdentifier
|
||||
self.numberOfObjects = section.elements.count
|
||||
self.sectionIndexTitle = nil
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let sectionName: String
|
||||
private let sectionIndexTitle: String?
|
||||
private let numberOfObjects: Int
|
||||
}
|
||||
|
||||
@available(macOS 10.12, *)
|
||||
@@ -409,6 +428,56 @@ extension ListMonitor: CustomDebugStringConvertible, CoreStoreDebugStringConvert
|
||||
}
|
||||
|
||||
|
||||
// MARK: - ListPublisher
|
||||
|
||||
@available(macOS 10.12, *)
|
||||
extension ListPublisher: CustomDebugStringConvertible, CoreStoreDebugStringConvertible {
|
||||
|
||||
// MARK: CustomDebugStringConvertible
|
||||
|
||||
public var debugDescription: String {
|
||||
|
||||
return formattedDebugDescription(self)
|
||||
}
|
||||
|
||||
|
||||
// MARK: CoreStoreDebugStringConvertible
|
||||
|
||||
public var coreStoreDumpString: String {
|
||||
|
||||
return createFormattedString(
|
||||
"(", ")",
|
||||
("snapshot", self.snapshot)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - ListSnapshot
|
||||
|
||||
extension ListSnapshot: CustomDebugStringConvertible, CoreStoreDebugStringConvertible {
|
||||
|
||||
// MARK: CustomDebugStringConvertible
|
||||
|
||||
public var debugDescription: String {
|
||||
|
||||
return formattedDebugDescription(self)
|
||||
}
|
||||
|
||||
|
||||
// MARK: CoreStoreDebugStringConvertible
|
||||
|
||||
public var coreStoreDumpString: String {
|
||||
|
||||
return createFormattedString(
|
||||
"(", ")",
|
||||
("numberOfObjects", self.numberOfItems),
|
||||
("sections", self.diffableSnapshot.sections.map(CoreStoreFetchedSectionInfoWrapper.init))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - LocalStorageOptions
|
||||
|
||||
extension LocalStorageOptions: CustomDebugStringConvertible, CoreStoreDebugStringConvertible {
|
||||
@@ -568,6 +637,56 @@ extension ObjectMonitor: CustomDebugStringConvertible, CoreStoreDebugStringConve
|
||||
}
|
||||
|
||||
|
||||
// MARK: - ObjectPublisher
|
||||
|
||||
extension ObjectPublisher: CustomDebugStringConvertible, CoreStoreDebugStringConvertible {
|
||||
|
||||
// MARK: CustomDebugStringConvertible
|
||||
|
||||
public var debugDescription: String {
|
||||
|
||||
return formattedDebugDescription(self)
|
||||
}
|
||||
|
||||
|
||||
// MARK: CoreStoreDebugStringConvertible
|
||||
|
||||
public var coreStoreDumpString: String {
|
||||
|
||||
return createFormattedString(
|
||||
"(", ")",
|
||||
("objectID", self.objectID()),
|
||||
("object", self.object as Any)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - ObjectSnapshot
|
||||
|
||||
extension ObjectSnapshot: CustomDebugStringConvertible, CoreStoreDebugStringConvertible {
|
||||
|
||||
// MARK: CustomDebugStringConvertible
|
||||
|
||||
public var debugDescription: String {
|
||||
|
||||
return formattedDebugDescription(self)
|
||||
}
|
||||
|
||||
|
||||
// MARK: CoreStoreDebugStringConvertible
|
||||
|
||||
public var coreStoreDumpString: String {
|
||||
|
||||
return createFormattedString(
|
||||
"(", ")",
|
||||
("objectID", self.objectID()),
|
||||
("dictionaryForValues", self.dictionaryForValues())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - OrderBy
|
||||
|
||||
extension OrderBy: CustomDebugStringConvertible, CoreStoreDebugStringConvertible {
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
#error CoreStore Objective-C utilities can only be used on platforms that support C function overloading
|
||||
#endif
|
||||
|
||||
#define CORESTORE_EXTERN extern
|
||||
#define CORESTORE_EXTERN extern __deprecated_msg("CoreStore Objective-C API will be removed soon.")
|
||||
#define CORESTORE_OVERLOADABLE __attribute__((__overloadable__))
|
||||
#define CORESTORE_REQUIRES_NIL_TERMINATION __attribute__((sentinel(0, 1)))
|
||||
#define CORESTORE_RETURNS_RETAINED __attribute__((ns_returns_retained))
|
||||
|
||||
@@ -31,6 +31,7 @@ import Foundation
|
||||
/**
|
||||
`CoreStoreObjectiveCType`s are Objective-C accessible classes that represent CoreStore's Swift types.
|
||||
*/
|
||||
@available(*, deprecated, message: "CoreStore Objective-C API will be removed soon.")
|
||||
public protocol CoreStoreObjectiveCType: AnyObject {
|
||||
|
||||
/**
|
||||
@@ -55,6 +56,7 @@ public protocol CoreStoreObjectiveCType: AnyObject {
|
||||
/**
|
||||
`CoreStoreSwiftType`s are CoreStore's Swift types that are bridgeable to Objective-C.
|
||||
*/
|
||||
@available(*, deprecated, message: "CoreStore Objective-C API will be removed soon.")
|
||||
public protocol CoreStoreSwiftType {
|
||||
|
||||
/**
|
||||
@@ -71,21 +73,25 @@ public protocol CoreStoreSwiftType {
|
||||
|
||||
// MARK: - Internal
|
||||
|
||||
@available(*, deprecated, message: "CoreStore Objective-C API will be removed soon.")
|
||||
internal func bridge<T: CoreStoreSwiftType>(_ closure: () -> T) -> T.ObjectiveCType {
|
||||
|
||||
return closure().bridgeToObjectiveC
|
||||
}
|
||||
|
||||
@available(*, deprecated, message: "CoreStore Objective-C API will be removed soon.")
|
||||
internal func bridge<T: CoreStoreSwiftType>(_ closure: () -> [T]) -> [T.ObjectiveCType] {
|
||||
|
||||
return closure().map { $0.bridgeToObjectiveC }
|
||||
}
|
||||
|
||||
@available(*, deprecated, message: "CoreStore Objective-C API will be removed soon.")
|
||||
internal func bridge<T: CoreStoreSwiftType>(_ closure: () -> T?) -> T.ObjectiveCType? {
|
||||
|
||||
return closure()?.bridgeToObjectiveC
|
||||
}
|
||||
|
||||
@available(*, deprecated, message: "CoreStore Objective-C API will be removed soon.")
|
||||
internal func bridge<T: CoreStoreSwiftType>(_ closure: () throws -> T) throws -> T.ObjectiveCType {
|
||||
|
||||
do {
|
||||
@@ -98,6 +104,7 @@ internal func bridge<T: CoreStoreSwiftType>(_ closure: () throws -> T) throws ->
|
||||
}
|
||||
}
|
||||
|
||||
@available(*, deprecated, message: "CoreStore Objective-C API will be removed soon.")
|
||||
internal func bridge(_ closure: () throws -> Void) throws {
|
||||
|
||||
do {
|
||||
@@ -110,6 +117,7 @@ internal func bridge(_ closure: () throws -> Void) throws {
|
||||
}
|
||||
}
|
||||
|
||||
@available(*, deprecated, message: "CoreStore Objective-C API will be removed soon.")
|
||||
internal func bridge<T: CoreStoreSwiftType>(_ error: NSErrorPointer, _ closure: () throws -> T) -> T.ObjectiveCType? {
|
||||
|
||||
do {
|
||||
@@ -125,6 +133,7 @@ internal func bridge<T: CoreStoreSwiftType>(_ error: NSErrorPointer, _ closure:
|
||||
}
|
||||
}
|
||||
|
||||
@available(*, deprecated, message: "CoreStore Objective-C API will be removed soon.")
|
||||
internal func bridge(_ error: NSErrorPointer, _ closure: () throws -> Void) -> Bool {
|
||||
|
||||
do {
|
||||
@@ -140,6 +149,7 @@ internal func bridge(_ error: NSErrorPointer, _ closure: () throws -> Void) -> B
|
||||
}
|
||||
}
|
||||
|
||||
@available(*, deprecated, message: "CoreStore Objective-C API will be removed soon.")
|
||||
internal func bridge<T>(_ error: NSErrorPointer, _ closure: () throws -> T?) -> T? {
|
||||
|
||||
do {
|
||||
@@ -155,6 +165,7 @@ internal func bridge<T>(_ error: NSErrorPointer, _ closure: () throws -> T?) ->
|
||||
}
|
||||
}
|
||||
|
||||
@available(*, deprecated, message: "CoreStore Objective-C API will be removed soon.")
|
||||
internal func bridge<T: CoreStoreSwiftType>(_ error: NSErrorPointer, _ closure: () throws -> [T]) -> [T.ObjectiveCType]? {
|
||||
|
||||
do {
|
||||
|
||||
@@ -41,7 +41,19 @@ extension DataStack {
|
||||
*/
|
||||
public func publishObject<O: DynamicObject>(_ object: O) -> ObjectPublisher<O> {
|
||||
|
||||
return ObjectPublisher<O>(objectID: object.cs_id(), context: self.unsafeContext())
|
||||
return self.publishObject(object.cs_id())
|
||||
}
|
||||
|
||||
/**
|
||||
Creates an `ObjectPublisher` for a `DynamicObject` with the specified `ObjectID`. Multiple objects may then register themselves to be notified when changes are made to the `DynamicObject`.
|
||||
|
||||
- parameter objectID: the `ObjectID` of the object to observe changes from
|
||||
- returns: an `ObjectPublisher` that broadcasts changes to `object`
|
||||
*/
|
||||
public func publishObject<O: DynamicObject>(_ objectID: O.ObjectID) -> ObjectPublisher<O> {
|
||||
|
||||
let context = self.unsafeContext()
|
||||
return context.objectPublisher(objectID: objectID)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
216
Sources/DiffableDataSource.BaseAdapter.swift
Normal file
216
Sources/DiffableDataSource.BaseAdapter.swift
Normal file
@@ -0,0 +1,216 @@
|
||||
//
|
||||
// DiffableDataSource.BaseAdapter.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2018 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CoreData
|
||||
|
||||
|
||||
// MARK: - DiffableDataSource
|
||||
|
||||
extension DiffableDataSource {
|
||||
|
||||
// MARK: - BaseAdapter
|
||||
|
||||
/**
|
||||
The `DiffableDataSource.BaseAdapter` serves as a superclass for consumers of `ListPublisher` and `ListSnapshot` diffable data.
|
||||
```
|
||||
self.dataSource = DiffableDataSource.TableViewAdapter<Person>(
|
||||
tableView: self.tableView,
|
||||
dataStack: CoreStoreDefaults.dataStack,
|
||||
cellProvider: { (tableView, indexPath, person) in
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "PersonCell") as! PersonCell
|
||||
cell.setPerson(person)
|
||||
return cell
|
||||
}
|
||||
)
|
||||
```
|
||||
The dataSource can then apply changes from a `ListPublisher` as shown:
|
||||
```
|
||||
listPublisher.addObserver(self) { [weak self] (listPublisher) in
|
||||
self?.dataSource?.apply(
|
||||
listPublisher.snapshot,
|
||||
animatingDifferences: true
|
||||
)
|
||||
}
|
||||
```
|
||||
- SeeAlso: CoreStore's DiffableDataSource implementation is based on https://github.com/ra1028/DiffableDataSources
|
||||
*/
|
||||
open class BaseAdapter<O: DynamicObject, T: Target>: NSObject {
|
||||
|
||||
// MARK: Public
|
||||
|
||||
/**
|
||||
The object type represented by this dataSource
|
||||
*/
|
||||
public typealias ObjectType = O
|
||||
|
||||
/**
|
||||
The target to be updated by this dataSource
|
||||
*/
|
||||
public let target: T
|
||||
|
||||
/**
|
||||
The `DataStack` where object fetches are performed
|
||||
*/
|
||||
public let dataStack: DataStack
|
||||
|
||||
/**
|
||||
Initializes the `DiffableDataSource.BaseAdapter` object. This instance needs to be held on (retained) for as long as the target's lifecycle.
|
||||
```
|
||||
self.dataSource = DiffableDataSource.TableViewAdapterAdapter<Person>(
|
||||
tableView: self.tableView,
|
||||
dataStack: CoreStoreDefaults.dataStack,
|
||||
cellProvider: { (tableView, indexPath, person) in
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "PersonCell") as! PersonCell
|
||||
cell.setPerson(person)
|
||||
return cell
|
||||
}
|
||||
)
|
||||
```
|
||||
- parameter tableView: the `UITableView` to set the `dataSource` of. This instance is not retained by the `DiffableDataSource.TableViewAdapter`.
|
||||
- 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
|
||||
*/
|
||||
public init(target: T, dataStack: DataStack) {
|
||||
|
||||
self.target = target
|
||||
self.dataStack = dataStack
|
||||
self.dispatcher = Internals.DiffableDataUIDispatcher<O>(dataStack: dataStack)
|
||||
}
|
||||
|
||||
/**
|
||||
Clears the target.
|
||||
- parameter animatingDifferences: if `true`, animations may be applied accordingly. Defaults to `true`.
|
||||
*/
|
||||
open func purge(animatingDifferences: Bool = true, completion: @escaping () -> Void = {}) {
|
||||
|
||||
self.dispatcher.purge(
|
||||
target: self.target,
|
||||
animatingDifferences: animatingDifferences,
|
||||
performUpdates: { target, changeset, setSections in
|
||||
|
||||
target.reload(
|
||||
using: changeset,
|
||||
animated: animatingDifferences,
|
||||
setData: setSections
|
||||
)
|
||||
},
|
||||
completion: completion
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
Reloads the target using a `ListSnapshot`. This is typically from the `snapshot` property of a `ListPublisher`:
|
||||
```
|
||||
listPublisher.addObserver(self) { [weak self] (listPublisher) in
|
||||
self?.dataSource?.apply(
|
||||
listPublisher.snapshot,
|
||||
animatingDifferences: true
|
||||
)
|
||||
}
|
||||
``
|
||||
- parameter snapshot: the `ListSnapshot` used to reload the target with. This is typically from the `snapshot` property of a `ListPublisher`.
|
||||
- parameter animatingDifferences: if `true`, animations may be applied accordingly. Defaults to `true`.
|
||||
*/
|
||||
open func apply(_ snapshot: ListSnapshot<O>, animatingDifferences: Bool = true, completion: @escaping () -> Void = {}) {
|
||||
|
||||
let diffableSnapshot = snapshot.diffableSnapshot
|
||||
self.dispatcher.apply(
|
||||
diffableSnapshot,
|
||||
target: self.target,
|
||||
animatingDifferences: animatingDifferences,
|
||||
performUpdates: { target, changeset, setSections in
|
||||
|
||||
target.reload(
|
||||
using: changeset,
|
||||
animated: animatingDifferences,
|
||||
setData: setSections
|
||||
)
|
||||
},
|
||||
completion: completion
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the number of sections
|
||||
|
||||
- parameter indexPath: the `IndexPath` to search for
|
||||
- returns: the number of sections
|
||||
*/
|
||||
public func numberOfSections() -> Int {
|
||||
|
||||
return self.dispatcher.numberOfSections()
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the number of items at the specified section, or `nil` if the section is not found
|
||||
|
||||
- parameter section: the section index to search for
|
||||
- returns: the number of items at the specified section, or `nil` if the section is not found
|
||||
*/
|
||||
public func numberOfItems(inSection section: Int) -> Int? {
|
||||
|
||||
return self.dispatcher.numberOfItems(inSection: section)
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the section identifier at the specified index, or `nil` if not found
|
||||
|
||||
- parameter section: the section index to search for
|
||||
- returns: the section identifier at the specified indec, or `nil` if not found
|
||||
*/
|
||||
public func sectionID(for section: Int) -> String? {
|
||||
|
||||
return self.dispatcher.sectionIdentifier(inSection: section)
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the object identifier for the item at the specified `IndexPath`, or `nil` if not found
|
||||
|
||||
- parameter indexPath: the `IndexPath` to search for
|
||||
- returns: the object identifier for the item at the specified `IndexPath`, or `nil` if not found
|
||||
*/
|
||||
public func itemID(for indexPath: IndexPath) -> O.ObjectID? {
|
||||
|
||||
return self.dispatcher.itemIdentifier(for: indexPath)
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the `IndexPath` for the item with the specified object identifier, or `nil` if not found
|
||||
|
||||
- parameter itemID: the object identifier to search for
|
||||
- returns: the `IndexPath` for the item with the specified object identifier, or `nil` if not found
|
||||
*/
|
||||
public func indexPath(for itemID: O.ObjectID) -> IndexPath? {
|
||||
|
||||
return self.dispatcher.indexPath(for: itemID)
|
||||
}
|
||||
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
internal let dispatcher: Internals.DiffableDataUIDispatcher<O>
|
||||
}
|
||||
}
|
||||
@@ -1,297 +0,0 @@
|
||||
//
|
||||
// DiffableDataSource.CollectionView-UIKit.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2018 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
#if canImport(UIKit) && (os(iOS) || os(tvOS))
|
||||
|
||||
import UIKit
|
||||
import CoreData
|
||||
|
||||
|
||||
// MARK: - DiffableDataSource
|
||||
|
||||
extension DiffableDataSource {
|
||||
|
||||
// MARK: - CollectionView
|
||||
|
||||
/**
|
||||
The `DiffableDataSource.CollectionView` serves as a `UICollectionViewDataSource` that handles `ListPublisher` snapshots for a `UICollectionView`. Subclasses of `DiffableDataSource.CollectionView` may override some `UICollectionViewDataSource` methods as needed.
|
||||
The `DiffableDataSource.CollectionView` instance needs to be held on (retained) for as long as the `UICollectionView`'s lifecycle.
|
||||
```
|
||||
self.dataSource = DiffableDataSource.CollectionView<Person>(
|
||||
collectionView: self.collectionView,
|
||||
dataStack: CoreStoreDefaults.dataStack,
|
||||
cellProvider: { (collectionView, indexPath, person) in
|
||||
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "PersonCell") as! PersonCell
|
||||
cell.setPerson(person)
|
||||
return cell
|
||||
}
|
||||
)
|
||||
```
|
||||
The dataSource can then apply changes from a `ListPublisher` as shown:
|
||||
```
|
||||
listPublisher.addObserver(self) { [weak self] (listPublisher) in
|
||||
self?.dataSource?.apply(
|
||||
listPublisher.snapshot,
|
||||
animatingDifferences: true
|
||||
)
|
||||
}
|
||||
```
|
||||
`DiffableDataSource.CollectionView` fully handles the reload animations.
|
||||
- SeeAlso: CoreStore's DiffableDataSource implementation is based on https://github.com/ra1028/DiffableDataSources
|
||||
*/
|
||||
open class CollectionView<O: DynamicObject>: NSObject, UICollectionViewDataSource {
|
||||
|
||||
// MARK: Public
|
||||
|
||||
/**
|
||||
The object type represented by this dataSource
|
||||
*/
|
||||
public typealias ObjectType = O
|
||||
|
||||
/**
|
||||
Initializes the `DiffableDataSource.CollectionView`. This instance needs to be held on (retained) for as long as the `UICollectionView`'s lifecycle.
|
||||
```
|
||||
self.dataSource = DiffableDataSource.CollectionView<Person>(
|
||||
collectionView: self.collectionView,
|
||||
dataStack: CoreStoreDefaults.dataStack,
|
||||
cellProvider: { (collectionView, indexPath, person) in
|
||||
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "PersonCell") as! PersonCell
|
||||
cell.setPerson(person)
|
||||
return cell
|
||||
}
|
||||
)
|
||||
```
|
||||
- parameter collectionView: the `UICollectionView` to set the `dataSource` of. This instance is not retained by the `DiffableDataSource.CollectionView`.
|
||||
- parameter dataStack: the `DataStack` instance that the dataSource will fetch objects from
|
||||
- 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`
|
||||
*/
|
||||
@nonobjc
|
||||
public init(collectionView: UICollectionView, dataStack: DataStack, cellProvider: @escaping (UICollectionView, IndexPath, O) -> UICollectionViewCell?, supplementaryViewProvider: @escaping (UICollectionView, String, IndexPath) -> UICollectionReusableView? = { _, _, _ in nil }) {
|
||||
|
||||
self.collectionView = collectionView
|
||||
self.cellProvider = cellProvider
|
||||
self.supplementaryViewProvider = supplementaryViewProvider
|
||||
self.dataStack = dataStack
|
||||
self.dispatcher = Internals.DiffableDataUIDispatcher<O>(dataStack: dataStack)
|
||||
|
||||
super.init()
|
||||
|
||||
collectionView.dataSource = self
|
||||
}
|
||||
|
||||
/**
|
||||
Reloads the `UICollectionView` using a `ListSnapshot`. This is typically from the `snapshot` property of a `ListPublisher`:
|
||||
```
|
||||
listPublisher.addObserver(self) { [weak self] (listPublisher) in
|
||||
self?.dataSource?.apply(
|
||||
listPublisher.snapshot,
|
||||
animatingDifferences: true
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
- parameter snapshot: the `ListSnapshot` used to reload the `UITableView` with. This is typically from the `snapshot` property of a `ListPublisher`.
|
||||
- parameter animatingDifferences: if `true`, animations will be applied as configured by the `defaultRowAnimation` value. Defaults to `true`.
|
||||
*/
|
||||
public func apply(_ snapshot: ListSnapshot<O>, animatingDifferences: Bool = true) {
|
||||
|
||||
let diffableSnapshot = snapshot.diffableSnapshot
|
||||
self.dispatcher.apply(
|
||||
diffableSnapshot as! Internals.DiffableDataSourceSnapshot,
|
||||
view: self.collectionView,
|
||||
animatingDifferences: animatingDifferences,
|
||||
performUpdates: { collectionView, changeset, setSections in
|
||||
|
||||
collectionView.reload(
|
||||
using: changeset,
|
||||
setData: setSections
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the object identifier for the item at the specified `IndexPath`, or `nil` if not found
|
||||
|
||||
- parameter indexPath: the `IndexPath` to search for
|
||||
- returns: the object identifier for the item at the specified `IndexPath`, or `nil` if not found
|
||||
*/
|
||||
@nonobjc
|
||||
public func itemID(for indexPath: IndexPath) -> O.ObjectID? {
|
||||
|
||||
return self.dispatcher.itemIdentifier(for: indexPath)
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the `IndexPath` for the item with the specified object identifier, or `nil` if not found
|
||||
|
||||
- parameter itemID: the object identifier to search for
|
||||
- returns: the `IndexPath` for the item with the specified object identifier, or `nil` if not found
|
||||
*/
|
||||
@nonobjc
|
||||
public func indexPath(for itemID: O.ObjectID) -> IndexPath? {
|
||||
|
||||
return self.dispatcher.indexPath(for: itemID)
|
||||
}
|
||||
|
||||
|
||||
// MARK: - UICollectionViewDataSource
|
||||
|
||||
@objc
|
||||
public dynamic func numberOfSections(in collectionView: UICollectionView) -> Int {
|
||||
|
||||
return self.dispatcher.numberOfSections()
|
||||
}
|
||||
|
||||
@objc
|
||||
public dynamic func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||
|
||||
return self.dispatcher.numberOfItems(inSection: section)
|
||||
}
|
||||
|
||||
@objc
|
||||
open dynamic func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||
|
||||
guard let objectID = self.dispatcher.itemIdentifier(for: indexPath) else {
|
||||
|
||||
Internals.abort("Object at \(Internals.typeName(IndexPath.self)) \(indexPath) already removed from list")
|
||||
}
|
||||
guard let object = self.dataStack.fetchExisting(objectID) as O? else {
|
||||
|
||||
Internals.abort("Object at \(Internals.typeName(IndexPath.self)) \(indexPath) has been deleted")
|
||||
}
|
||||
guard let cell = self.cellProvider(collectionView, indexPath, object) else {
|
||||
|
||||
Internals.abort("\(Internals.typeName(UICollectionViewDataSource.self)) returned a `nil` cell for \(Internals.typeName(IndexPath.self)) \(indexPath)")
|
||||
}
|
||||
return cell
|
||||
}
|
||||
|
||||
@objc
|
||||
open dynamic func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
|
||||
|
||||
guard let view = self.supplementaryViewProvider(collectionView, kind, indexPath) else {
|
||||
|
||||
return UICollectionReusableView()
|
||||
}
|
||||
return view
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private weak var collectionView: UICollectionView?
|
||||
|
||||
private let dataStack: DataStack
|
||||
private let cellProvider: (UICollectionView, IndexPath, O) -> UICollectionViewCell?
|
||||
private let supplementaryViewProvider: (UICollectionView, String, IndexPath) -> UICollectionReusableView?
|
||||
private let dispatcher: Internals.DiffableDataUIDispatcher<O>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - UICollectionView
|
||||
|
||||
extension UICollectionView {
|
||||
|
||||
// MARK: FilePrivate
|
||||
|
||||
// Implementation based on https://github.com/ra1028/DiffableDataSources
|
||||
@nonobjc
|
||||
fileprivate func reload<C, O>(
|
||||
using stagedChangeset: Internals.DiffableDataUIDispatcher<O>.StagedChangeset<C>,
|
||||
interrupt: ((Internals.DiffableDataUIDispatcher<O>.Changeset<C>) -> Bool)? = nil,
|
||||
setData: (C) -> Void
|
||||
) {
|
||||
|
||||
if case .none = window, let data = stagedChangeset.last?.data {
|
||||
|
||||
setData(data)
|
||||
self.reloadData()
|
||||
return
|
||||
}
|
||||
for changeset in stagedChangeset {
|
||||
|
||||
if let interrupt = interrupt, interrupt(changeset), let data = stagedChangeset.last?.data {
|
||||
|
||||
setData(data)
|
||||
self.reloadData()
|
||||
return
|
||||
}
|
||||
self.performBatchUpdates(
|
||||
{
|
||||
setData(changeset.data)
|
||||
|
||||
if !changeset.sectionDeleted.isEmpty {
|
||||
|
||||
self.deleteSections(IndexSet(changeset.sectionDeleted))
|
||||
}
|
||||
if !changeset.sectionInserted.isEmpty {
|
||||
|
||||
self.insertSections(IndexSet(changeset.sectionInserted))
|
||||
}
|
||||
if !changeset.sectionUpdated.isEmpty {
|
||||
|
||||
self.reloadSections(IndexSet(changeset.sectionUpdated))
|
||||
}
|
||||
for (source, target) in changeset.sectionMoved {
|
||||
|
||||
self.moveSection(source, toSection: target)
|
||||
}
|
||||
if !changeset.elementDeleted.isEmpty {
|
||||
|
||||
self.deleteItems(
|
||||
at: changeset.elementDeleted.map { IndexPath(item: $0.element, section: $0.section) }
|
||||
)
|
||||
}
|
||||
if !changeset.elementInserted.isEmpty {
|
||||
|
||||
self.insertItems(
|
||||
at: changeset.elementInserted.map { IndexPath(item: $0.element, section: $0.section) }
|
||||
)
|
||||
}
|
||||
if !changeset.elementUpdated.isEmpty {
|
||||
|
||||
self.reloadItems(
|
||||
at: changeset.elementUpdated.map { IndexPath(item: $0.element, section: $0.section) }
|
||||
)
|
||||
}
|
||||
for (source, target) in changeset.elementMoved {
|
||||
|
||||
self.moveItem(
|
||||
at: IndexPath(item: source.element, section: source.section),
|
||||
to: IndexPath(item: target.element, section: target.section)
|
||||
)
|
||||
}
|
||||
},
|
||||
completion: nil
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
232
Sources/DiffableDataSource.CollectionViewAdapter-AppKit.swift
Normal file
232
Sources/DiffableDataSource.CollectionViewAdapter-AppKit.swift
Normal file
@@ -0,0 +1,232 @@
|
||||
//
|
||||
// DiffableDataSource.CollectionViewAdapter-AppKit.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2018 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
#if canImport(AppKit) && os(macOS)
|
||||
|
||||
import AppKit
|
||||
import CoreData
|
||||
|
||||
|
||||
// MARK: - DiffableDataSource
|
||||
|
||||
extension DiffableDataSource {
|
||||
|
||||
// MARK: - CollectionView
|
||||
|
||||
/**
|
||||
The `DiffableDataSource.CollectionViewAdapter` serves as a `NSCollectionViewDataSource` that handles `ListPublisher` snapshots for a `NSCollectionView`. Subclasses of `DiffableDataSource.CollectionViewAdapter` may override some `NSCollectionViewDataSource` methods as needed.
|
||||
The `DiffableDataSource.CollectionViewAdapter` instance needs to be held on (retained) for as long as the `NSCollectionView`'s lifecycle.
|
||||
```
|
||||
self.dataSource = DiffableDataSource.CollectionViewAdapter<Person>(
|
||||
collectionView: self.collectionView,
|
||||
dataStack: CoreStoreDefaults.dataStack,
|
||||
itemProvider: { (collectionView, indexPath, person) in
|
||||
let item = collectionView.makeItem(withIdentifier: .collectionViewItem, for: indexPath) as! PersonItem
|
||||
item.setPerson(person)
|
||||
return item
|
||||
}
|
||||
)
|
||||
```
|
||||
The dataSource can then apply changes from a `ListPublisher` as shown:
|
||||
```
|
||||
listPublisher.addObserver(self) { [weak self] (listPublisher) in
|
||||
self?.dataSource?.apply(
|
||||
listPublisher.snapshot,
|
||||
animatingDifferences: true
|
||||
)
|
||||
}
|
||||
```
|
||||
`DiffableDataSource.CollectionViewAdapter` fully handles the reload animations.
|
||||
- SeeAlso: CoreStore's DiffableDataSource implementation is based on https://github.com/ra1028/DiffableDataSources
|
||||
*/
|
||||
open class CollectionViewAdapter<O: DynamicObject>: BaseAdapter<O, DefaultCollectionViewTarget<NSCollectionView>>, NSCollectionViewDataSource {
|
||||
|
||||
// MARK: Public
|
||||
|
||||
/**
|
||||
Initializes the `DiffableDataSource.CollectionViewAdapter`. This instance needs to be held on (retained) for as long as the `NSCollectionView`'s lifecycle.
|
||||
```
|
||||
self.dataSource = DiffableDataSource.CollectionViewAdapter<Person>(
|
||||
collectionView: self.collectionView,
|
||||
dataStack: CoreStoreDefaults.dataStack,
|
||||
itemProvider: { (collectionView, indexPath, person) in
|
||||
let item = collectionView.makeItem(withIdentifier: .collectionViewItem, for: indexPath) as! PersonItem
|
||||
item.setPerson(person)
|
||||
return item
|
||||
}
|
||||
)
|
||||
```
|
||||
- parameter collectionView: the `NSCollectionView` to set the `dataSource` of. This instance is not retained by the `DiffableDataSource.CollectionViewAdapter`.
|
||||
- parameter dataStack: the `DataStack` instance that the dataSource will fetch objects from
|
||||
- parameter itemProvider: a closure that configures and returns the `NSCollectionViewItem` for the object
|
||||
*/
|
||||
@nonobjc
|
||||
public init(collectionView: NSCollectionView, dataStack: DataStack, itemProvider: @escaping (NSCollectionView, IndexPath, O) -> NSCollectionViewItem?, supplementaryViewProvider: @escaping (NSCollectionView, String, IndexPath) -> NSView? = { _, _, _ in nil }) {
|
||||
|
||||
self.itemProvider = itemProvider
|
||||
self.supplementaryViewProvider = supplementaryViewProvider
|
||||
|
||||
super.init(target: .init(collectionView), dataStack: dataStack)
|
||||
|
||||
collectionView.dataSource = self
|
||||
}
|
||||
|
||||
|
||||
// MARK: - NSCollectionViewDataSource
|
||||
|
||||
@objc
|
||||
public dynamic func numberOfSections(in collectionView: NSCollectionView) -> Int {
|
||||
|
||||
return self.numberOfSections()
|
||||
}
|
||||
|
||||
@objc
|
||||
public dynamic func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||
|
||||
return self.numberOfItems(inSection: section) ?? 0
|
||||
}
|
||||
|
||||
@objc
|
||||
open dynamic func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
|
||||
|
||||
guard let objectID = self.itemID(for: indexPath) else {
|
||||
|
||||
Internals.abort("Object at \(Internals.typeName(IndexPath.self)) \(indexPath) already removed from list")
|
||||
}
|
||||
guard let object = self.dataStack.fetchExisting(objectID) as O? else {
|
||||
|
||||
Internals.abort("Object at \(Internals.typeName(IndexPath.self)) \(indexPath) has been deleted")
|
||||
}
|
||||
guard let item = self.itemProvider(collectionView, indexPath, object) else {
|
||||
|
||||
Internals.abort("\(Internals.typeName(NSCollectionViewDataSource.self)) returned a `nil` item for \(Internals.typeName(IndexPath.self)) \(indexPath)")
|
||||
}
|
||||
return item
|
||||
}
|
||||
|
||||
@objc
|
||||
open dynamic func collectionView(_ collectionView: NSCollectionView, viewForSupplementaryElementOfKind kind: NSCollectionView.SupplementaryElementKind, at indexPath: IndexPath) -> NSView {
|
||||
|
||||
guard let view = self.supplementaryViewProvider(collectionView, kind, indexPath) else {
|
||||
|
||||
return NSView()
|
||||
}
|
||||
return view
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let itemProvider: (NSCollectionView, IndexPath, O) -> NSCollectionViewItem?
|
||||
private let supplementaryViewProvider: (NSCollectionView, String, IndexPath) -> NSView?
|
||||
}
|
||||
|
||||
|
||||
// MARK: - DefaultCollectionViewTarget
|
||||
|
||||
public struct DefaultCollectionViewTarget<T: NSCollectionView>: Target {
|
||||
|
||||
// MARK: Public
|
||||
|
||||
public typealias Base = T
|
||||
|
||||
public private(set) weak var base: Base?
|
||||
|
||||
public init(_ base: Base) {
|
||||
|
||||
self.base = base
|
||||
}
|
||||
|
||||
|
||||
// MARK: DiffableDataSource.Target:
|
||||
|
||||
public var shouldSuspendBatchUpdates: Bool {
|
||||
|
||||
return self.base?.window == nil
|
||||
}
|
||||
|
||||
public func deleteSections(at indices: IndexSet, animated: Bool) {
|
||||
|
||||
self.base?.deleteSections(indices)
|
||||
}
|
||||
|
||||
public func insertSections(at indices: IndexSet, animated: Bool) {
|
||||
|
||||
self.base?.insertSections(indices)
|
||||
}
|
||||
|
||||
public func reloadSections(at indices: IndexSet, animated: Bool) {
|
||||
|
||||
self.base?.reloadSections(indices)
|
||||
}
|
||||
|
||||
public func moveSection(at index: IndexSet.Element, to newIndex: IndexSet.Element, animated: Bool) {
|
||||
|
||||
self.base?.moveSection(index, toSection: newIndex)
|
||||
}
|
||||
|
||||
public func deleteItems(at indexPaths: [IndexPath], animated: Bool) {
|
||||
|
||||
self.base?.deleteItems(at: Set(indexPaths))
|
||||
}
|
||||
|
||||
public func insertItems(at indexPaths: [IndexPath], animated: Bool) {
|
||||
|
||||
self.base?.insertItems(at: Set(indexPaths))
|
||||
}
|
||||
|
||||
public func reloadItems(at indexPaths: [IndexPath], animated: Bool) {
|
||||
|
||||
self.base?.reloadItems(at: Set(indexPaths))
|
||||
}
|
||||
|
||||
public func moveItem(at indexPath: IndexPath, to newIndexPath: IndexPath, animated: Bool) {
|
||||
|
||||
self.base?.moveItem(at: indexPath, to: newIndexPath)
|
||||
}
|
||||
|
||||
public func performBatchUpdates(updates: () -> Void, animated: Bool) {
|
||||
|
||||
self.base?.animator().performBatchUpdates(updates, completionHandler: nil)
|
||||
}
|
||||
|
||||
public func reloadData() {
|
||||
|
||||
self.base?.reloadData()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Deprecated
|
||||
|
||||
extension DiffableDataSource {
|
||||
|
||||
@available(*, deprecated, renamed: "CollectionViewAdapter")
|
||||
public typealias CollectionView = CollectionViewAdapter
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
231
Sources/DiffableDataSource.CollectionViewAdapter-UIKit.swift
Normal file
231
Sources/DiffableDataSource.CollectionViewAdapter-UIKit.swift
Normal file
@@ -0,0 +1,231 @@
|
||||
//
|
||||
// DiffableDataSource.CollectionViewAdapter-UIKit.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2018 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
#if canImport(UIKit) && (os(iOS) || os(tvOS))
|
||||
|
||||
import UIKit
|
||||
import CoreData
|
||||
|
||||
|
||||
// MARK: - DiffableDataSource
|
||||
|
||||
extension DiffableDataSource {
|
||||
|
||||
// MARK: - CollectionView
|
||||
|
||||
/**
|
||||
The `DiffableDataSource.CollectionViewAdapter` serves as a `UICollectionViewDataSource` that handles `ListPublisher` snapshots for a `UICollectionView`. Subclasses of `DiffableDataSource.CollectionViewAdapter` may override some `UICollectionViewDataSource` methods as needed.
|
||||
The `DiffableDataSource.CollectionViewAdapter` instance needs to be held on (retained) for as long as the `UICollectionView`'s lifecycle.
|
||||
```
|
||||
self.dataSource = DiffableDataSource.CollectionViewAdapter<Person>(
|
||||
collectionView: self.collectionView,
|
||||
dataStack: CoreStoreDefaults.dataStack,
|
||||
cellProvider: { (collectionView, indexPath, person) in
|
||||
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "PersonCell") as! PersonCell
|
||||
cell.setPerson(person)
|
||||
return cell
|
||||
}
|
||||
)
|
||||
```
|
||||
The dataSource can then apply changes from a `ListPublisher` as shown:
|
||||
```
|
||||
listPublisher.addObserver(self) { [weak self] (listPublisher) in
|
||||
self?.dataSource?.apply(
|
||||
listPublisher.snapshot,
|
||||
animatingDifferences: true
|
||||
)
|
||||
}
|
||||
```
|
||||
`DiffableDataSource.CollectionViewAdapter` fully handles the reload animations.
|
||||
- SeeAlso: CoreStore's DiffableDataSource implementation is based on https://github.com/ra1028/DiffableDataSources
|
||||
*/
|
||||
open class CollectionViewAdapter<O: DynamicObject>: BaseAdapter<O, DefaultCollectionViewTarget<UICollectionView>>, UICollectionViewDataSource {
|
||||
|
||||
// MARK: Public
|
||||
|
||||
/**
|
||||
Initializes the `DiffableDataSource.CollectionViewAdapter`. This instance needs to be held on (retained) for as long as the `UICollectionView`'s lifecycle.
|
||||
```
|
||||
self.dataSource = DiffableDataSource.CollectionViewAdapter<Person>(
|
||||
collectionView: self.collectionView,
|
||||
dataStack: CoreStoreDefaults.dataStack,
|
||||
cellProvider: { (collectionView, indexPath, person) in
|
||||
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "PersonCell") as! PersonCell
|
||||
cell.setPerson(person)
|
||||
return cell
|
||||
}
|
||||
)
|
||||
```
|
||||
- parameter collectionView: the `UICollectionView` to set the `dataSource` of. This instance is not retained by the `DiffableDataSource.CollectionViewAdapter`.
|
||||
- parameter dataStack: the `DataStack` instance that the dataSource will fetch objects from
|
||||
- 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`
|
||||
*/
|
||||
public init(collectionView: UICollectionView, dataStack: DataStack, cellProvider: @escaping (UICollectionView, IndexPath, O) -> UICollectionViewCell?, supplementaryViewProvider: @escaping (UICollectionView, String, IndexPath) -> UICollectionReusableView? = { _, _, _ in nil }) {
|
||||
|
||||
self.cellProvider = cellProvider
|
||||
self.supplementaryViewProvider = supplementaryViewProvider
|
||||
|
||||
super.init(target: .init(collectionView), dataStack: dataStack)
|
||||
|
||||
collectionView.dataSource = self
|
||||
}
|
||||
|
||||
|
||||
// MARK: - UICollectionViewDataSource
|
||||
|
||||
@objc
|
||||
public dynamic func numberOfSections(in collectionView: UICollectionView) -> Int {
|
||||
|
||||
return self.numberOfSections()
|
||||
}
|
||||
|
||||
@objc
|
||||
public dynamic func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||
|
||||
return self.numberOfItems(inSection: section) ?? 0
|
||||
}
|
||||
|
||||
@objc
|
||||
open dynamic func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||
|
||||
guard let objectID = self.itemID(for: indexPath) else {
|
||||
|
||||
Internals.abort("Object at \(Internals.typeName(IndexPath.self)) \(indexPath) already removed from list")
|
||||
}
|
||||
guard let object = self.dataStack.fetchExisting(objectID) as O? else {
|
||||
|
||||
Internals.abort("Object at \(Internals.typeName(IndexPath.self)) \(indexPath) has been deleted")
|
||||
}
|
||||
guard let cell = self.cellProvider(collectionView, indexPath, object) else {
|
||||
|
||||
Internals.abort("\(Internals.typeName(UICollectionViewDataSource.self)) returned a `nil` cell for \(Internals.typeName(IndexPath.self)) \(indexPath)")
|
||||
}
|
||||
return cell
|
||||
}
|
||||
|
||||
@objc
|
||||
open dynamic func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
|
||||
|
||||
guard let view = self.supplementaryViewProvider(collectionView, kind, indexPath) else {
|
||||
|
||||
return UICollectionReusableView()
|
||||
}
|
||||
return view
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let cellProvider: (UICollectionView, IndexPath, O) -> UICollectionViewCell?
|
||||
private let supplementaryViewProvider: (UICollectionView, String, IndexPath) -> UICollectionReusableView?
|
||||
}
|
||||
|
||||
|
||||
// MARK: - DefaultCollectionViewTarget
|
||||
|
||||
public struct DefaultCollectionViewTarget<T: UICollectionView>: Target {
|
||||
|
||||
// MARK: Public
|
||||
|
||||
public typealias Base = T
|
||||
|
||||
public private(set) weak var base: Base?
|
||||
|
||||
public init(_ base: Base) {
|
||||
|
||||
self.base = base
|
||||
}
|
||||
|
||||
|
||||
// MARK: DiffableDataSource.Target
|
||||
|
||||
public var shouldSuspendBatchUpdates: Bool {
|
||||
|
||||
return self.base?.window == nil
|
||||
}
|
||||
|
||||
public func deleteSections(at indices: IndexSet, animated: Bool) {
|
||||
|
||||
self.base?.deleteSections(indices)
|
||||
}
|
||||
|
||||
public func insertSections(at indices: IndexSet, animated: Bool) {
|
||||
|
||||
self.base?.insertSections(indices)
|
||||
}
|
||||
|
||||
public func reloadSections(at indices: IndexSet, animated: Bool) {
|
||||
|
||||
self.base?.reloadSections(indices)
|
||||
}
|
||||
|
||||
public func moveSection(at index: IndexSet.Element, to newIndex: IndexSet.Element, animated: Bool) {
|
||||
|
||||
self.base?.moveSection(index, toSection: newIndex)
|
||||
}
|
||||
|
||||
public func deleteItems(at indexPaths: [IndexPath], animated: Bool) {
|
||||
|
||||
self.base?.deleteItems(at: indexPaths)
|
||||
}
|
||||
|
||||
public func insertItems(at indexPaths: [IndexPath], animated: Bool) {
|
||||
|
||||
self.base?.insertItems(at: indexPaths)
|
||||
}
|
||||
|
||||
public func reloadItems(at indexPaths: [IndexPath], animated: Bool) {
|
||||
|
||||
self.base?.reloadItems(at: indexPaths)
|
||||
}
|
||||
|
||||
public func moveItem(at indexPath: IndexPath, to newIndexPath: IndexPath, animated: Bool) {
|
||||
|
||||
self.base?.moveItem(at: indexPath, to: newIndexPath)
|
||||
}
|
||||
|
||||
public func performBatchUpdates(updates: () -> Void, animated: Bool) {
|
||||
|
||||
self.base?.performBatchUpdates(updates, completion: nil)
|
||||
}
|
||||
|
||||
public func reloadData() {
|
||||
|
||||
self.base?.reloadData()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Deprecated
|
||||
|
||||
extension DiffableDataSource {
|
||||
|
||||
@available(*, deprecated, renamed: "CollectionViewAdapter")
|
||||
public typealias CollectionView = CollectionViewAdapter
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,372 +0,0 @@
|
||||
//
|
||||
// DiffableDataSource.TableView-UIKit.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2018 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
#if canImport(UIKit) && (os(iOS) || os(tvOS))
|
||||
|
||||
import UIKit
|
||||
import CoreData
|
||||
|
||||
|
||||
// MARK: - DiffableDataSource
|
||||
|
||||
extension DiffableDataSource {
|
||||
|
||||
// MARK: - TableView
|
||||
|
||||
/**
|
||||
The `DiffableDataSource.TableView` serves as a `UITableViewDataSource` that handles `ListPublisher` snapshots for a `UITableView`. Subclasses of `DiffableDataSource.TableView` may override some `UITableViewDataSource` methods as needed.
|
||||
The `DiffableDataSource.TableView` instance needs to be held on (retained) for as long as the `UITableView`'s lifecycle.
|
||||
```
|
||||
self.dataSource = DiffableDataSource.TableView<Person>(
|
||||
tableView: self.tableView,
|
||||
dataStack: CoreStoreDefaults.dataStack,
|
||||
cellProvider: { (tableView, indexPath, person) in
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "PersonCell") as! PersonCell
|
||||
cell.setPerson(person)
|
||||
return cell
|
||||
}
|
||||
)
|
||||
```
|
||||
The dataSource can then apply changes from a `ListPublisher` as shown:
|
||||
```
|
||||
listPublisher.addObserver(self) { [weak self] (listPublisher) in
|
||||
self?.dataSource?.apply(
|
||||
listPublisher.snapshot,
|
||||
animatingDifferences: true
|
||||
)
|
||||
}
|
||||
```
|
||||
`DiffableDataSource.TableView` fully handles the reload animations. To turn change the default animation, set the `defaultRowAnimation`.
|
||||
- SeeAlso: CoreStore's DiffableDataSource implementation is based on https://github.com/ra1028/DiffableDataSources
|
||||
*/
|
||||
open class TableView<O: DynamicObject>: NSObject, UITableViewDataSource {
|
||||
|
||||
// MARK: Open
|
||||
|
||||
/**
|
||||
The animation style for row changes
|
||||
*/
|
||||
@nonobjc
|
||||
open var defaultRowAnimation: UITableView.RowAnimation = .automatic
|
||||
|
||||
|
||||
// MARK: Public
|
||||
|
||||
/**
|
||||
The object type represented by this dataSource
|
||||
*/
|
||||
public typealias ObjectType = O
|
||||
|
||||
/**
|
||||
Initializes the `DiffableDataSource.TableView`. This instance needs to be held on (retained) for as long as the `UITableView`'s lifecycle.
|
||||
```
|
||||
self.dataSource = DiffableDataSource.TableView<Person>(
|
||||
tableView: self.tableView,
|
||||
dataStack: CoreStoreDefaults.dataStack,
|
||||
cellProvider: { (tableView, indexPath, person) in
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "PersonCell") as! PersonCell
|
||||
cell.setPerson(person)
|
||||
return cell
|
||||
}
|
||||
)
|
||||
```
|
||||
- parameter tableView: the `UITableView` to set the `dataSource` of. This instance is not retained by the `DiffableDataSource.TableView`.
|
||||
- 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
|
||||
*/
|
||||
@nonobjc
|
||||
public init(tableView: UITableView, dataStack: DataStack, cellProvider: @escaping (UITableView, IndexPath, O) -> UITableViewCell?) {
|
||||
|
||||
self.tableView = tableView
|
||||
self.cellProvider = cellProvider
|
||||
self.dataStack = dataStack
|
||||
self.dispatcher = Internals.DiffableDataUIDispatcher<O>(dataStack: dataStack)
|
||||
|
||||
super.init()
|
||||
|
||||
tableView.dataSource = self
|
||||
}
|
||||
|
||||
/**
|
||||
Reloads the `UITableView` using a `ListSnapshot`. This is typically from the `snapshot` property of a `ListPublisher`:
|
||||
```
|
||||
listPublisher.addObserver(self) { [weak self] (listPublisher) in
|
||||
self?.dataSource?.apply(
|
||||
listPublisher.snapshot,
|
||||
animatingDifferences: true
|
||||
)
|
||||
}
|
||||
```
|
||||
If the `defaultRowAnimation` is configured to, animations are also applied accordingly.
|
||||
|
||||
- parameter snapshot: the `ListSnapshot` used to reload the `UITableView` with. This is typically from the `snapshot` property of a `ListPublisher`.
|
||||
- parameter animatingDifferences: if `true`, animations will be applied as configured by the `defaultRowAnimation` value. Defaults to `true`.
|
||||
*/
|
||||
@nonobjc
|
||||
public func apply(_ snapshot: ListSnapshot<O>, animatingDifferences: Bool = true) {
|
||||
|
||||
let diffableSnapshot = snapshot.diffableSnapshot
|
||||
// if #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) {
|
||||
//
|
||||
// self.modernDataSource.apply(
|
||||
// diffableSnapshot as! NSDiffableDataSourceSnapshot<String, NSManagedObjectID>,
|
||||
// animatingDifferences: animatingDifferences,
|
||||
// completion: nil
|
||||
// )
|
||||
// }
|
||||
// else {
|
||||
|
||||
self.dispatcher.apply(
|
||||
diffableSnapshot as! Internals.DiffableDataSourceSnapshot,
|
||||
view: self.tableView,
|
||||
animatingDifferences: animatingDifferences,
|
||||
performUpdates: { tableView, changeset, setSections in
|
||||
|
||||
tableView.reload(
|
||||
using: changeset,
|
||||
with: self.defaultRowAnimation,
|
||||
setData: setSections
|
||||
)
|
||||
}
|
||||
)
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the object identifier for the item at the specified `IndexPath`, or `nil` if not found
|
||||
|
||||
- parameter indexPath: the `IndexPath` to search for
|
||||
- returns: the object identifier for the item at the specified `IndexPath`, or `nil` if not found
|
||||
*/
|
||||
@nonobjc
|
||||
public func itemID(for indexPath: IndexPath) -> O.ObjectID? {
|
||||
|
||||
return self.dispatcher.itemIdentifier(for: indexPath)
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the `IndexPath` for the item with the specified object identifier, or `nil` if not found
|
||||
|
||||
- parameter itemID: the object identifier to search for
|
||||
- returns: the `IndexPath` for the item with the specified object identifier, or `nil` if not found
|
||||
*/
|
||||
@nonobjc
|
||||
public func indexPath(for itemID: O.ObjectID) -> IndexPath? {
|
||||
|
||||
return self.dispatcher.indexPath(for: itemID)
|
||||
}
|
||||
|
||||
|
||||
// MARK: - UITableViewDataSource
|
||||
|
||||
@objc
|
||||
public dynamic func numberOfSections(in tableView: UITableView) -> Int {
|
||||
|
||||
return self.dispatcher.numberOfSections()
|
||||
}
|
||||
|
||||
@objc
|
||||
public dynamic func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
|
||||
return self.dispatcher.numberOfItems(inSection: section)
|
||||
}
|
||||
|
||||
@objc
|
||||
open dynamic func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||
|
||||
return self.dispatcher.sectionIdentifier(inSection: section)
|
||||
}
|
||||
|
||||
@objc
|
||||
open dynamic func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@objc
|
||||
open dynamic func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
|
||||
guard let objectID = self.dispatcher.itemIdentifier(for: indexPath) else {
|
||||
|
||||
Internals.abort("Object at \(Internals.typeName(IndexPath.self)) \(indexPath) already removed from list")
|
||||
}
|
||||
guard let object = self.dataStack.fetchExisting(objectID) as O? else {
|
||||
|
||||
Internals.abort("Object at \(Internals.typeName(IndexPath.self)) \(indexPath) has been deleted")
|
||||
}
|
||||
guard let cell = self.cellProvider(tableView, indexPath, object) else {
|
||||
|
||||
Internals.abort("\(Internals.typeName(UITableViewDataSource.self)) returned a `nil` cell for \(Internals.typeName(IndexPath.self)) \(indexPath)")
|
||||
}
|
||||
return cell
|
||||
}
|
||||
|
||||
@objc
|
||||
open dynamic func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@objc
|
||||
open dynamic func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle {
|
||||
|
||||
return .delete
|
||||
}
|
||||
|
||||
@objc
|
||||
open dynamic func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
@nonobjc
|
||||
private weak var tableView: UITableView?
|
||||
|
||||
@nonobjc
|
||||
private let dataStack: DataStack
|
||||
|
||||
@nonobjc
|
||||
private let cellProvider: (UITableView, IndexPath, O) -> UITableViewCell?
|
||||
|
||||
@nonobjc
|
||||
private let dispatcher: Internals.DiffableDataUIDispatcher<O>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - UITableView
|
||||
|
||||
extension UITableView {
|
||||
|
||||
// MARK: FilePrivate
|
||||
|
||||
// Implementation based on https://github.com/ra1028/DiffableDataSources
|
||||
@nonobjc
|
||||
fileprivate func reload<C, O>(
|
||||
using stagedChangeset: Internals.DiffableDataUIDispatcher<O>.StagedChangeset<C>,
|
||||
with animation: @autoclosure () -> RowAnimation,
|
||||
interrupt: ((Internals.DiffableDataUIDispatcher<O>.Changeset<C>) -> Bool)? = nil,
|
||||
setData: (C) -> Void
|
||||
) {
|
||||
|
||||
self.reload(
|
||||
using: stagedChangeset,
|
||||
deleteSectionsAnimation: animation(),
|
||||
insertSectionsAnimation: animation(),
|
||||
reloadSectionsAnimation: animation(),
|
||||
deleteRowsAnimation: animation(),
|
||||
insertRowsAnimation: animation(),
|
||||
reloadRowsAnimation: animation(),
|
||||
interrupt: interrupt,
|
||||
setData: setData
|
||||
)
|
||||
}
|
||||
|
||||
// Implementation based on https://github.com/ra1028/DiffableDataSources
|
||||
@nonobjc
|
||||
fileprivate func reload<C, O>(
|
||||
using stagedChangeset: Internals.DiffableDataUIDispatcher<O>.StagedChangeset<C>,
|
||||
deleteSectionsAnimation: @autoclosure () -> RowAnimation,
|
||||
insertSectionsAnimation: @autoclosure () -> RowAnimation,
|
||||
reloadSectionsAnimation: @autoclosure () -> RowAnimation,
|
||||
deleteRowsAnimation: @autoclosure () -> RowAnimation,
|
||||
insertRowsAnimation: @autoclosure () -> RowAnimation,
|
||||
reloadRowsAnimation: @autoclosure () -> RowAnimation,
|
||||
interrupt: ((Internals.DiffableDataUIDispatcher<O>.Changeset<C>) -> Bool)? = nil,
|
||||
setData: (C) -> Void
|
||||
) {
|
||||
|
||||
if case .none = window, let data = stagedChangeset.last?.data {
|
||||
|
||||
setData(data)
|
||||
self.reloadData()
|
||||
return
|
||||
}
|
||||
for changeset in stagedChangeset {
|
||||
|
||||
if let interrupt = interrupt, interrupt(changeset), let data = stagedChangeset.last?.data {
|
||||
|
||||
setData(data)
|
||||
self.reloadData()
|
||||
return
|
||||
}
|
||||
self.cs_performBatchUpdates {
|
||||
|
||||
setData(changeset.data)
|
||||
|
||||
if !changeset.sectionDeleted.isEmpty {
|
||||
|
||||
self.deleteSections(IndexSet(changeset.sectionDeleted), with: deleteSectionsAnimation())
|
||||
}
|
||||
if !changeset.sectionInserted.isEmpty {
|
||||
|
||||
self.insertSections(IndexSet(changeset.sectionInserted), with: insertSectionsAnimation())
|
||||
}
|
||||
if !changeset.sectionUpdated.isEmpty {
|
||||
|
||||
self.reloadSections(IndexSet(changeset.sectionUpdated), with: reloadSectionsAnimation())
|
||||
}
|
||||
for (source, target) in changeset.sectionMoved {
|
||||
|
||||
self.moveSection(source, toSection: target)
|
||||
}
|
||||
if !changeset.elementDeleted.isEmpty {
|
||||
|
||||
self.deleteRows(at: changeset.elementDeleted.map { IndexPath(row: $0.element, section: $0.section) }, with: deleteRowsAnimation())
|
||||
}
|
||||
if !changeset.elementInserted.isEmpty {
|
||||
|
||||
self.insertRows(at: changeset.elementInserted.map { IndexPath(row: $0.element, section: $0.section) }, with: insertRowsAnimation())
|
||||
}
|
||||
if !changeset.elementUpdated.isEmpty {
|
||||
|
||||
self.reloadRows(at: changeset.elementUpdated.map { IndexPath(row: $0.element, section: $0.section) }, with: reloadRowsAnimation())
|
||||
}
|
||||
for (source, target) in changeset.elementMoved {
|
||||
|
||||
self.moveRow(at: IndexPath(row: source.element, section: source.section), to: IndexPath(row: target.element, section: target.section))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
private func cs_performBatchUpdates(_ updates: () -> Void) {
|
||||
|
||||
if #available(iOS 11.0, tvOS 11.0, *) {
|
||||
|
||||
self.performBatchUpdates(updates)
|
||||
}
|
||||
else {
|
||||
|
||||
self.beginUpdates()
|
||||
updates()
|
||||
self.endUpdates()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
266
Sources/DiffableDataSource.TableViewAdapter-UIKit.swift
Normal file
266
Sources/DiffableDataSource.TableViewAdapter-UIKit.swift
Normal file
@@ -0,0 +1,266 @@
|
||||
//
|
||||
// DiffableDataSource.TableViewAdapter-UIKit.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2018 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
#if canImport(UIKit) && (os(iOS) || os(tvOS))
|
||||
|
||||
import UIKit
|
||||
import CoreData
|
||||
|
||||
|
||||
// MARK: - DiffableDataSource
|
||||
|
||||
extension DiffableDataSource {
|
||||
|
||||
// MARK: - TableViewAdapter
|
||||
|
||||
/**
|
||||
The `DiffableDataSource.TableViewAdapterAdapter` serves as a `UITableViewDataSource` that handles `ListPublisher` snapshots for a `UITableView`. Subclasses of `DiffableDataSource.TableViewAdapter` may override some `UITableViewDataSource` methods as needed.
|
||||
The `DiffableDataSource.TableViewAdapterAdapter` instance needs to be held on (retained) for as long as the `UITableView`'s lifecycle.
|
||||
```
|
||||
self.dataSource = DiffableDataSource.TableViewAdapter<Person>(
|
||||
tableView: self.tableView,
|
||||
dataStack: CoreStoreDefaults.dataStack,
|
||||
cellProvider: { (tableView, indexPath, person) in
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "PersonCell") as! PersonCell
|
||||
cell.setPerson(person)
|
||||
return cell
|
||||
}
|
||||
)
|
||||
```
|
||||
The dataSource can then apply changes from a `ListPublisher` as shown:
|
||||
```
|
||||
listPublisher.addObserver(self) { [weak self] (listPublisher) in
|
||||
self?.dataSource?.apply(
|
||||
listPublisher.snapshot,
|
||||
animatingDifferences: true
|
||||
)
|
||||
}
|
||||
```
|
||||
`DiffableDataSource.TableViewAdapter` fully handles the reload animations.
|
||||
- SeeAlso: CoreStore's DiffableDataSource implementation is based on https://github.com/ra1028/DiffableDataSources
|
||||
*/
|
||||
open class TableViewAdapter<O: DynamicObject>: BaseAdapter<O, DefaultTableViewTarget<UITableView>>, UITableViewDataSource {
|
||||
|
||||
// MARK: Publi
|
||||
|
||||
/**
|
||||
Initializes the `DiffableDataSource.TableViewAdapter`. This instance needs to be held on (retained) for as long as the `UITableView`'s lifecycle.
|
||||
```
|
||||
self.dataSource = DiffableDataSource.TableViewAdapter<Person>(
|
||||
tableView: self.tableView,
|
||||
dataStack: CoreStoreDefaults.dataStack,
|
||||
cellProvider: { (tableView, indexPath, person) in
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "PersonCell") as! PersonCell
|
||||
cell.setPerson(person)
|
||||
return cell
|
||||
}
|
||||
)
|
||||
```
|
||||
- parameter tableView: the `UITableView` to set the `dataSource` of. This instance is not retained by the `DiffableDataSource.TableViewAdapter`.
|
||||
- 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
|
||||
*/
|
||||
public init(tableView: UITableView, dataStack: DataStack, cellProvider: @escaping (UITableView, IndexPath, O) -> UITableViewCell?) {
|
||||
|
||||
self.cellProvider = cellProvider
|
||||
super.init(target: .init(tableView), dataStack: dataStack)
|
||||
|
||||
tableView.dataSource = self
|
||||
}
|
||||
|
||||
/**
|
||||
The target `UITableView`
|
||||
*/
|
||||
public var tableView: UITableView? {
|
||||
|
||||
return self.target.base
|
||||
}
|
||||
|
||||
|
||||
// MARK: - UITableViewDataSource
|
||||
|
||||
@objc
|
||||
public dynamic func numberOfSections(in tableView: UITableView) -> Int {
|
||||
|
||||
return self.numberOfSections()
|
||||
}
|
||||
|
||||
@objc
|
||||
public dynamic func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
|
||||
return self.numberOfItems(inSection: section) ?? 0
|
||||
}
|
||||
|
||||
@objc
|
||||
open dynamic func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||
|
||||
return self.sectionID(for: section)
|
||||
}
|
||||
|
||||
@objc
|
||||
open dynamic func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@objc
|
||||
open dynamic func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
|
||||
guard let objectID = self.itemID(for: indexPath) else {
|
||||
|
||||
Internals.abort("Object at \(Internals.typeName(IndexPath.self)) \(indexPath) already removed from list")
|
||||
}
|
||||
guard let object = self.dataStack.fetchExisting(objectID) as O? else {
|
||||
|
||||
Internals.abort("Object at \(Internals.typeName(IndexPath.self)) \(indexPath) has been deleted")
|
||||
}
|
||||
guard let cell = self.cellProvider(tableView, indexPath, object) else {
|
||||
|
||||
Internals.abort("\(Internals.typeName(UITableViewDataSource.self)) returned a `nil` cell for \(Internals.typeName(IndexPath.self)) \(indexPath)")
|
||||
}
|
||||
return cell
|
||||
}
|
||||
|
||||
@objc
|
||||
open dynamic func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@objc
|
||||
open dynamic func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle {
|
||||
|
||||
return .delete
|
||||
}
|
||||
|
||||
@objc
|
||||
open dynamic func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
@nonobjc
|
||||
private let cellProvider: (UITableView, IndexPath, O) -> UITableViewCell?
|
||||
}
|
||||
|
||||
|
||||
// MARK: - DefaultTableViewTarget
|
||||
|
||||
public struct DefaultTableViewTarget<T: UITableView>: Target {
|
||||
|
||||
// MARK: Public
|
||||
|
||||
public typealias Base = T
|
||||
|
||||
public private(set) weak var base: Base?
|
||||
|
||||
public init(_ base: Base) {
|
||||
|
||||
self.base = base
|
||||
}
|
||||
|
||||
|
||||
// MARK: DiffableDataSource.Target
|
||||
|
||||
public var shouldSuspendBatchUpdates: Bool {
|
||||
|
||||
return self.base?.window == nil
|
||||
}
|
||||
|
||||
public func deleteSections(at indices: IndexSet, animated: Bool) {
|
||||
|
||||
self.base?.deleteSections(indices, with: .automatic)
|
||||
}
|
||||
|
||||
public func insertSections(at indices: IndexSet, animated: Bool) {
|
||||
|
||||
self.base?.insertSections(indices, with: .automatic)
|
||||
}
|
||||
|
||||
public func reloadSections(at indices: IndexSet, animated: Bool) {
|
||||
|
||||
self.base?.reloadSections(indices, with: .automatic)
|
||||
}
|
||||
|
||||
public func moveSection(at index: IndexSet.Element, to newIndex: IndexSet.Element, animated: Bool) {
|
||||
|
||||
self.base?.moveSection(index, toSection: newIndex)
|
||||
}
|
||||
|
||||
public func deleteItems(at indexPaths: [IndexPath], animated: Bool) {
|
||||
|
||||
self.base?.deleteRows(at: indexPaths, with: .automatic)
|
||||
}
|
||||
|
||||
public func insertItems(at indexPaths: [IndexPath], animated: Bool) {
|
||||
|
||||
self.base?.insertRows(at: indexPaths, with: .automatic)
|
||||
}
|
||||
|
||||
public func reloadItems(at indexPaths: [IndexPath], animated: Bool) {
|
||||
|
||||
self.base?.reloadRows(at: indexPaths, with: .automatic)
|
||||
}
|
||||
|
||||
public func moveItem(at indexPath: IndexPath, to newIndexPath: IndexPath, animated: Bool) {
|
||||
|
||||
self.base?.moveRow(at: indexPath, to: newIndexPath)
|
||||
}
|
||||
|
||||
public func performBatchUpdates(updates: () -> Void, animated: Bool) {
|
||||
|
||||
guard let base = self.base else {
|
||||
|
||||
return
|
||||
}
|
||||
if #available(iOS 11.0, tvOS 11.0, *) {
|
||||
|
||||
base.performBatchUpdates(updates)
|
||||
}
|
||||
else {
|
||||
|
||||
base.beginUpdates()
|
||||
updates()
|
||||
base.endUpdates()
|
||||
}
|
||||
}
|
||||
|
||||
public func reloadData() {
|
||||
|
||||
self.base?.reloadData()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Deprecated
|
||||
|
||||
extension DiffableDataSource {
|
||||
|
||||
@available(*, deprecated, renamed: "TableViewAdapter")
|
||||
public typealias TableView = TableViewAdapter
|
||||
}
|
||||
|
||||
#endif
|
||||
211
Sources/DiffableDataSource.Target.swift
Normal file
211
Sources/DiffableDataSource.Target.swift
Normal file
@@ -0,0 +1,211 @@
|
||||
//
|
||||
// DiffableDataSource.Target.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2018 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
// MARK: - DiffableDataSourc
|
||||
|
||||
extension DiffableDataSource {
|
||||
|
||||
// MARK: - Target
|
||||
|
||||
/**
|
||||
The `DiffableDataSource.Target` protocol allows custom views to consume `ListSnapshot` diffable data similar to how `DiffableDataSource.TableViewAdapter` and `DiffableDataSource.CollectionViewAdapter` reloads data for their corresponding views.
|
||||
*/
|
||||
public typealias Target = DiffableDataSourceTarget
|
||||
}
|
||||
|
||||
|
||||
// MARK: - DiffableDataSource.Target
|
||||
|
||||
/**
|
||||
The `DiffableDataSource.Target` protocol allows custom views to consume `ListSnapshot` diffable data similar to how `DiffableDataSource.TableViewAdapter` and `DiffableDataSource.CollectionViewAdapter` reloads data for their corresponding views.
|
||||
*/
|
||||
public protocol DiffableDataSourceTarget {
|
||||
|
||||
// MARK: Public
|
||||
|
||||
/**
|
||||
Whether `reloadData()` should be executed instead of `performBatchUpdates(updates:animated:)`.
|
||||
*/
|
||||
var shouldSuspendBatchUpdates: Bool { get }
|
||||
|
||||
/**
|
||||
Deletes one or more sections.
|
||||
*/
|
||||
func deleteSections(at indices: IndexSet, animated: Bool)
|
||||
|
||||
/**
|
||||
Inserts one or more sections
|
||||
*/
|
||||
func insertSections(at indices: IndexSet, animated: Bool)
|
||||
|
||||
/**
|
||||
Reloads the specified sections.
|
||||
*/
|
||||
func reloadSections(at indices: IndexSet, animated: Bool)
|
||||
|
||||
/**
|
||||
Moves a section to a new location.
|
||||
*/
|
||||
func moveSection(at index: IndexSet.Element, to newIndex: IndexSet.Element, animated: Bool)
|
||||
|
||||
/**
|
||||
Deletes the items specified by an array of index paths.
|
||||
*/
|
||||
func deleteItems(at indexPaths: [IndexPath], animated: Bool)
|
||||
|
||||
/**
|
||||
Inserts items at the locations identified by an array of index paths.
|
||||
*/
|
||||
func insertItems(at indexPaths: [IndexPath], animated: Bool)
|
||||
|
||||
/**
|
||||
Reloads the specified items.
|
||||
*/
|
||||
func reloadItems(at indexPaths: [IndexPath], animated: Bool)
|
||||
|
||||
/**
|
||||
Moves the item at a specified location to a destination location.
|
||||
*/
|
||||
func moveItem(at indexPath: IndexPath, to newIndexPath: IndexPath, animated: Bool)
|
||||
|
||||
/**
|
||||
Animates multiple insert, delete, reload, and move operations as a group.
|
||||
*/
|
||||
func performBatchUpdates(updates: () -> Void, animated: Bool)
|
||||
|
||||
/**
|
||||
Reloads all sections and items.
|
||||
*/
|
||||
func reloadData()
|
||||
}
|
||||
|
||||
extension DiffableDataSource.Target {
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
internal func reload<C, O>(
|
||||
using stagedChangeset: Internals.DiffableDataUIDispatcher<O>.StagedChangeset<C>,
|
||||
animated: Bool,
|
||||
interrupt: ((Internals.DiffableDataUIDispatcher<O>.Changeset<C>) -> Bool)? = nil,
|
||||
setData: (C) -> Void
|
||||
) {
|
||||
|
||||
if self.shouldSuspendBatchUpdates, let data = stagedChangeset.last?.data {
|
||||
|
||||
setData(data)
|
||||
self.reloadData()
|
||||
return
|
||||
}
|
||||
for changeset in stagedChangeset {
|
||||
|
||||
if let interrupt = interrupt,
|
||||
interrupt(changeset),
|
||||
let data = stagedChangeset.last?.data {
|
||||
|
||||
setData(data)
|
||||
self.reloadData()
|
||||
return
|
||||
}
|
||||
self.performBatchUpdates(
|
||||
updates: {
|
||||
|
||||
setData(changeset.data)
|
||||
|
||||
if !changeset.sectionDeleted.isEmpty {
|
||||
|
||||
self.deleteSections(
|
||||
at: IndexSet(changeset.sectionDeleted),
|
||||
animated: animated
|
||||
)
|
||||
}
|
||||
if !changeset.sectionInserted.isEmpty {
|
||||
|
||||
self.insertSections(
|
||||
at: IndexSet(changeset.sectionInserted),
|
||||
animated: animated
|
||||
)
|
||||
}
|
||||
if !changeset.sectionUpdated.isEmpty {
|
||||
|
||||
self.reloadSections(
|
||||
at: IndexSet(changeset.sectionUpdated),
|
||||
animated: animated
|
||||
)
|
||||
}
|
||||
for (source, target) in changeset.sectionMoved {
|
||||
|
||||
self.moveSection(
|
||||
at: source,
|
||||
to: target,
|
||||
animated: animated
|
||||
)
|
||||
}
|
||||
if !changeset.elementDeleted.isEmpty {
|
||||
|
||||
self.deleteItems(
|
||||
at: changeset.elementDeleted.map {
|
||||
|
||||
IndexPath(item: $0.element, section: $0.section)
|
||||
},
|
||||
animated: animated
|
||||
)
|
||||
}
|
||||
if !changeset.elementInserted.isEmpty {
|
||||
|
||||
self.insertItems(
|
||||
at: changeset.elementInserted.map {
|
||||
|
||||
IndexPath(item: $0.element, section: $0.section)
|
||||
},
|
||||
animated: animated
|
||||
)
|
||||
}
|
||||
if !changeset.elementUpdated.isEmpty {
|
||||
|
||||
self.reloadItems(
|
||||
at: changeset.elementUpdated.map {
|
||||
|
||||
IndexPath(item: $0.element, section: $0.section)
|
||||
},
|
||||
animated: animated
|
||||
)
|
||||
}
|
||||
for (source, target) in changeset.elementMoved {
|
||||
|
||||
self.moveItem(
|
||||
at: IndexPath(item: source.element, section: source.section),
|
||||
to: IndexPath(item: target.element, section: target.section),
|
||||
animated: animated
|
||||
)
|
||||
}
|
||||
},
|
||||
animated: animated
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@
|
||||
// MARK: - DiffableDataSource
|
||||
|
||||
/**
|
||||
Namespace for diffable data source types. See `DiffableDataSource.TableView` and `DiffableDataSource.CollectionView` for actual implementations
|
||||
Namespace for diffable data source types. See `DiffableDataSource.TableViewAdapter` and `DiffableDataSource.CollectionViewAdapter` for actual implementations
|
||||
*/
|
||||
public enum DiffableDataSource {}
|
||||
|
||||
|
||||
@@ -47,9 +47,13 @@ extension Internals {
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
init(sections: [NSFetchedResultsSectionInfo]) {
|
||||
init(sections: [NSFetchedResultsSectionInfo], fetchOffset: Int, fetchLimit: Int) {
|
||||
|
||||
self.structure = .init(sections: sections)
|
||||
self.structure = .init(
|
||||
sections: sections,
|
||||
fetchOffset: Swift.max(0, fetchOffset),
|
||||
fetchLimit: (fetchLimit > 0) ? fetchLimit : nil
|
||||
)
|
||||
}
|
||||
|
||||
var sections: [Section] {
|
||||
@@ -92,6 +96,11 @@ extension Internals {
|
||||
return self.structure.allItemIDs
|
||||
}
|
||||
|
||||
var updatedItemIdentifiers: Set<NSManagedObjectID> {
|
||||
|
||||
return self.structure.reloadedItems
|
||||
}
|
||||
|
||||
func numberOfItems(inSection identifier: String) -> Int {
|
||||
|
||||
return self.itemIdentifiers(inSection: identifier).count
|
||||
@@ -117,22 +126,22 @@ extension Internals {
|
||||
return self.structure.allSectionIDs.firstIndex(of: identifier)
|
||||
}
|
||||
|
||||
mutating func appendItems(_ identifiers: [NSManagedObjectID], toSection sectionIdentifier: String?) {
|
||||
mutating func appendItems<C: Collection>(_ identifiers: C, toSection sectionIdentifier: String?) where C.Element == NSManagedObjectID {
|
||||
|
||||
self.structure.append(itemIDs: identifiers, to: sectionIdentifier)
|
||||
}
|
||||
|
||||
mutating func insertItems(_ identifiers: [NSManagedObjectID], beforeItem beforeIdentifier: NSManagedObjectID) {
|
||||
mutating func insertItems<C: Collection>(_ identifiers: C, beforeItem beforeIdentifier: NSManagedObjectID) where C.Element == NSManagedObjectID {
|
||||
|
||||
self.structure.insert(itemIDs: identifiers, before: beforeIdentifier)
|
||||
}
|
||||
|
||||
mutating func insertItems(_ identifiers: [NSManagedObjectID], afterItem afterIdentifier: NSManagedObjectID) {
|
||||
mutating func insertItems<C: Collection>(_ identifiers: C, afterItem afterIdentifier: NSManagedObjectID) where C.Element == NSManagedObjectID {
|
||||
|
||||
self.structure.insert(itemIDs: identifiers, after: afterIdentifier)
|
||||
}
|
||||
|
||||
mutating func deleteItems(_ identifiers: [NSManagedObjectID]) {
|
||||
mutating func deleteItems<S: Sequence>(_ identifiers: S) where S.Element == NSManagedObjectID {
|
||||
|
||||
self.structure.remove(itemIDs: identifiers)
|
||||
}
|
||||
@@ -152,27 +161,27 @@ extension Internals {
|
||||
self.structure.move(itemID: identifier, after: toIdentifier)
|
||||
}
|
||||
|
||||
mutating func reloadItems(_ identifiers: [NSManagedObjectID]) {
|
||||
mutating func reloadItems<S: Sequence>(_ identifiers: S) where S.Element == NSManagedObjectID {
|
||||
|
||||
self.structure.update(itemIDs: identifiers)
|
||||
}
|
||||
|
||||
mutating func appendSections(_ identifiers: [String]) {
|
||||
mutating func appendSections<C: Collection>(_ identifiers: C) where C.Element == String {
|
||||
|
||||
self.structure.append(sectionIDs: identifiers)
|
||||
}
|
||||
|
||||
mutating func insertSections(_ identifiers: [String], beforeSection toIdentifier: String) {
|
||||
mutating func insertSections<C: Collection>(_ identifiers: C, beforeSection toIdentifier: String) where C.Element == String {
|
||||
|
||||
self.structure.insert(sectionIDs: identifiers, before: toIdentifier)
|
||||
}
|
||||
|
||||
mutating func insertSections(_ identifiers: [String], afterSection toIdentifier: String) {
|
||||
mutating func insertSections<C: Collection>(_ identifiers: C, afterSection toIdentifier: String) where C.Element == String {
|
||||
|
||||
self.structure.insert(sectionIDs: identifiers, after: toIdentifier)
|
||||
}
|
||||
|
||||
mutating func deleteSections(_ identifiers: [String]) {
|
||||
mutating func deleteSections<S: Sequence>(_ identifiers: S) where S.Element == String {
|
||||
|
||||
self.structure.remove(sectionIDs: identifiers)
|
||||
}
|
||||
@@ -187,7 +196,7 @@ extension Internals {
|
||||
self.structure.move(sectionID: identifier, after: toIdentifier)
|
||||
}
|
||||
|
||||
mutating func reloadSections(_ identifiers: [String]) {
|
||||
mutating func reloadSections<S: Sequence>(_ identifiers: S) where S.Element == String {
|
||||
|
||||
self.structure.update(sectionIDs: identifiers)
|
||||
}
|
||||
@@ -268,23 +277,54 @@ extension Internals {
|
||||
// MARK: Internal
|
||||
|
||||
var sections: [Section]
|
||||
private(set) var reloadedItems: Set<NSManagedObjectID>
|
||||
|
||||
init() {
|
||||
|
||||
self.sections = []
|
||||
self.reloadedItems = []
|
||||
}
|
||||
|
||||
init(sections: [NSFetchedResultsSectionInfo]) {
|
||||
init(sections: [NSFetchedResultsSectionInfo], fetchOffset: Int, fetchLimit: Int?) {
|
||||
|
||||
self.sections = sections.map {
|
||||
let sliceItems: (_ array: [Any], _ offset: Int) -> Array<Any>.SubSequence
|
||||
if let fetchLimit = fetchLimit {
|
||||
|
||||
Section(
|
||||
differenceIdentifier: $0.name,
|
||||
items: $0.objects?
|
||||
.compactMap({ ($0 as? NSManagedObject)?.objectID })
|
||||
.map({ Item(differenceIdentifier: $0) }) ?? []
|
||||
var remainingCount = fetchLimit
|
||||
sliceItems = {
|
||||
|
||||
let slice = $0[$1...].prefix(remainingCount)
|
||||
remainingCount -= slice.count
|
||||
return slice
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
sliceItems = { $0[$1...] }
|
||||
}
|
||||
var newSections: [Internals.DiffableDataSourceSnapshot.Section] = []
|
||||
var ignoreCount = fetchOffset
|
||||
for section in sections {
|
||||
|
||||
let objects = section.objects ?? []
|
||||
guard objects.indices.contains(ignoreCount) else {
|
||||
|
||||
ignoreCount -= objects.count
|
||||
continue
|
||||
}
|
||||
let items = sliceItems(objects, ignoreCount)
|
||||
.map({ Item(differenceIdentifier: ($0 as! NSManagedObject).objectID) })
|
||||
ignoreCount = 0
|
||||
guard !items.isEmpty else {
|
||||
|
||||
continue
|
||||
}
|
||||
newSections.append(
|
||||
Section(differenceIdentifier: section.name, items: items)
|
||||
)
|
||||
}
|
||||
self.sections = newSections
|
||||
self.reloadedItems = []
|
||||
}
|
||||
|
||||
var allSectionIDs: [String] {
|
||||
@@ -308,10 +348,10 @@ extension Internals {
|
||||
|
||||
func section(containing itemID: NSManagedObjectID) -> String? {
|
||||
|
||||
return self.itemPositionMap()[itemID]?.section.differenceIdentifier
|
||||
return self.itemPositionMap(itemID)?.section.differenceIdentifier
|
||||
}
|
||||
|
||||
mutating func append(itemIDs: [NSManagedObjectID], to sectionID: String?) {
|
||||
mutating func append<C: Collection>(itemIDs: C, to sectionID: String?) where C.Element == NSManagedObjectID {
|
||||
|
||||
let index: Array<Section>.Index
|
||||
if let sectionID = sectionID {
|
||||
@@ -335,9 +375,9 @@ extension Internals {
|
||||
self.sections[index].elements.append(contentsOf: items)
|
||||
}
|
||||
|
||||
mutating func insert(itemIDs: [NSManagedObjectID], before beforeItemID: NSManagedObjectID) {
|
||||
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 {
|
||||
|
||||
Internals.abort("Item \(beforeItemID) does not exist")
|
||||
}
|
||||
@@ -346,9 +386,9 @@ extension Internals {
|
||||
.insert(contentsOf: items, at: itemPosition.itemRelativeIndex)
|
||||
}
|
||||
|
||||
mutating func insert(itemIDs: [NSManagedObjectID], after afterItemID: 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 {
|
||||
|
||||
Internals.abort("Item \(afterItemID) does not exist")
|
||||
}
|
||||
@@ -359,7 +399,7 @@ extension Internals {
|
||||
.insert(contentsOf: items, at: itemIndex)
|
||||
}
|
||||
|
||||
mutating func remove(itemIDs: [NSManagedObjectID]) {
|
||||
mutating func remove<S: Sequence>(itemIDs: S) where S.Element == NSManagedObjectID {
|
||||
|
||||
let itemPositionMap = self.itemPositionMap()
|
||||
var removeIndexSetMap: [Int: IndexSet] = [:]
|
||||
@@ -390,13 +430,18 @@ extension Internals {
|
||||
}
|
||||
}
|
||||
|
||||
mutating func removeAllEmptySections() {
|
||||
|
||||
self.sections.removeAll(where: { $0.elements.isEmpty })
|
||||
}
|
||||
|
||||
mutating func move(itemID: NSManagedObjectID, before beforeItemID: NSManagedObjectID) {
|
||||
|
||||
guard let removed = self.remove(itemID: itemID) else {
|
||||
|
||||
Internals.abort("Item \(itemID) does not exist")
|
||||
}
|
||||
guard let itemPosition = self.itemPositionMap()[beforeItemID] else {
|
||||
guard let itemPosition = self.itemPositionMap(beforeItemID) else {
|
||||
|
||||
Internals.abort("Item \(beforeItemID) does not exist")
|
||||
}
|
||||
@@ -410,7 +455,7 @@ extension Internals {
|
||||
|
||||
Internals.abort("Item \(itemID) does not exist")
|
||||
}
|
||||
guard let itemPosition = self.itemPositionMap()[afterItemID] else {
|
||||
guard let itemPosition = self.itemPositionMap(afterItemID) else {
|
||||
|
||||
Internals.abort("Item \(afterItemID) does not exist")
|
||||
}
|
||||
@@ -423,6 +468,7 @@ extension Internals {
|
||||
mutating func update<S: Sequence>(itemIDs: S) where S.Element == NSManagedObjectID {
|
||||
|
||||
let itemPositionMap = self.itemPositionMap()
|
||||
var newItemIDs: Set<NSManagedObjectID> = []
|
||||
for itemID in itemIDs {
|
||||
|
||||
guard let itemPosition = itemPositionMap[itemID] else {
|
||||
@@ -431,16 +477,18 @@ extension Internals {
|
||||
}
|
||||
self.sections[itemPosition.sectionIndex]
|
||||
.elements[itemPosition.itemRelativeIndex].isReloaded = true
|
||||
newItemIDs.insert(itemID)
|
||||
}
|
||||
self.reloadedItems.formUnion(newItemIDs)
|
||||
}
|
||||
|
||||
mutating func append(sectionIDs: [String]) {
|
||||
mutating func append<C: Collection>(sectionIDs: C) where C.Element == String {
|
||||
|
||||
let newSections = sectionIDs.lazy.map({ Section(differenceIdentifier: $0) })
|
||||
self.sections.append(contentsOf: newSections)
|
||||
}
|
||||
|
||||
mutating func insert(sectionIDs: [String], before beforeSectionID: String) {
|
||||
mutating func insert<C: Collection>(sectionIDs: C, before beforeSectionID: String) where C.Element == String {
|
||||
|
||||
guard let sectionIndex = self.sectionIndex(of: beforeSectionID) else {
|
||||
|
||||
@@ -450,7 +498,7 @@ extension Internals {
|
||||
self.sections.insert(contentsOf: newSections, at: sectionIndex)
|
||||
}
|
||||
|
||||
mutating func insert(sectionIDs: [String], after afterSectionID: String) {
|
||||
mutating func insert<C: Collection>(sectionIDs: C, after afterSectionID: String) where C.Element == String {
|
||||
|
||||
guard let beforeIndex = self.sectionIndex(of: afterSectionID) else {
|
||||
|
||||
@@ -461,7 +509,7 @@ extension Internals {
|
||||
self.sections.insert(contentsOf: newSections, at: sectionIndex)
|
||||
}
|
||||
|
||||
mutating func remove(sectionIDs: [String]) {
|
||||
mutating func remove<S: Sequence>(sectionIDs: S) where S.Element == String {
|
||||
|
||||
for sectionID in sectionIDs {
|
||||
|
||||
@@ -519,7 +567,7 @@ extension Internals {
|
||||
@discardableResult
|
||||
private mutating func remove(itemID: NSManagedObjectID) -> Item? {
|
||||
|
||||
guard let itemPosition = self.itemPositionMap()[itemID] else {
|
||||
guard let itemPosition = self.itemPositionMap(itemID) else {
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -537,6 +585,28 @@ extension Internals {
|
||||
return self.sections.remove(at: sectionIndex)
|
||||
}
|
||||
|
||||
private func itemPositionMap(_ itemID: NSManagedObjectID) -> ItemPosition? {
|
||||
|
||||
let sections = self.sections
|
||||
for (sectionIndex, section) in sections.enumerated() {
|
||||
|
||||
for (itemRelativeIndex, item) in section.elements.enumerated() {
|
||||
|
||||
guard item.differenceIdentifier == itemID else {
|
||||
|
||||
continue
|
||||
}
|
||||
return ItemPosition(
|
||||
item: item,
|
||||
itemRelativeIndex: itemRelativeIndex,
|
||||
section: section,
|
||||
sectionIndex: sectionIndex
|
||||
)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
private func itemPositionMap() -> [NSManagedObjectID: ItemPosition] {
|
||||
|
||||
return self.sections.enumerated().reduce(into: [:]) { result, section in
|
||||
|
||||
@@ -51,8 +51,38 @@ extension Internals {
|
||||
|
||||
self.dataStack = dataStack
|
||||
}
|
||||
|
||||
func purge<Target: DiffableDataSource.Target>(
|
||||
target: Target?,
|
||||
animatingDifferences: Bool,
|
||||
performUpdates: @escaping (
|
||||
Target,
|
||||
StagedChangeset<[Internals.DiffableDataSourceSnapshot.Section]>,
|
||||
@escaping ([Internals.DiffableDataSourceSnapshot.Section]) -> Void
|
||||
) -> Void,
|
||||
completion: @escaping () -> Void
|
||||
) {
|
||||
|
||||
self.apply(
|
||||
.init(),
|
||||
target: target,
|
||||
animatingDifferences: animatingDifferences,
|
||||
performUpdates: performUpdates,
|
||||
completion: completion
|
||||
)
|
||||
}
|
||||
|
||||
func apply<View: AnyObject>(_ snapshot: DiffableDataSourceSnapshot, view: View?, animatingDifferences: Bool, performUpdates: @escaping (View, StagedChangeset<[Internals.DiffableDataSourceSnapshot.Section]>, @escaping ([Internals.DiffableDataSourceSnapshot.Section]) -> Void) -> Void) {
|
||||
func apply<Target: DiffableDataSource.Target>(
|
||||
_ snapshot: DiffableDataSourceSnapshot,
|
||||
target: Target?,
|
||||
animatingDifferences: Bool,
|
||||
performUpdates: @escaping (
|
||||
Target,
|
||||
StagedChangeset<[Internals.DiffableDataSourceSnapshot.Section]>,
|
||||
@escaping ([Internals.DiffableDataSourceSnapshot.Section]) -> Void
|
||||
) -> Void,
|
||||
completion: @escaping () -> Void
|
||||
) {
|
||||
|
||||
self.dispatcher.dispatch { [weak self] in
|
||||
|
||||
@@ -64,36 +94,42 @@ extension Internals {
|
||||
self.currentSnapshot = snapshot
|
||||
|
||||
let newSections = snapshot.sections
|
||||
guard let view = view else {
|
||||
guard let target = target else {
|
||||
|
||||
return self.sections = newSections
|
||||
self.sections = newSections
|
||||
return
|
||||
}
|
||||
|
||||
let performDiffingUpdates: () -> Void = {
|
||||
|
||||
let changeset = StagedChangeset(source: self.sections, target: newSections)
|
||||
performUpdates(view, changeset) { sections in
|
||||
performUpdates(target, changeset) { sections in
|
||||
|
||||
self.sections = sections
|
||||
}
|
||||
}
|
||||
|
||||
#if canImport(QuartzCore)
|
||||
|
||||
|
||||
CATransaction.begin()
|
||||
CATransaction.setCompletionBlock(completion)
|
||||
|
||||
if !animatingDifferences {
|
||||
|
||||
CATransaction.begin()
|
||||
|
||||
CATransaction.setDisableActions(true)
|
||||
|
||||
performDiffingUpdates()
|
||||
|
||||
CATransaction.commit()
|
||||
return
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
performDiffingUpdates()
|
||||
|
||||
CATransaction.commit()
|
||||
|
||||
|
||||
#else
|
||||
|
||||
performDiffingUpdates()
|
||||
completion()
|
||||
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,14 +140,23 @@ extension Internals {
|
||||
return snapshot
|
||||
}
|
||||
|
||||
func sectionIdentifier(inSection section: Int) -> String? {
|
||||
|
||||
guard self.sections.indices.contains(section) else {
|
||||
|
||||
return nil
|
||||
}
|
||||
return self.sections[section].differenceIdentifier
|
||||
}
|
||||
|
||||
func itemIdentifier(for indexPath: IndexPath) -> O.ObjectID? {
|
||||
|
||||
guard (0 ..< self.sections.endIndex) ~= indexPath.section else {
|
||||
|
||||
guard self.sections.indices.contains(indexPath.section) else {
|
||||
|
||||
return nil
|
||||
}
|
||||
let items = self.sections[indexPath.section].elements
|
||||
guard (0 ..< items.endIndex) ~= indexPath.item else {
|
||||
guard items.indices.contains(indexPath.item) else {
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -138,15 +183,14 @@ extension Internals {
|
||||
return self.sections.count
|
||||
}
|
||||
|
||||
func numberOfItems(inSection section: Int) -> Int {
|
||||
|
||||
func numberOfItems(inSection section: Int) -> Int? {
|
||||
|
||||
guard self.sections.indices.contains(section) else {
|
||||
|
||||
return nil
|
||||
}
|
||||
return self.sections[section].elements.count
|
||||
}
|
||||
|
||||
func sectionIdentifier(inSection section: Int) -> String {
|
||||
|
||||
return self.sections[section].differenceIdentifier
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
@@ -41,9 +41,6 @@ import AppKit
|
||||
|
||||
internal protocol FetchedDiffableDataSourceSnapshotHandler: AnyObject {
|
||||
|
||||
// @available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *)
|
||||
// func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChangeContentWith snapshot: NSDiffableDataSourceSnapshot<String, NSManagedObjectID>)
|
||||
|
||||
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChangeContentWith snapshot: Internals.DiffableDataSourceSnapshot)
|
||||
|
||||
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, sectionIndexTitleForSectionName sectionName: String?) -> String?
|
||||
@@ -80,15 +77,6 @@ extension Internals {
|
||||
|
||||
internal func initialFetch() {
|
||||
|
||||
// #if canImport(UIKit) || canImport(AppKit)
|
||||
//
|
||||
// if #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) {
|
||||
//
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// #endif
|
||||
|
||||
guard let fetchedResultsController = self.fetchedResultsController else {
|
||||
|
||||
return
|
||||
@@ -98,26 +86,14 @@ extension Internals {
|
||||
|
||||
|
||||
// MARK: NSFetchedResultsControllerDelegate
|
||||
|
||||
// #if canImport(UIKit) || canImport(AppKit)
|
||||
//
|
||||
// @available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *)
|
||||
// @objc
|
||||
// dynamic func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChangeContentWith snapshot: NSDiffableDataSourceSnapshotReference) {
|
||||
//
|
||||
// self.handler?.controller(
|
||||
// controller,
|
||||
// didChangeContentWith: snapshot as NSDiffableDataSourceSnapshot<String, NSManagedObjectID>
|
||||
// )
|
||||
// }
|
||||
//
|
||||
// #endif
|
||||
|
||||
@objc
|
||||
dynamic func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
|
||||
|
||||
var snapshot = Internals.DiffableDataSourceSnapshot(
|
||||
sections: controller.sections ?? []
|
||||
sections: controller.sections ?? [],
|
||||
fetchOffset: controller.fetchRequest.fetchOffset,
|
||||
fetchLimit: controller.fetchRequest.fetchLimit
|
||||
)
|
||||
snapshot.reloadSections(self.reloadedSectionIDs)
|
||||
snapshot.reloadItems(self.reloadedItemIDs)
|
||||
|
||||
@@ -62,7 +62,7 @@ import SwiftUI
|
||||
.orderBy(.ascending(\.lastName))
|
||||
)
|
||||
```
|
||||
All access to the `ListPublisher` items should be done via its `snapshot` value, which is a `struct` of type `ListSnapshot<O>`. `ListSnapshot`s are also designed to work well with `DiffableDataSource.TableView`s and `DiffableDataSource.CollectionView`s. For detailed examples, refer to the documentation for `DiffableDataSource.TableView` and `DiffableDataSource.CollectionView`.
|
||||
All access to the `ListPublisher` items should be done via its `snapshot` value, which is a `struct` of type `ListSnapshot<O>`. `ListSnapshot`s are also designed to work well with `DiffableDataSource.TableViewAdapter`s and `DiffableDataSource.CollectionViewAdapter`s. For detailed examples, refer to the documentation for `DiffableDataSource.TableViewAdapter` and `DiffableDataSource.CollectionViewAdapter`.
|
||||
*/
|
||||
@available(macOS 10.12, *)
|
||||
public final class ListPublisher<O: DynamicObject>: Hashable {
|
||||
@@ -397,15 +397,6 @@ public final class ListPublisher<O: DynamicObject>: Hashable {
|
||||
extension ListPublisher: FetchedDiffableDataSourceSnapshotHandler {
|
||||
|
||||
// MARK: FetchedDiffableDataSourceSnapshotHandler
|
||||
|
||||
// @available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *)
|
||||
// internal func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChangeContentWith snapshot: NSDiffableDataSourceSnapshot<String, NSManagedObjectID>) {
|
||||
//
|
||||
// self.snapshot = .init(
|
||||
// diffableSnapshot: snapshot,
|
||||
// context: controller.managedObjectContext
|
||||
// )
|
||||
// }
|
||||
|
||||
internal func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChangeContentWith snapshot: Internals.DiffableDataSourceSnapshot) {
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ import AppKit
|
||||
// MARK: - ListSnapshot
|
||||
|
||||
/**
|
||||
A `ListSnapshot` holds a stable list of `DynamicObject` identifiers. This is typically created by a `ListPublisher` and are designed to work well with `DiffableDataSource.TableView`s and `DiffableDataSource.CollectionView`s. For detailed examples, see the documentation on `DiffableDataSource.TableView` and `DiffableDataSource.CollectionView`.
|
||||
A `ListSnapshot` holds a stable list of `DynamicObject` identifiers. This is typically created by a `ListPublisher` and are designed to work well with `DiffableDataSource.TableViewAdapter`s and `DiffableDataSource.CollectionViewAdapter`s. For detailed examples, see the documentation on `DiffableDataSource.TableViewAdapter` and `DiffableDataSource.CollectionViewAdapter`.
|
||||
|
||||
While the `ListSnapshot` stores only object identifiers, all accessors to its items return `ObjectPublisher`s, which are lazily created. For more details, see the documentation on `ListObject`.
|
||||
|
||||
@@ -83,7 +83,10 @@ public struct ListSnapshot<O: DynamicObject>: RandomAccessCollection, Hashable {
|
||||
*/
|
||||
public subscript(safeIndex index: Index) -> ObjectPublisher<O>? {
|
||||
|
||||
let context = self.context!
|
||||
guard let context = self.context else {
|
||||
|
||||
return nil
|
||||
}
|
||||
let itemIDs = self.diffableSnapshot.itemIdentifiers
|
||||
guard itemIDs.indices.contains(index) else {
|
||||
|
||||
@@ -118,7 +121,10 @@ public struct ListSnapshot<O: DynamicObject>: RandomAccessCollection, Hashable {
|
||||
*/
|
||||
public subscript(safeSectionIndex sectionIndex: Int, safeItemIndex itemIndex: Int) -> ObjectPublisher<O>? {
|
||||
|
||||
let context = self.context!
|
||||
guard let context = self.context else {
|
||||
|
||||
return nil
|
||||
}
|
||||
let snapshot = self.diffableSnapshot
|
||||
let sectionIDs = snapshot.sectionIdentifiers
|
||||
guard sectionIDs.indices.contains(sectionIndex) else {
|
||||
@@ -214,6 +220,14 @@ public struct ListSnapshot<O: DynamicObject>: RandomAccessCollection, Hashable {
|
||||
return snapshot.numberOfItems(inSection: sectionID) > 0
|
||||
}
|
||||
|
||||
/**
|
||||
Returns item identifiers for updated objects. This is mainly useful for Data Source adapters such as `UICollectionViewDiffableDataSource` or `UITableViewDiffableDataSource` which work on collection diffs when reloading. Since objects with same IDs resolve as "equal" in their old and new states, adapters may need extra heuristics to determine which row items need reloading. If your row items are all observing changes from each corresponding `ObjectPublisher`, or if you are using CoreStore's built-in `DiffableDataSource`s, there is no need to inspect this property.
|
||||
*/
|
||||
public var updatedItemIdentifiers: Set<NSManagedObjectID> {
|
||||
|
||||
return self.diffableSnapshot.updatedItemIdentifiers
|
||||
}
|
||||
|
||||
/**
|
||||
The number of items in all sections in the `ListSnapshot`
|
||||
*/
|
||||
@@ -340,7 +354,7 @@ public struct ListSnapshot<O: DynamicObject>: RandomAccessCollection, Hashable {
|
||||
return indices.map { position in
|
||||
|
||||
let itemID = itemIDs[position]
|
||||
return ObjectPublisher<O>(objectID: itemID, context: context)
|
||||
return context.objectPublisher(objectID: itemID)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -354,10 +368,7 @@ public struct ListSnapshot<O: DynamicObject>: RandomAccessCollection, Hashable {
|
||||
|
||||
let context = self.context!
|
||||
let itemIDs = self.diffableSnapshot.itemIdentifiers(inSection: sectionID)
|
||||
return itemIDs.map {
|
||||
|
||||
return ObjectPublisher<O>(objectID: $0, context: context)
|
||||
}
|
||||
return itemIDs.map(context.objectPublisher(objectID:))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -374,7 +385,7 @@ public struct ListSnapshot<O: DynamicObject>: RandomAccessCollection, Hashable {
|
||||
return itemIndices.map { position in
|
||||
|
||||
let itemID = itemIDs[position]
|
||||
return ObjectPublisher<O>(objectID: itemID, context: context)
|
||||
return context.objectPublisher(objectID: itemID)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -391,7 +402,7 @@ public struct ListSnapshot<O: DynamicObject>: RandomAccessCollection, Hashable {
|
||||
return indices.lazy.map { position in
|
||||
|
||||
let itemID = itemIDs[position]
|
||||
return ObjectPublisher<O>(objectID: itemID, context: context)
|
||||
return context.objectPublisher(objectID: itemID)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -405,10 +416,7 @@ public struct ListSnapshot<O: DynamicObject>: RandomAccessCollection, Hashable {
|
||||
|
||||
let context = self.context!
|
||||
let itemIDs = self.diffableSnapshot.itemIdentifiers(inSection: sectionID)
|
||||
return itemIDs.lazy.map {
|
||||
|
||||
return ObjectPublisher<O>(objectID: $0, context: context)
|
||||
}
|
||||
return itemIDs.lazy.map(context.objectPublisher(objectID:))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -425,7 +433,7 @@ public struct ListSnapshot<O: DynamicObject>: RandomAccessCollection, Hashable {
|
||||
return itemIndices.lazy.map { position in
|
||||
|
||||
let itemID = itemIDs[position]
|
||||
return ObjectPublisher<O>(objectID: itemID, context: context)
|
||||
return context.objectPublisher(objectID: itemID)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -438,7 +446,7 @@ public struct ListSnapshot<O: DynamicObject>: RandomAccessCollection, Hashable {
|
||||
- parameter itemIDs: the object identifiers for the objects to append
|
||||
- parameter sectionID: the section to append the items to
|
||||
*/
|
||||
public mutating func appendItems(withIDs itemIDs: [ItemID], toSectionWithID sectionID: SectionID? = nil) {
|
||||
public mutating func appendItems<C: Collection>(withIDs itemIDs: C, toSectionWithID sectionID: SectionID? = nil) where C.Element == ItemID {
|
||||
|
||||
self.diffableSnapshot.appendItems(itemIDs, toSection: sectionID)
|
||||
}
|
||||
@@ -449,7 +457,7 @@ public struct ListSnapshot<O: DynamicObject>: RandomAccessCollection, Hashable {
|
||||
- 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.
|
||||
*/
|
||||
public mutating func insertItems(withIDs itemIDs: [ItemID], beforeItemID: ItemID) {
|
||||
public mutating func insertItems<C: Collection>(withIDs itemIDs: C, beforeItemID: ItemID) where C.Element == ItemID {
|
||||
|
||||
self.diffableSnapshot.insertItems(itemIDs, beforeItem: beforeItemID)
|
||||
}
|
||||
@@ -460,7 +468,7 @@ public struct ListSnapshot<O: DynamicObject>: RandomAccessCollection, Hashable {
|
||||
- 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.
|
||||
*/
|
||||
public mutating func insertItems(withIDs itemIDs: [ItemID], afterItemID: ItemID) {
|
||||
public mutating func insertItems<C: Collection>(withIDs itemIDs: C, afterItemID: ItemID) where C.Element == ItemID {
|
||||
|
||||
self.diffableSnapshot.insertItems(itemIDs, afterItem: afterItemID)
|
||||
}
|
||||
@@ -470,7 +478,7 @@ public struct ListSnapshot<O: DynamicObject>: RandomAccessCollection, Hashable {
|
||||
|
||||
- parameter itemIDs: the object identifiers for the objects to delete
|
||||
*/
|
||||
public mutating func deleteItems(withIDs itemIDs: [ItemID]) {
|
||||
public mutating func deleteItems<S: Sequence>(withIDs itemIDs: S) where S.Element == ItemID {
|
||||
|
||||
self.diffableSnapshot.deleteItems(itemIDs)
|
||||
}
|
||||
@@ -510,7 +518,7 @@ public struct ListSnapshot<O: DynamicObject>: RandomAccessCollection, Hashable {
|
||||
|
||||
- parameter itemIDs: the object identifiers to reload
|
||||
*/
|
||||
public mutating func reloadItems(withIDs itemIDs: [ItemID]) {
|
||||
public mutating func reloadItems<S: Sequence>(withIDs itemIDs: S) where S.Element == ItemID {
|
||||
|
||||
self.diffableSnapshot.reloadItems(itemIDs)
|
||||
}
|
||||
@@ -520,7 +528,7 @@ public struct ListSnapshot<O: DynamicObject>: RandomAccessCollection, Hashable {
|
||||
|
||||
- parameter sectionIDs: the sections to append
|
||||
*/
|
||||
public mutating func appendSections(withIDs sectionIDs: [SectionID]) {
|
||||
public mutating func appendSections<C: Collection>(withIDs sectionIDs: C) where C.Element == SectionID {
|
||||
|
||||
self.diffableSnapshot.appendSections(sectionIDs)
|
||||
}
|
||||
@@ -531,7 +539,7 @@ public struct ListSnapshot<O: DynamicObject>: RandomAccessCollection, Hashable {
|
||||
- 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.
|
||||
*/
|
||||
public mutating func insertSections(withIDs sectionIDs: [SectionID], beforeSectionID: SectionID) {
|
||||
public mutating func insertSections<C: Collection>(withIDs sectionIDs: C, beforeSectionID: SectionID) where C.Element == SectionID {
|
||||
|
||||
self.diffableSnapshot.insertSections(sectionIDs, beforeSection: beforeSectionID)
|
||||
}
|
||||
@@ -542,7 +550,7 @@ public struct ListSnapshot<O: DynamicObject>: RandomAccessCollection, Hashable {
|
||||
- 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.
|
||||
*/
|
||||
public mutating func insertSections(withIDs sectionIDs: [SectionID], afterSectionID: SectionID) {
|
||||
public mutating func insertSections<C: Collection>(withIDs sectionIDs: C, afterSectionID: SectionID) where C.Element == SectionID {
|
||||
|
||||
self.diffableSnapshot.insertSections(sectionIDs, afterSection: afterSectionID)
|
||||
}
|
||||
@@ -552,7 +560,7 @@ public struct ListSnapshot<O: DynamicObject>: RandomAccessCollection, Hashable {
|
||||
|
||||
- parameter sectionIDs: the section identifiers for the sections to delete
|
||||
*/
|
||||
public mutating func deleteSections(withIDs sectionIDs: [SectionID]) {
|
||||
public mutating func deleteSections<S: Sequence>(withIDs sectionIDs: S) where S.Element == SectionID {
|
||||
|
||||
self.diffableSnapshot.deleteSections(sectionIDs)
|
||||
}
|
||||
@@ -584,7 +592,7 @@ public struct ListSnapshot<O: DynamicObject>: RandomAccessCollection, Hashable {
|
||||
|
||||
- parameter sectionIDs: the section identifiers to reload
|
||||
*/
|
||||
public mutating func reloadSections(withIDs sectionIDs: [SectionID]) {
|
||||
public mutating func reloadSections<S: Sequence>(withIDs sectionIDs: S) where S.Element == SectionID {
|
||||
|
||||
self.diffableSnapshot.reloadSections(sectionIDs)
|
||||
}
|
||||
@@ -629,28 +637,14 @@ public struct ListSnapshot<O: DynamicObject>: RandomAccessCollection, Hashable {
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
internal private(set) var diffableSnapshot: DiffableDataSourceSnapshotProtocol
|
||||
internal private(set) var diffableSnapshot: Internals.DiffableDataSourceSnapshot
|
||||
|
||||
internal init() {
|
||||
|
||||
// if #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) {
|
||||
//
|
||||
// self.diffableSnapshot = NSDiffableDataSourceSnapshot<String, NSManagedObjectID>()
|
||||
// }
|
||||
// else {
|
||||
|
||||
self.diffableSnapshot = Internals.DiffableDataSourceSnapshot()
|
||||
// }
|
||||
self.diffableSnapshot = Internals.DiffableDataSourceSnapshot()
|
||||
self.context = nil
|
||||
}
|
||||
|
||||
// @available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *)
|
||||
// internal init(diffableSnapshot: NSDiffableDataSourceSnapshot<String, NSManagedObjectID>, context: NSManagedObjectContext) {
|
||||
//
|
||||
// self.diffableSnapshot = diffableSnapshot
|
||||
// self.context = context
|
||||
// }
|
||||
|
||||
internal init(diffableSnapshot: Internals.DiffableDataSourceSnapshot, context: NSManagedObjectContext) {
|
||||
|
||||
self.diffableSnapshot = diffableSnapshot
|
||||
|
||||
@@ -99,7 +99,7 @@ extension NSManagedObjectContext {
|
||||
|
||||
return objectPublisher
|
||||
}
|
||||
let objectPublisher = ObjectPublisher<O>(objectID: objectID, context: self)
|
||||
let objectPublisher = ObjectPublisher<O>.createUncached(objectID: objectID, context: self)
|
||||
cache.setObject(objectPublisher, forKey: objectID)
|
||||
return objectPublisher
|
||||
}
|
||||
@@ -145,6 +145,12 @@ extension NSManagedObjectContext {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
internal func objectsDidChangeObserver<U: AnyObject>(remove: U) {
|
||||
|
||||
_ = self.userInfo(for: .objectsChangeObserver(U.self), initialize: { nil as Any? })
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
@@ -172,9 +178,9 @@ extension NSManagedObjectContext {
|
||||
private func userInfo<T>(for key: UserInfoKeys, initialize: @escaping () -> T) -> T {
|
||||
|
||||
let keyString = key.keyString
|
||||
if let value = self.userInfo[keyString] {
|
||||
if let value = self.userInfo[keyString] as? T {
|
||||
|
||||
return value as! T
|
||||
return value
|
||||
}
|
||||
let value = initialize()
|
||||
self.userInfo[keyString] = value
|
||||
|
||||
@@ -130,7 +130,7 @@ public final class ObjectPublisher<O: DynamicObject>: ObjectRepresentation, Hash
|
||||
|
||||
return self
|
||||
}
|
||||
return Self.init(objectID: self.id, context: context)
|
||||
return context.objectPublisher(objectID: self.id)
|
||||
}
|
||||
|
||||
public func asReadOnly(in dataStack: DataStack) -> O? {
|
||||
@@ -184,9 +184,9 @@ public final class ObjectPublisher<O: DynamicObject>: ObjectRepresentation, Hash
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
internal convenience init(objectID: O.ObjectID, context: NSManagedObjectContext) {
|
||||
internal static func createUncached(objectID: O.ObjectID, context: NSManagedObjectContext) -> ObjectPublisher<O> {
|
||||
|
||||
self.init(
|
||||
return self.init(
|
||||
objectID: objectID,
|
||||
context: context,
|
||||
initializer: ObjectSnapshot<O>.init(objectID:context:)
|
||||
@@ -195,6 +195,7 @@ public final class ObjectPublisher<O: DynamicObject>: ObjectRepresentation, Hash
|
||||
|
||||
deinit {
|
||||
|
||||
self.context.objectsDidChangeObserver(remove: self)
|
||||
self.observers.removeAllObjects()
|
||||
}
|
||||
|
||||
@@ -221,30 +222,36 @@ public final class ObjectPublisher<O: DynamicObject>: ObjectRepresentation, Hash
|
||||
|
||||
self.rawObjectWillChange = nil
|
||||
}
|
||||
self.$lazySnapshot.initialize({ initializer(objectID, context) })
|
||||
|
||||
context.objectsDidChangeObserver(for: self).addObserver(self) { [weak self] (updatedIDs, deletedIDs) in
|
||||
self.$lazySnapshot.initialize { [weak self] in
|
||||
|
||||
guard let self = self else {
|
||||
|
||||
return
|
||||
return initializer(objectID, context)
|
||||
}
|
||||
if deletedIDs.contains(objectID) {
|
||||
context.objectsDidChangeObserver(for: self).addObserver(self) { [weak self] (updatedIDs, deletedIDs) in
|
||||
|
||||
self.object = nil
|
||||
guard let self = self else {
|
||||
|
||||
self.willChange()
|
||||
self.$lazySnapshot.reset({ nil })
|
||||
self.didChange()
|
||||
self.notifyObservers()
|
||||
}
|
||||
else if updatedIDs.contains(objectID) {
|
||||
|
||||
self.willChange()
|
||||
self.$lazySnapshot.reset({ initializer(objectID, context) })
|
||||
self.didChange()
|
||||
self.notifyObservers()
|
||||
return
|
||||
}
|
||||
if deletedIDs.contains(objectID) {
|
||||
|
||||
self.object = nil
|
||||
|
||||
self.willChange()
|
||||
self.$lazySnapshot.reset({ nil })
|
||||
self.didChange()
|
||||
self.notifyObservers()
|
||||
}
|
||||
else if updatedIDs.contains(objectID) {
|
||||
|
||||
self.willChange()
|
||||
self.$lazySnapshot.reset({ initializer(objectID, context) })
|
||||
self.didChange()
|
||||
self.notifyObservers()
|
||||
}
|
||||
}
|
||||
return initializer(objectID, context)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -329,7 +336,6 @@ extension ObjectPublisher {
|
||||
|
||||
// MARK: - ObjectPublisher where O: NSManagedObject
|
||||
|
||||
@available(*, unavailable, message: "KeyPaths accessed from @dynamicMemberLookup types can't generate KVC keys yet (https://bugs.swift.org/browse/SR-11351)")
|
||||
extension ObjectPublisher where O: NSManagedObject {
|
||||
|
||||
// MARK: Public
|
||||
@@ -337,11 +343,21 @@ extension ObjectPublisher where O: NSManagedObject {
|
||||
/**
|
||||
Returns the value for the property identified by a given key.
|
||||
*/
|
||||
@available(*, unavailable, message: "KeyPaths accessed from @dynamicMemberLookup types can't generate KVC keys yet (https://bugs.swift.org/browse/SR-11351)")
|
||||
public subscript<V: AllowedObjectiveCKeyPathValue>(dynamicMember member: KeyPath<O, V>) -> V {
|
||||
|
||||
fatalError()
|
||||
// return self.snapshot[dynamicMember: member]
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the value for the property identified by a given key.
|
||||
*/
|
||||
public func value<V: AllowedObjectiveCKeyPathValue>(forKeyPath keyPath: KeyPath<O, V>) -> V! {
|
||||
|
||||
let key = String(keyPath: keyPath)
|
||||
return self.snapshot?.dictionaryForValues()[key] as! V?
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -75,6 +75,29 @@ extension CoreStoreObject: ObjectRepresentation {}
|
||||
|
||||
extension DynamicObject where Self: ObjectRepresentation {
|
||||
|
||||
// MARK: Public
|
||||
|
||||
/**
|
||||
An `ObjectPublisher` wrapper for the exact same object
|
||||
*/
|
||||
public func asPublisher() -> ObjectPublisher<Self>? {
|
||||
|
||||
return self.cs_toRaw()
|
||||
.managedObjectContext
|
||||
.map({ $0.objectPublisher(objectID: self.cs_id()) })
|
||||
}
|
||||
|
||||
/**
|
||||
A thread-safe `struct` that is a full-copy of the object's properties
|
||||
*/
|
||||
public func asSnapshot() -> ObjectSnapshot<Self>? {
|
||||
|
||||
return self.cs_toRaw()
|
||||
.managedObjectContext
|
||||
.flatMap({ ObjectSnapshot<Self>(objectID: self.cs_id(), context: $0) })
|
||||
}
|
||||
|
||||
|
||||
// MARK: ObjectRepresentation
|
||||
|
||||
public func objectID() -> Self.ObjectID {
|
||||
@@ -85,7 +108,7 @@ extension DynamicObject where Self: ObjectRepresentation {
|
||||
public func asPublisher(in dataStack: DataStack) -> ObjectPublisher<Self> {
|
||||
|
||||
let context = dataStack.unsafeContext()
|
||||
return ObjectPublisher<Self>(objectID: self.cs_id(), context: context)
|
||||
return context.objectPublisher(objectID: self.cs_id())
|
||||
}
|
||||
|
||||
public func asReadOnly(in dataStack: DataStack) -> Self? {
|
||||
|
||||
@@ -41,6 +41,14 @@ import AppKit
|
||||
*/
|
||||
@dynamicMemberLookup
|
||||
public struct ObjectSnapshot<O: DynamicObject>: ObjectRepresentation, Hashable {
|
||||
|
||||
// MARK: Public
|
||||
|
||||
public func dictionaryForValues() -> [String: Any] {
|
||||
|
||||
return self.values
|
||||
}
|
||||
|
||||
|
||||
// MARK: ObjectRepresentation
|
||||
|
||||
@@ -54,7 +62,7 @@ public struct ObjectSnapshot<O: DynamicObject>: ObjectRepresentation, Hashable {
|
||||
public func asPublisher(in dataStack: DataStack) -> ObjectPublisher<O> {
|
||||
|
||||
let context = dataStack.unsafeContext()
|
||||
return ObjectPublisher<O>(objectID: self.id, context: context)
|
||||
return context.objectPublisher(objectID: self.id)
|
||||
}
|
||||
|
||||
public func asReadOnly(in dataStack: DataStack) -> O? {
|
||||
@@ -107,14 +115,20 @@ public struct ObjectSnapshot<O: DynamicObject>: ObjectRepresentation, Hashable {
|
||||
return nil
|
||||
}
|
||||
self.id = objectID
|
||||
self.context = context
|
||||
self.values = values
|
||||
}
|
||||
|
||||
|
||||
// MARK: FilePrivate
|
||||
|
||||
fileprivate var values: [String: Any]
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let id: O.ObjectID
|
||||
private var values: [String: Any]
|
||||
private let context: NSManagedObjectContext
|
||||
|
||||
private var valuesRef: NSDictionary {
|
||||
|
||||
@@ -125,17 +139,35 @@ public struct ObjectSnapshot<O: DynamicObject>: ObjectRepresentation, Hashable {
|
||||
|
||||
// MARK: - ObjectSnapshot where O: NSManagedObject
|
||||
|
||||
@available(*, unavailable, message: "KeyPaths accessed from @dynamicMemberLookup types can't generate KVC keys yet (https://bugs.swift.org/browse/SR-11351)")
|
||||
extension ObjectSnapshot where O: NSManagedObject {
|
||||
|
||||
/**
|
||||
Returns the value for the property identified by a given key.
|
||||
*/
|
||||
@available(*, unavailable, message: "KeyPaths accessed from @dynamicMemberLookup types can't generate KVC keys yet (https://bugs.swift.org/browse/SR-11351)")
|
||||
public subscript<V: AllowedObjectiveCKeyPathValue>(dynamicMember member: KeyPath<O, V>) -> V {
|
||||
|
||||
let key = String(keyPath: member)
|
||||
return self.values[key] as! V
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the value for the property identified by a given key.
|
||||
*/
|
||||
public func value<V: AllowedObjectiveCKeyPathValue>(forKeyPath keyPath: KeyPath<O, V>) -> V! {
|
||||
|
||||
let key = String(keyPath: keyPath)
|
||||
return self.values[key] as! V?
|
||||
}
|
||||
|
||||
/**
|
||||
Mutates the value for the property identified by a given key.
|
||||
*/
|
||||
public mutating func setValue<V: AllowedObjectiveCKeyPathValue>(_ value: V!, forKeyPath keyPath: KeyPath<O, V>) {
|
||||
|
||||
let key = String(keyPath: keyPath)
|
||||
self.values[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -168,7 +200,7 @@ extension ObjectSnapshot where O: CoreStoreObject {
|
||||
get {
|
||||
|
||||
let key = String(keyPath: member)
|
||||
return self.values[key] as! V?
|
||||
return self.values[key] as? V
|
||||
}
|
||||
set {
|
||||
|
||||
@@ -202,7 +234,7 @@ extension ObjectSnapshot where O: CoreStoreObject {
|
||||
get {
|
||||
|
||||
let key = String(keyPath: member)
|
||||
return self.values[key] as! V?
|
||||
return self.values[key] as? V
|
||||
}
|
||||
set {
|
||||
|
||||
@@ -214,51 +246,59 @@ extension ObjectSnapshot where O: CoreStoreObject {
|
||||
/**
|
||||
Returns the value for the property identified by a given key.
|
||||
*/
|
||||
public subscript<OBase, D>(dynamicMember member: KeyPath<O, RelationshipContainer<OBase>.ToOne<D>>) -> D.ObjectID? {
|
||||
public subscript<OBase, D>(dynamicMember member: KeyPath<O, RelationshipContainer<OBase>.ToOne<D>>) -> ObjectPublisher<D>? {
|
||||
|
||||
get {
|
||||
|
||||
let key = String(keyPath: member)
|
||||
return self.values[key] as! D.ObjectID?
|
||||
guard let id = self.values[key] as? D.ObjectID else {
|
||||
|
||||
return nil
|
||||
}
|
||||
return self.context.objectPublisher(objectID: id)
|
||||
}
|
||||
set {
|
||||
|
||||
let key = String(keyPath: member)
|
||||
self.values[key] = newValue
|
||||
self.values[key] = newValue?.objectID()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the value for the property identified by a given key.
|
||||
*/
|
||||
public subscript<OBase, D>(dynamicMember member: KeyPath<O, RelationshipContainer<OBase>.ToManyOrdered<D>>) -> [D.ObjectID] {
|
||||
public subscript<OBase, D>(dynamicMember member: KeyPath<O, RelationshipContainer<OBase>.ToManyOrdered<D>>) -> [ObjectPublisher<D>] {
|
||||
|
||||
get {
|
||||
|
||||
let key = String(keyPath: member)
|
||||
return self.values[key] as! [D.ObjectID]
|
||||
let context = self.context
|
||||
let ids = self.values[key] as! [D.ObjectID]
|
||||
return ids.map(context.objectPublisher(objectID:))
|
||||
}
|
||||
set {
|
||||
|
||||
let key = String(keyPath: member)
|
||||
self.values[key] = newValue
|
||||
self.values[key] = newValue.map({ $0.objectID() })
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the value for the property identified by a given key.
|
||||
*/
|
||||
public subscript<OBase, D>(dynamicMember member: KeyPath<O, RelationshipContainer<OBase>.ToManyUnordered<D>>) -> Set<D.ObjectID> {
|
||||
public subscript<OBase, D>(dynamicMember member: KeyPath<O, RelationshipContainer<OBase>.ToManyUnordered<D>>) -> Set<ObjectPublisher<D>> {
|
||||
|
||||
get {
|
||||
|
||||
let key = String(keyPath: member)
|
||||
return self.values[key] as! Set<D.ObjectID>
|
||||
let context = self.context
|
||||
let ids = self.values[key] as! Set<D.ObjectID>
|
||||
return Set(ids.map(context.objectPublisher(objectID:)))
|
||||
}
|
||||
set {
|
||||
|
||||
let key = String(keyPath: member)
|
||||
self.values[key] = newValue
|
||||
self.values[key] = Set(newValue.map({ $0.objectID() }))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -313,9 +313,9 @@ public enum RelationshipContainer<O: CoreStoreObject> {
|
||||
}
|
||||
}
|
||||
|
||||
internal var valueForSnapshot: Any {
|
||||
internal var valueForSnapshot: Any? {
|
||||
|
||||
return self.value?.objectID() as Any
|
||||
return self.value?.objectID()
|
||||
}
|
||||
|
||||
|
||||
@@ -609,9 +609,9 @@ public enum RelationshipContainer<O: CoreStoreObject> {
|
||||
}
|
||||
}
|
||||
|
||||
internal var valueForSnapshot: Any {
|
||||
internal var valueForSnapshot: Any? {
|
||||
|
||||
return self.value.map({ $0.objectID() }) as Any
|
||||
return self.value.map({ $0.objectID() })
|
||||
}
|
||||
|
||||
|
||||
@@ -910,9 +910,9 @@ public enum RelationshipContainer<O: CoreStoreObject> {
|
||||
}
|
||||
}
|
||||
|
||||
internal var valueForSnapshot: Any {
|
||||
internal var valueForSnapshot: Any? {
|
||||
|
||||
return Set(self.value.map({ $0.objectID() })) as Any
|
||||
return Set(self.value.map({ $0.objectID() }))
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -41,5 +41,5 @@ internal protocol RelationshipProtocol: PropertyProtocol {
|
||||
var renamingIdentifier: () -> String? { get }
|
||||
var minCount: Int { get }
|
||||
var maxCount: Int { get }
|
||||
var valueForSnapshot: Any { get }
|
||||
var valueForSnapshot: Any? { get }
|
||||
}
|
||||
|
||||
@@ -272,9 +272,9 @@ public enum TransformableContainer<O: CoreStoreObject> {
|
||||
}
|
||||
}
|
||||
|
||||
internal var valueForSnapshot: Any {
|
||||
internal var valueForSnapshot: Any? {
|
||||
|
||||
return self.value as Any
|
||||
return self.value
|
||||
}
|
||||
|
||||
|
||||
@@ -494,9 +494,9 @@ public enum TransformableContainer<O: CoreStoreObject> {
|
||||
}
|
||||
}
|
||||
|
||||
internal var valueForSnapshot: Any {
|
||||
internal var valueForSnapshot: Any? {
|
||||
|
||||
return self.value as Any
|
||||
return self.value
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -267,9 +267,9 @@ public enum ValueContainer<O: CoreStoreObject> {
|
||||
}
|
||||
}
|
||||
|
||||
internal var valueForSnapshot: Any {
|
||||
internal var valueForSnapshot: Any? {
|
||||
|
||||
return self.value as Any
|
||||
return self.value
|
||||
}
|
||||
|
||||
|
||||
@@ -489,9 +489,9 @@ public enum ValueContainer<O: CoreStoreObject> {
|
||||
}
|
||||
}
|
||||
|
||||
internal var valueForSnapshot: Any {
|
||||
internal var valueForSnapshot: Any? {
|
||||
|
||||
return self.value as Any
|
||||
return self.value
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,296 +0,0 @@
|
||||
//
|
||||
// DiffableDataSource.CollectionView-AppKit.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2018 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
#if canImport(AppKit) && os(macOS)
|
||||
|
||||
import AppKit
|
||||
import CoreData
|
||||
|
||||
|
||||
// MARK: - DiffableDataSource
|
||||
|
||||
extension DiffableDataSource {
|
||||
|
||||
// MARK: - CollectionView
|
||||
|
||||
/**
|
||||
The `DiffableDataSource.CollectionView` serves as a `NSCollectionViewDataSource` that handles `ListPublisher` snapshots for a `NSCollectionView`. Subclasses of `DiffableDataSource.CollectionView` may override some `NSCollectionViewDataSource` methods as needed.
|
||||
The `DiffableDataSource.CollectionView` instance needs to be held on (retained) for as long as the `NSCollectionView`'s lifecycle.
|
||||
```
|
||||
self.dataSource = DiffableDataSource.CollectionView<Person>(
|
||||
collectionView: self.collectionView,
|
||||
dataStack: CoreStoreDefaults.dataStack,
|
||||
itemProvider: { (collectionView, indexPath, person) in
|
||||
let item = collectionView.makeItem(withIdentifier: .collectionViewItem, for: indexPath) as! PersonItem
|
||||
item.setPerson(person)
|
||||
return item
|
||||
}
|
||||
)
|
||||
```
|
||||
The dataSource can then apply changes from a `ListPublisher` as shown:
|
||||
```
|
||||
listPublisher.addObserver(self) { [weak self] (listPublisher) in
|
||||
self?.dataSource?.apply(
|
||||
listPublisher.snapshot,
|
||||
animatingDifferences: true
|
||||
)
|
||||
}
|
||||
```
|
||||
`DiffableDataSource.CollectionView` fully handles the reload animations.
|
||||
- SeeAlso: CoreStore's DiffableDataSource implementation is based on https://github.com/ra1028/DiffableDataSources
|
||||
*/
|
||||
open class CollectionView<O: DynamicObject>: NSObject, NSCollectionViewDataSource {
|
||||
|
||||
// MARK: Public
|
||||
|
||||
/**
|
||||
The object type represented by this dataSource
|
||||
*/
|
||||
public typealias ObjectType = O
|
||||
|
||||
/**
|
||||
Initializes the `DiffableDataSource.CollectionView`. This instance needs to be held on (retained) for as long as the `NSCollectionView`'s lifecycle.
|
||||
```
|
||||
self.dataSource = DiffableDataSource.CollectionView<Person>(
|
||||
collectionView: self.collectionView,
|
||||
dataStack: CoreStoreDefaults.dataStack,
|
||||
itemProvider: { (collectionView, indexPath, person) in
|
||||
let item = collectionView.makeItem(withIdentifier: .collectionViewItem, for: indexPath) as! PersonItem
|
||||
item.setPerson(person)
|
||||
return item
|
||||
}
|
||||
)
|
||||
```
|
||||
- parameter collectionView: the `NSCollectionView` to set the `dataSource` of. This instance is not retained by the `DiffableDataSource.CollectionView`.
|
||||
- parameter dataStack: the `DataStack` instance that the dataSource will fetch objects from
|
||||
- parameter itemProvider: a closure that configures and returns the `NSCollectionViewItem` for the object
|
||||
*/
|
||||
@nonobjc
|
||||
public init(collectionView: NSCollectionView, dataStack: DataStack, itemProvider: @escaping (NSCollectionView, IndexPath, O) -> NSCollectionViewItem?, supplementaryViewProvider: @escaping (NSCollectionView, String, IndexPath) -> NSView? = { _, _, _ in nil }) {
|
||||
|
||||
self.collectionView = collectionView
|
||||
self.itemProvider = itemProvider
|
||||
self.supplementaryViewProvider = supplementaryViewProvider
|
||||
self.dataStack = dataStack
|
||||
self.dispatcher = Internals.DiffableDataUIDispatcher<O>(dataStack: dataStack)
|
||||
|
||||
super.init()
|
||||
|
||||
collectionView.dataSource = self
|
||||
}
|
||||
|
||||
/**
|
||||
Reloads the `NSCollectionView` using a `ListSnapshot`. This is typically from the `snapshot` property of a `ListPublisher`:
|
||||
```
|
||||
listPublisher.addObserver(self) { [weak self] (listPublisher) in
|
||||
self?.dataSource?.apply(
|
||||
listPublisher.snapshot,
|
||||
animatingDifferences: true
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
- parameter snapshot: the `ListSnapshot` used to reload the `UITableView` with. This is typically from the `snapshot` property of a `ListPublisher`.
|
||||
- parameter animatingDifferences: if `true`, animations will be applied as configured by the `defaultRowAnimation` value. Defaults to `true`.
|
||||
*/
|
||||
public func apply(_ snapshot: ListSnapshot<O>, animatingDifferences: Bool = true) {
|
||||
|
||||
let diffableSnapshot = snapshot.diffableSnapshot
|
||||
self.dispatcher.apply(
|
||||
diffableSnapshot as! Internals.DiffableDataSourceSnapshot,
|
||||
view: self.collectionView,
|
||||
animatingDifferences: animatingDifferences,
|
||||
performUpdates: { collectionView, changeset, setSections in
|
||||
|
||||
collectionView.reload(
|
||||
using: changeset,
|
||||
setData: setSections
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the object identifier for the item at the specified `IndexPath`, or `nil` if not found
|
||||
|
||||
- parameter indexPath: the `IndexPath` to search for
|
||||
- returns: the object identifier for the item at the specified `IndexPath`, or `nil` if not found
|
||||
*/
|
||||
@nonobjc
|
||||
public func itemID(for indexPath: IndexPath) -> O.ObjectID? {
|
||||
|
||||
return self.dispatcher.itemIdentifier(for: indexPath)
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the `IndexPath` for the item with the specified object identifier, or `nil` if not found
|
||||
|
||||
- parameter itemID: the object identifier to search for
|
||||
- returns: the `IndexPath` for the item with the specified object identifier, or `nil` if not found
|
||||
*/
|
||||
@nonobjc
|
||||
public func indexPath(for itemID: O.ObjectID) -> IndexPath? {
|
||||
|
||||
return self.dispatcher.indexPath(for: itemID)
|
||||
}
|
||||
|
||||
|
||||
// MARK: - NSCollectionViewDataSource
|
||||
|
||||
@objc
|
||||
public dynamic func numberOfSections(in collectionView: NSCollectionView) -> Int {
|
||||
|
||||
return self.dispatcher.numberOfSections()
|
||||
}
|
||||
|
||||
@objc
|
||||
public dynamic func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||
|
||||
return self.dispatcher.numberOfItems(inSection: section)
|
||||
}
|
||||
|
||||
@objc
|
||||
open dynamic func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
|
||||
|
||||
guard let objectID = self.dispatcher.itemIdentifier(for: indexPath) else {
|
||||
|
||||
Internals.abort("Object at \(Internals.typeName(IndexPath.self)) \(indexPath) already removed from list")
|
||||
}
|
||||
guard let object = self.dataStack.fetchExisting(objectID) as O? else {
|
||||
|
||||
Internals.abort("Object at \(Internals.typeName(IndexPath.self)) \(indexPath) has been deleted")
|
||||
}
|
||||
guard let item = self.itemProvider(collectionView, indexPath, object) else {
|
||||
|
||||
Internals.abort("\(Internals.typeName(NSCollectionViewDataSource.self)) returned a `nil` item for \(Internals.typeName(IndexPath.self)) \(indexPath)")
|
||||
}
|
||||
return item
|
||||
}
|
||||
|
||||
@objc
|
||||
open dynamic func collectionView(_ collectionView: NSCollectionView, viewForSupplementaryElementOfKind kind: NSCollectionView.SupplementaryElementKind, at indexPath: IndexPath) -> NSView {
|
||||
|
||||
guard let view = self.supplementaryViewProvider(collectionView, kind, indexPath) else {
|
||||
|
||||
return NSView()
|
||||
}
|
||||
return view
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private weak var collectionView: NSCollectionView?
|
||||
|
||||
private let dataStack: DataStack
|
||||
private let itemProvider: (NSCollectionView, IndexPath, O) -> NSCollectionViewItem?
|
||||
private let supplementaryViewProvider: (NSCollectionView, String, IndexPath) -> NSView?
|
||||
private let dispatcher: Internals.DiffableDataUIDispatcher<O>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - NSCollectionView
|
||||
|
||||
extension NSCollectionView {
|
||||
|
||||
// MARK: FilePrivate
|
||||
|
||||
// Implementation based on https://github.com/ra1028/DiffableDataSources
|
||||
@nonobjc
|
||||
fileprivate func reload<C, O>(
|
||||
using stagedChangeset: Internals.DiffableDataUIDispatcher<O>.StagedChangeset<C>,
|
||||
interrupt: ((Internals.DiffableDataUIDispatcher<O>.Changeset<C>) -> Bool)? = nil,
|
||||
setData: (C) -> Void
|
||||
) {
|
||||
|
||||
if case .none = window, let data = stagedChangeset.last?.data {
|
||||
|
||||
setData(data)
|
||||
self.reloadData()
|
||||
return
|
||||
}
|
||||
for changeset in stagedChangeset {
|
||||
|
||||
if let interrupt = interrupt, interrupt(changeset), let data = stagedChangeset.last?.data {
|
||||
|
||||
setData(data)
|
||||
self.reloadData()
|
||||
return
|
||||
}
|
||||
self.animator().performBatchUpdates(
|
||||
{
|
||||
setData(changeset.data)
|
||||
|
||||
if !changeset.sectionDeleted.isEmpty {
|
||||
|
||||
self.deleteSections(IndexSet(changeset.sectionDeleted))
|
||||
}
|
||||
if !changeset.sectionInserted.isEmpty {
|
||||
|
||||
self.insertSections(IndexSet(changeset.sectionInserted))
|
||||
}
|
||||
if !changeset.sectionUpdated.isEmpty {
|
||||
|
||||
self.reloadSections(IndexSet(changeset.sectionUpdated))
|
||||
}
|
||||
for (source, target) in changeset.sectionMoved {
|
||||
|
||||
self.moveSection(source, toSection: target)
|
||||
}
|
||||
if !changeset.elementDeleted.isEmpty {
|
||||
|
||||
self.deleteItems(
|
||||
at: Set(changeset.elementDeleted.map { IndexPath(item: $0.element, section: $0.section) })
|
||||
)
|
||||
}
|
||||
if !changeset.elementInserted.isEmpty {
|
||||
|
||||
self.insertItems(
|
||||
at: Set(changeset.elementInserted.map { IndexPath(item: $0.element, section: $0.section) })
|
||||
)
|
||||
}
|
||||
if !changeset.elementUpdated.isEmpty {
|
||||
|
||||
self.reloadItems(
|
||||
at: Set(changeset.elementUpdated.map { IndexPath(item: $0.element, section: $0.section) })
|
||||
)
|
||||
}
|
||||
for (source, target) in changeset.elementMoved {
|
||||
|
||||
self.moveItem(
|
||||
at: IndexPath(item: source.element, section: source.section),
|
||||
to: IndexPath(item: target.element, section: target.section)
|
||||
)
|
||||
}
|
||||
},
|
||||
completionHandler: nil
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user