mirror of
https://github.com/JohnEstropia/CoreStore.git
synced 2026-03-30 06:11:50 +02:00
back-portable TableView DiffableDataSource
This commit is contained in:
@@ -105,6 +105,26 @@
|
|||||||
B509C7F51E54511B0061C547 /* ImportableAttributeType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B509C7F31E54511B0061C547 /* ImportableAttributeType.swift */; };
|
B509C7F51E54511B0061C547 /* ImportableAttributeType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B509C7F31E54511B0061C547 /* ImportableAttributeType.swift */; };
|
||||||
B509C7F61E54511B0061C547 /* ImportableAttributeType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B509C7F31E54511B0061C547 /* ImportableAttributeType.swift */; };
|
B509C7F61E54511B0061C547 /* ImportableAttributeType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B509C7F31E54511B0061C547 /* ImportableAttributeType.swift */; };
|
||||||
B509C7F71E54511B0061C547 /* ImportableAttributeType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B509C7F31E54511B0061C547 /* ImportableAttributeType.swift */; };
|
B509C7F71E54511B0061C547 /* ImportableAttributeType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B509C7F31E54511B0061C547 /* ImportableAttributeType.swift */; };
|
||||||
|
B50E174D23517C03004F033C /* Internals.DiffableDataUIDispatcher.StagedChangeset.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50E174C23517C03004F033C /* Internals.DiffableDataUIDispatcher.StagedChangeset.swift */; };
|
||||||
|
B50E174E23517C03004F033C /* Internals.DiffableDataUIDispatcher.StagedChangeset.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50E174C23517C03004F033C /* Internals.DiffableDataUIDispatcher.StagedChangeset.swift */; };
|
||||||
|
B50E174F23517C03004F033C /* Internals.DiffableDataUIDispatcher.StagedChangeset.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50E174C23517C03004F033C /* Internals.DiffableDataUIDispatcher.StagedChangeset.swift */; };
|
||||||
|
B50E175023517C03004F033C /* Internals.DiffableDataUIDispatcher.StagedChangeset.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50E174C23517C03004F033C /* Internals.DiffableDataUIDispatcher.StagedChangeset.swift */; };
|
||||||
|
B50E175223517C6B004F033C /* Internals.DiffableDataUIDispatcher.Changeset.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50E175123517C6B004F033C /* Internals.DiffableDataUIDispatcher.Changeset.swift */; };
|
||||||
|
B50E175323517C6B004F033C /* Internals.DiffableDataUIDispatcher.Changeset.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50E175123517C6B004F033C /* Internals.DiffableDataUIDispatcher.Changeset.swift */; };
|
||||||
|
B50E175423517C6B004F033C /* Internals.DiffableDataUIDispatcher.Changeset.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50E175123517C6B004F033C /* Internals.DiffableDataUIDispatcher.Changeset.swift */; };
|
||||||
|
B50E175523517C6B004F033C /* Internals.DiffableDataUIDispatcher.Changeset.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50E175123517C6B004F033C /* Internals.DiffableDataUIDispatcher.Changeset.swift */; };
|
||||||
|
B50E175723517DE4004F033C /* Differentiable.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50E175623517DE4004F033C /* Differentiable.swift */; };
|
||||||
|
B50E175823517DE4004F033C /* Differentiable.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50E175623517DE4004F033C /* Differentiable.swift */; };
|
||||||
|
B50E175923517DE4004F033C /* Differentiable.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50E175623517DE4004F033C /* Differentiable.swift */; };
|
||||||
|
B50E175A23517DE4004F033C /* Differentiable.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50E175623517DE4004F033C /* Differentiable.swift */; };
|
||||||
|
B50E175C2351848E004F033C /* Internals.DiffableDataUIDispatcher.DiffResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50E175B2351848E004F033C /* Internals.DiffableDataUIDispatcher.DiffResult.swift */; };
|
||||||
|
B50E175D2351848E004F033C /* Internals.DiffableDataUIDispatcher.DiffResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50E175B2351848E004F033C /* Internals.DiffableDataUIDispatcher.DiffResult.swift */; };
|
||||||
|
B50E175E2351848E004F033C /* Internals.DiffableDataUIDispatcher.DiffResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50E175B2351848E004F033C /* Internals.DiffableDataUIDispatcher.DiffResult.swift */; };
|
||||||
|
B50E175F2351848E004F033C /* Internals.DiffableDataUIDispatcher.DiffResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50E175B2351848E004F033C /* Internals.DiffableDataUIDispatcher.DiffResult.swift */; };
|
||||||
|
B50E17612351FA66004F033C /* Internals.Closure.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50E17602351FA66004F033C /* Internals.Closure.swift */; };
|
||||||
|
B50E17622351FA66004F033C /* Internals.Closure.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50E17602351FA66004F033C /* Internals.Closure.swift */; };
|
||||||
|
B50E17632351FA66004F033C /* Internals.Closure.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50E17602351FA66004F033C /* Internals.Closure.swift */; };
|
||||||
|
B50E17642351FA66004F033C /* Internals.Closure.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50E17602351FA66004F033C /* Internals.Closure.swift */; };
|
||||||
B50EE14223473C92009B8C47 /* CoreStoreObject+DataSources.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50EE14123473C92009B8C47 /* CoreStoreObject+DataSources.swift */; };
|
B50EE14223473C92009B8C47 /* CoreStoreObject+DataSources.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50EE14123473C92009B8C47 /* CoreStoreObject+DataSources.swift */; };
|
||||||
B50EE14323473C96009B8C47 /* CoreStoreObject+DataSources.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50EE14123473C92009B8C47 /* CoreStoreObject+DataSources.swift */; };
|
B50EE14323473C96009B8C47 /* CoreStoreObject+DataSources.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50EE14123473C92009B8C47 /* CoreStoreObject+DataSources.swift */; };
|
||||||
B50EE14423473C97009B8C47 /* CoreStoreObject+DataSources.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50EE14123473C92009B8C47 /* CoreStoreObject+DataSources.swift */; };
|
B50EE14423473C97009B8C47 /* CoreStoreObject+DataSources.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50EE14123473C92009B8C47 /* CoreStoreObject+DataSources.swift */; };
|
||||||
@@ -572,10 +592,10 @@
|
|||||||
B5BF7FB8234C97CE0070E741 /* DiffableDataSource.TableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FB6234C97CE0070E741 /* DiffableDataSource.TableView.swift */; };
|
B5BF7FB8234C97CE0070E741 /* DiffableDataSource.TableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FB6234C97CE0070E741 /* DiffableDataSource.TableView.swift */; };
|
||||||
B5BF7FB9234C97CE0070E741 /* DiffableDataSource.TableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FB6234C97CE0070E741 /* DiffableDataSource.TableView.swift */; };
|
B5BF7FB9234C97CE0070E741 /* DiffableDataSource.TableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FB6234C97CE0070E741 /* DiffableDataSource.TableView.swift */; };
|
||||||
B5BF7FBA234C97CE0070E741 /* DiffableDataSource.TableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FB6234C97CE0070E741 /* DiffableDataSource.TableView.swift */; };
|
B5BF7FBA234C97CE0070E741 /* DiffableDataSource.TableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FB6234C97CE0070E741 /* DiffableDataSource.TableView.swift */; };
|
||||||
B5BF7FBC234C99190070E741 /* Internals.FallbackDiffableDataUIDispatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FBB234C99190070E741 /* Internals.FallbackDiffableDataUIDispatcher.swift */; };
|
B5BF7FBC234C99190070E741 /* Internals.DiffableDataUIDispatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FBB234C99190070E741 /* Internals.DiffableDataUIDispatcher.swift */; };
|
||||||
B5BF7FBD234C99190070E741 /* Internals.FallbackDiffableDataUIDispatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FBB234C99190070E741 /* Internals.FallbackDiffableDataUIDispatcher.swift */; };
|
B5BF7FBD234C99190070E741 /* Internals.DiffableDataUIDispatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FBB234C99190070E741 /* Internals.DiffableDataUIDispatcher.swift */; };
|
||||||
B5BF7FBE234C99190070E741 /* Internals.FallbackDiffableDataUIDispatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FBB234C99190070E741 /* Internals.FallbackDiffableDataUIDispatcher.swift */; };
|
B5BF7FBE234C99190070E741 /* Internals.DiffableDataUIDispatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FBB234C99190070E741 /* Internals.DiffableDataUIDispatcher.swift */; };
|
||||||
B5BF7FBF234C99190070E741 /* Internals.FallbackDiffableDataUIDispatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FBB234C99190070E741 /* Internals.FallbackDiffableDataUIDispatcher.swift */; };
|
B5BF7FBF234C99190070E741 /* Internals.DiffableDataUIDispatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FBB234C99190070E741 /* Internals.DiffableDataUIDispatcher.swift */; };
|
||||||
B5BF7FC1234D7B2E0070E741 /* LiveObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FC0234D7B2E0070E741 /* LiveObject.swift */; };
|
B5BF7FC1234D7B2E0070E741 /* LiveObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FC0234D7B2E0070E741 /* LiveObject.swift */; };
|
||||||
B5BF7FC2234D7B2E0070E741 /* LiveObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FC0234D7B2E0070E741 /* LiveObject.swift */; };
|
B5BF7FC2234D7B2E0070E741 /* LiveObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FC0234D7B2E0070E741 /* LiveObject.swift */; };
|
||||||
B5BF7FC3234D7B2E0070E741 /* LiveObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FC0234D7B2E0070E741 /* LiveObject.swift */; };
|
B5BF7FC3234D7B2E0070E741 /* LiveObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FC0234D7B2E0070E741 /* LiveObject.swift */; };
|
||||||
@@ -878,6 +898,11 @@
|
|||||||
B504D0D51B02362500B2BBB1 /* CoreStore+Setup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CoreStore+Setup.swift"; sourceTree = "<group>"; };
|
B504D0D51B02362500B2BBB1 /* CoreStore+Setup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CoreStore+Setup.swift"; sourceTree = "<group>"; };
|
||||||
B50564D22350CC3100482308 /* PropertyProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PropertyProtocol.swift; sourceTree = "<group>"; };
|
B50564D22350CC3100482308 /* PropertyProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PropertyProtocol.swift; sourceTree = "<group>"; };
|
||||||
B509C7F31E54511B0061C547 /* ImportableAttributeType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImportableAttributeType.swift; sourceTree = "<group>"; };
|
B509C7F31E54511B0061C547 /* ImportableAttributeType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImportableAttributeType.swift; sourceTree = "<group>"; };
|
||||||
|
B50E174C23517C03004F033C /* Internals.DiffableDataUIDispatcher.StagedChangeset.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Internals.DiffableDataUIDispatcher.StagedChangeset.swift; sourceTree = "<group>"; };
|
||||||
|
B50E175123517C6B004F033C /* Internals.DiffableDataUIDispatcher.Changeset.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Internals.DiffableDataUIDispatcher.Changeset.swift; sourceTree = "<group>"; };
|
||||||
|
B50E175623517DE4004F033C /* Differentiable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Differentiable.swift; sourceTree = "<group>"; };
|
||||||
|
B50E175B2351848E004F033C /* Internals.DiffableDataUIDispatcher.DiffResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Internals.DiffableDataUIDispatcher.DiffResult.swift; sourceTree = "<group>"; };
|
||||||
|
B50E17602351FA66004F033C /* Internals.Closure.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Internals.Closure.swift; sourceTree = "<group>"; };
|
||||||
B50EE14123473C92009B8C47 /* CoreStoreObject+DataSources.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CoreStoreObject+DataSources.swift"; sourceTree = "<group>"; };
|
B50EE14123473C92009B8C47 /* CoreStoreObject+DataSources.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CoreStoreObject+DataSources.swift"; sourceTree = "<group>"; };
|
||||||
B512607E1E97A18000402229 /* CoreStoreObject+Convenience.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CoreStoreObject+Convenience.swift"; sourceTree = "<group>"; };
|
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>"; };
|
B51260881E9B252B00402229 /* NSEntityDescription+DynamicModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSEntityDescription+DynamicModel.swift"; sourceTree = "<group>"; };
|
||||||
@@ -994,7 +1019,7 @@
|
|||||||
B5BF7FAC234C41E90070E741 /* Internals.DiffableDataSourceSnapshot.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Internals.DiffableDataSourceSnapshot.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>"; };
|
B5BF7FB1234C97910070E741 /* DiffableDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiffableDataSource.swift; sourceTree = "<group>"; };
|
||||||
B5BF7FB6234C97CE0070E741 /* DiffableDataSource.TableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiffableDataSource.TableView.swift; sourceTree = "<group>"; };
|
B5BF7FB6234C97CE0070E741 /* DiffableDataSource.TableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiffableDataSource.TableView.swift; sourceTree = "<group>"; };
|
||||||
B5BF7FBB234C99190070E741 /* Internals.FallbackDiffableDataUIDispatcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Internals.FallbackDiffableDataUIDispatcher.swift; sourceTree = "<group>"; };
|
B5BF7FBB234C99190070E741 /* Internals.DiffableDataUIDispatcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Internals.DiffableDataUIDispatcher.swift; sourceTree = "<group>"; };
|
||||||
B5BF7FC0234D7B2E0070E741 /* LiveObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveObject.swift; sourceTree = "<group>"; };
|
B5BF7FC0234D7B2E0070E741 /* LiveObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveObject.swift; sourceTree = "<group>"; };
|
||||||
B5BF7FC5234D7E460070E741 /* ObjectSnapshot.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObjectSnapshot.swift; sourceTree = "<group>"; };
|
B5BF7FC5234D7E460070E741 /* ObjectSnapshot.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObjectSnapshot.swift; sourceTree = "<group>"; };
|
||||||
B5BF7FCA234D80910070E741 /* Internals.LazyNonmutating.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Internals.LazyNonmutating.swift; sourceTree = "<group>"; };
|
B5BF7FCA234D80910070E741 /* Internals.LazyNonmutating.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Internals.LazyNonmutating.swift; sourceTree = "<group>"; };
|
||||||
@@ -1316,6 +1341,7 @@
|
|||||||
B5831B741F34AC7A00A9F647 /* RelationshipProtocol.swift */,
|
B5831B741F34AC7A00A9F647 /* RelationshipProtocol.swift */,
|
||||||
B50564D22350CC3100482308 /* PropertyProtocol.swift */,
|
B50564D22350CC3100482308 /* PropertyProtocol.swift */,
|
||||||
B53D9E5823513712000F48FB /* DiffableDataSourceSnapshotProtocol.swift */,
|
B53D9E5823513712000F48FB /* DiffableDataSourceSnapshotProtocol.swift */,
|
||||||
|
B50E175623517DE4004F033C /* Differentiable.swift */,
|
||||||
);
|
);
|
||||||
name = Protocols;
|
name = Protocols;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -1714,13 +1740,17 @@
|
|||||||
B5474D142227C08700B21FEC /* Internals.CoreStoreFetchRequest.swift */,
|
B5474D142227C08700B21FEC /* Internals.CoreStoreFetchRequest.swift */,
|
||||||
B5BF7FAC234C41E90070E741 /* Internals.DiffableDataSourceSnapshot.swift */,
|
B5BF7FAC234C41E90070E741 /* Internals.DiffableDataSourceSnapshot.swift */,
|
||||||
B51260921E9B28F100402229 /* Internals.EntityIdentifier.swift */,
|
B51260921E9B28F100402229 /* Internals.EntityIdentifier.swift */,
|
||||||
B5BF7FBB234C99190070E741 /* Internals.FallbackDiffableDataUIDispatcher.swift */,
|
B5BF7FBB234C99190070E741 /* Internals.DiffableDataUIDispatcher.swift */,
|
||||||
|
B50E174C23517C03004F033C /* Internals.DiffableDataUIDispatcher.StagedChangeset.swift */,
|
||||||
|
B50E175123517C6B004F033C /* Internals.DiffableDataUIDispatcher.Changeset.swift */,
|
||||||
|
B50E175B2351848E004F033C /* Internals.DiffableDataUIDispatcher.DiffResult.swift */,
|
||||||
B501322F2346B76E00FC238B /* Internals.FetchedDiffableDataSourceSnapshotDelegate.swift */,
|
B501322F2346B76E00FC238B /* Internals.FetchedDiffableDataSourceSnapshotDelegate.swift */,
|
||||||
B54A6A541BA15F2A007870FD /* Internals.FetchedResultsControllerDelegate.swift */,
|
B54A6A541BA15F2A007870FD /* Internals.FetchedResultsControllerDelegate.swift */,
|
||||||
B5BF7FCA234D80910070E741 /* Internals.LazyNonmutating.swift */,
|
B5BF7FCA234D80910070E741 /* Internals.LazyNonmutating.swift */,
|
||||||
B5FAD6AB1B51285300714891 /* Internals.MigrationManager.swift */,
|
B5FAD6AB1B51285300714891 /* Internals.MigrationManager.swift */,
|
||||||
B5E84F2B1AFF849C0064E85B /* Internals.NotificationObserver.swift */,
|
B5E84F2B1AFF849C0064E85B /* Internals.NotificationObserver.swift */,
|
||||||
B5277676234F265F0056BE9F /* Internals.SharedNotificationObserver.swift */,
|
B5277676234F265F0056BE9F /* Internals.SharedNotificationObserver.swift */,
|
||||||
|
B50E17602351FA66004F033C /* Internals.Closure.swift */,
|
||||||
B5DE522A230BD7CC00A22534 /* Internals.swift */,
|
B5DE522A230BD7CC00A22534 /* Internals.swift */,
|
||||||
B5E84F2D1AFF849C0064E85B /* Internals.WeakObject.swift */,
|
B5E84F2D1AFF849C0064E85B /* Internals.WeakObject.swift */,
|
||||||
B51260881E9B252B00402229 /* NSEntityDescription+DynamicModel.swift */,
|
B51260881E9B252B00402229 /* NSEntityDescription+DynamicModel.swift */,
|
||||||
@@ -2063,6 +2093,7 @@
|
|||||||
B5E84F221AFF84860064E85B /* ObjectMonitor.swift in Sources */,
|
B5E84F221AFF84860064E85B /* ObjectMonitor.swift in Sources */,
|
||||||
B5ECDBF91CA804FD00C7F112 /* NSManagedObjectContext+ObjectiveC.swift in Sources */,
|
B5ECDBF91CA804FD00C7F112 /* NSManagedObjectContext+ObjectiveC.swift in Sources */,
|
||||||
B5CA2B081F7E5ACA004B1936 /* WhereClauseType.swift in Sources */,
|
B5CA2B081F7E5ACA004B1936 /* WhereClauseType.swift in Sources */,
|
||||||
|
B50E17612351FA66004F033C /* Internals.Closure.swift in Sources */,
|
||||||
B5C976E71C6E3A5A00B1AF90 /* Internals.CoreStoreFetchedResultsController.swift in Sources */,
|
B5C976E71C6E3A5A00B1AF90 /* Internals.CoreStoreFetchedResultsController.swift in Sources */,
|
||||||
B56923F51EB828BF007C4DC9 /* CSDynamicSchema.swift in Sources */,
|
B56923F51EB828BF007C4DC9 /* CSDynamicSchema.swift in Sources */,
|
||||||
B5F1DA901B9AA991007C5CBB /* ImportableUniqueObject.swift in Sources */,
|
B5F1DA901B9AA991007C5CBB /* ImportableUniqueObject.swift in Sources */,
|
||||||
@@ -2114,6 +2145,7 @@
|
|||||||
B5E84F0E1AFF847B0064E85B /* Tweak.swift in Sources */,
|
B5E84F0E1AFF847B0064E85B /* Tweak.swift in Sources */,
|
||||||
B5E1B5931CAA0C15007FD580 /* CSObjectMonitor.swift in Sources */,
|
B5E1B5931CAA0C15007FD580 /* CSObjectMonitor.swift in Sources */,
|
||||||
B5277677234F265F0056BE9F /* Internals.SharedNotificationObserver.swift in Sources */,
|
B5277677234F265F0056BE9F /* Internals.SharedNotificationObserver.swift in Sources */,
|
||||||
|
B50E175C2351848E004F033C /* Internals.DiffableDataUIDispatcher.DiffResult.swift in Sources */,
|
||||||
B5ECDC291CA81CC700C7F112 /* CSDataStack+Transaction.swift in Sources */,
|
B5ECDC291CA81CC700C7F112 /* CSDataStack+Transaction.swift in Sources */,
|
||||||
B56923F01EB827F6007C4DC9 /* XcodeSchemaMappingProvider.swift in Sources */,
|
B56923F01EB827F6007C4DC9 /* XcodeSchemaMappingProvider.swift in Sources */,
|
||||||
B5F335582348D75D00FD649F /* LiveResult.swift in Sources */,
|
B5F335582348D75D00FD649F /* LiveResult.swift in Sources */,
|
||||||
@@ -2121,6 +2153,7 @@
|
|||||||
B546F9581C99B17400D5AC55 /* CSCoreStore+Setup.swift in Sources */,
|
B546F9581C99B17400D5AC55 /* CSCoreStore+Setup.swift in Sources */,
|
||||||
B5E84F361AFF85470064E85B /* NSManagedObjectContext+Setup.swift in Sources */,
|
B5E84F361AFF85470064E85B /* NSManagedObjectContext+Setup.swift in Sources */,
|
||||||
B5FAD6AE1B518DCB00714891 /* CoreStore+Migration.swift in Sources */,
|
B5FAD6AE1B518DCB00714891 /* CoreStore+Migration.swift in Sources */,
|
||||||
|
B50E175223517C6B004F033C /* Internals.DiffableDataUIDispatcher.Changeset.swift in Sources */,
|
||||||
B5E84EE71AFF84610064E85B /* CoreStore+Logging.swift in Sources */,
|
B5E84EE71AFF84610064E85B /* CoreStore+Logging.swift in Sources */,
|
||||||
B546F9731C9C553300D5AC55 /* SetupResult.swift in Sources */,
|
B546F9731C9C553300D5AC55 /* SetupResult.swift in Sources */,
|
||||||
B53CA9A21EF1EF1600E0F440 /* PartialObject.swift in Sources */,
|
B53CA9A21EF1EF1600E0F440 /* PartialObject.swift in Sources */,
|
||||||
@@ -2160,6 +2193,7 @@
|
|||||||
B596BBB61DD5BC67001DCDD9 /* FetchableSource.swift in Sources */,
|
B596BBB61DD5BC67001DCDD9 /* FetchableSource.swift in Sources */,
|
||||||
B5E1B5A21CAA4365007FD580 /* CSCoreStore+Observing.swift in Sources */,
|
B5E1B5A21CAA4365007FD580 /* CSCoreStore+Observing.swift in Sources */,
|
||||||
B5E84EDF1AFF84500064E85B /* DataStack.swift in Sources */,
|
B5E84EDF1AFF84500064E85B /* DataStack.swift in Sources */,
|
||||||
|
B50E175723517DE4004F033C /* Differentiable.swift in Sources */,
|
||||||
B53304AA230BA4F7007C2BD8 /* DynamicObjectMeta.swift in Sources */,
|
B53304AA230BA4F7007C2BD8 /* DynamicObjectMeta.swift in Sources */,
|
||||||
B59AFF411C6593E400C0ABE2 /* NSPersistentStoreCoordinator+Setup.swift in Sources */,
|
B59AFF411C6593E400C0ABE2 /* NSPersistentStoreCoordinator+Setup.swift in Sources */,
|
||||||
B5E84F231AFF84860064E85B /* ListMonitor.swift in Sources */,
|
B5E84F231AFF84860064E85B /* ListMonitor.swift in Sources */,
|
||||||
@@ -2177,6 +2211,7 @@
|
|||||||
B5FAD6A91B50A4B400714891 /* Progress+Convenience.swift in Sources */,
|
B5FAD6A91B50A4B400714891 /* Progress+Convenience.swift in Sources */,
|
||||||
B5E84EFC1AFF846E0064E85B /* SynchronousDataTransaction.swift in Sources */,
|
B5E84EFC1AFF846E0064E85B /* SynchronousDataTransaction.swift in Sources */,
|
||||||
B5215CA91FA4810300139E3A /* QueryChainBuilder.swift in Sources */,
|
B5215CA91FA4810300139E3A /* QueryChainBuilder.swift in Sources */,
|
||||||
|
B50E174D23517C03004F033C /* Internals.DiffableDataUIDispatcher.StagedChangeset.swift in Sources */,
|
||||||
B5E222231CA4E12600BA2E95 /* CSSynchronousDataTransaction.swift in Sources */,
|
B5E222231CA4E12600BA2E95 /* CSSynchronousDataTransaction.swift in Sources */,
|
||||||
B5E84F281AFF84920064E85B /* NSManagedObject+Convenience.swift in Sources */,
|
B5E84F281AFF84920064E85B /* NSManagedObject+Convenience.swift in Sources */,
|
||||||
B52F744A1E9B8740005F3DAC /* CoreStoreSchema.swift in Sources */,
|
B52F744A1E9B8740005F3DAC /* CoreStoreSchema.swift in Sources */,
|
||||||
@@ -2185,7 +2220,7 @@
|
|||||||
B5E84F391AFF85470064E85B /* NSManagedObjectContext+Querying.swift in Sources */,
|
B5E84F391AFF85470064E85B /* NSManagedObjectContext+Querying.swift in Sources */,
|
||||||
B56923E81EB827F5007C4DC9 /* InferredSchemaMappingProvider.swift in Sources */,
|
B56923E81EB827F5007C4DC9 /* InferredSchemaMappingProvider.swift in Sources */,
|
||||||
B53B275F1EE3B92E00E9B352 /* CoreStoreManagedObject.swift in Sources */,
|
B53B275F1EE3B92E00E9B352 /* CoreStoreManagedObject.swift in Sources */,
|
||||||
B5BF7FBC234C99190070E741 /* Internals.FallbackDiffableDataUIDispatcher.swift in Sources */,
|
B5BF7FBC234C99190070E741 /* Internals.DiffableDataUIDispatcher.swift in Sources */,
|
||||||
B5215CA41FA47DFD00139E3A /* FetchChainBuilder.swift in Sources */,
|
B5215CA41FA47DFD00139E3A /* FetchChainBuilder.swift in Sources */,
|
||||||
B5D33A011E96012400C880DE /* Relationship.swift in Sources */,
|
B5D33A011E96012400C880DE /* Relationship.swift in Sources */,
|
||||||
B5E84EE81AFF84610064E85B /* CoreStoreLogger.swift in Sources */,
|
B5E84EE81AFF84610064E85B /* CoreStoreLogger.swift in Sources */,
|
||||||
@@ -2288,7 +2323,7 @@
|
|||||||
82BA18B21C4BBD3900A0916E /* ImportableObject.swift in Sources */,
|
82BA18B21C4BBD3900A0916E /* ImportableObject.swift in Sources */,
|
||||||
82BA18AE1C4BBD3100A0916E /* DataStack+Transaction.swift in Sources */,
|
82BA18AE1C4BBD3100A0916E /* DataStack+Transaction.swift in Sources */,
|
||||||
82BA18AB1C4BBD3100A0916E /* AsynchronousDataTransaction.swift in Sources */,
|
82BA18AB1C4BBD3100A0916E /* AsynchronousDataTransaction.swift in Sources */,
|
||||||
B5BF7FBD234C99190070E741 /* Internals.FallbackDiffableDataUIDispatcher.swift in Sources */,
|
B5BF7FBD234C99190070E741 /* Internals.DiffableDataUIDispatcher.swift in Sources */,
|
||||||
B5D339D91E9489AB00C880DE /* CoreStoreObject.swift in Sources */,
|
B5D339D91E9489AB00C880DE /* CoreStoreObject.swift in Sources */,
|
||||||
82BA18CE1C4BBD7100A0916E /* Internals.FetchedResultsControllerDelegate.swift in Sources */,
|
82BA18CE1C4BBD7100A0916E /* Internals.FetchedResultsControllerDelegate.swift in Sources */,
|
||||||
B56923FB1EB82956007C4DC9 /* CSXcodeDataModelSchema.swift in Sources */,
|
B56923FB1EB82956007C4DC9 /* CSXcodeDataModelSchema.swift in Sources */,
|
||||||
@@ -2307,6 +2342,7 @@
|
|||||||
B5A992201EA898720091A2E3 /* UserInfo.swift in Sources */,
|
B5A992201EA898720091A2E3 /* UserInfo.swift in Sources */,
|
||||||
B5FE4DAD1C85D44E00FA6A91 /* SQLiteStore.swift in Sources */,
|
B5FE4DAD1C85D44E00FA6A91 /* SQLiteStore.swift in Sources */,
|
||||||
B5D339E31E948C3600C880DE /* Value.swift in Sources */,
|
B5D339E31E948C3600C880DE /* Value.swift in Sources */,
|
||||||
|
B50E175323517C6B004F033C /* Internals.DiffableDataUIDispatcher.Changeset.swift in Sources */,
|
||||||
82BA18C51C4BBD5300A0916E /* ListObserver.swift in Sources */,
|
82BA18C51C4BBD5300A0916E /* ListObserver.swift in Sources */,
|
||||||
B53FBA0D1CAB5E6500F0D40A /* CSCoreStore+Migrating.swift in Sources */,
|
B53FBA0D1CAB5E6500F0D40A /* CSCoreStore+Migrating.swift in Sources */,
|
||||||
82BA18C21C4BBD5300A0916E /* ObjectMonitor.swift in Sources */,
|
82BA18C21C4BBD5300A0916E /* ObjectMonitor.swift in Sources */,
|
||||||
@@ -2324,6 +2360,7 @@
|
|||||||
B5E1B5AA1CAA49E2007FD580 /* CSDataStack+Migrating.swift in Sources */,
|
B5E1B5AA1CAA49E2007FD580 /* CSDataStack+Migrating.swift in Sources */,
|
||||||
B5D339F21E94AF5800C880DE /* CoreStoreStrings.swift in Sources */,
|
B5D339F21E94AF5800C880DE /* CoreStoreStrings.swift in Sources */,
|
||||||
B5E1B59F1CAA2568007FD580 /* CSDataStack+Observing.swift in Sources */,
|
B5E1B59F1CAA2568007FD580 /* CSDataStack+Observing.swift in Sources */,
|
||||||
|
B50E17622351FA66004F033C /* Internals.Closure.swift in Sources */,
|
||||||
B5ECDC251CA81A3900C7F112 /* CSCoreStore+Querying.swift in Sources */,
|
B5ECDC251CA81A3900C7F112 /* CSCoreStore+Querying.swift in Sources */,
|
||||||
B549F6741E56A92800FBAB2D /* CoreDataNativeType.swift in Sources */,
|
B549F6741E56A92800FBAB2D /* CoreDataNativeType.swift in Sources */,
|
||||||
B509C7F51E54511B0061C547 /* ImportableAttributeType.swift in Sources */,
|
B509C7F51E54511B0061C547 /* ImportableAttributeType.swift in Sources */,
|
||||||
@@ -2396,11 +2433,13 @@
|
|||||||
B5E222251CA4E12600BA2E95 /* CSSynchronousDataTransaction.swift in Sources */,
|
B5E222251CA4E12600BA2E95 /* CSSynchronousDataTransaction.swift in Sources */,
|
||||||
82BA18C41C4BBD5300A0916E /* ListMonitor.swift in Sources */,
|
82BA18C41C4BBD5300A0916E /* ListMonitor.swift in Sources */,
|
||||||
B5215CAA1FA4810300139E3A /* QueryChainBuilder.swift in Sources */,
|
B5215CAA1FA4810300139E3A /* QueryChainBuilder.swift in Sources */,
|
||||||
|
B50E175823517DE4004F033C /* Differentiable.swift in Sources */,
|
||||||
82BA18BA1C4BBD4A00A0916E /* Select.swift in Sources */,
|
82BA18BA1C4BBD4A00A0916E /* Select.swift in Sources */,
|
||||||
B52F744B1E9B8740005F3DAC /* CoreStoreSchema.swift in Sources */,
|
B52F744B1E9B8740005F3DAC /* CoreStoreSchema.swift in Sources */,
|
||||||
B5AEFAB61C9962AE00AD137F /* CoreStoreBridge.swift in Sources */,
|
B5AEFAB61C9962AE00AD137F /* CoreStoreBridge.swift in Sources */,
|
||||||
B5E2222C1CA51B6E00BA2E95 /* CSUnsafeDataTransaction.swift in Sources */,
|
B5E2222C1CA51B6E00BA2E95 /* CSUnsafeDataTransaction.swift in Sources */,
|
||||||
82BA18A71C4BBD2900A0916E /* CoreStore+Logging.swift in Sources */,
|
82BA18A71C4BBD2900A0916E /* CoreStore+Logging.swift in Sources */,
|
||||||
|
B50E174E23517C03004F033C /* Internals.DiffableDataUIDispatcher.StagedChangeset.swift in Sources */,
|
||||||
82BA18D81C4BBD7100A0916E /* Internals.WeakObject.swift in Sources */,
|
82BA18D81C4BBD7100A0916E /* Internals.WeakObject.swift in Sources */,
|
||||||
B56923E91EB827F5007C4DC9 /* InferredSchemaMappingProvider.swift in Sources */,
|
B56923E91EB827F5007C4DC9 /* InferredSchemaMappingProvider.swift in Sources */,
|
||||||
B53B27601EE3B92E00E9B352 /* CoreStoreManagedObject.swift in Sources */,
|
B53B27601EE3B92E00E9B352 /* CoreStoreManagedObject.swift in Sources */,
|
||||||
@@ -2436,6 +2475,7 @@
|
|||||||
B52F743E1E9B8724005F3DAC /* DynamicSchema.swift in Sources */,
|
B52F743E1E9B8724005F3DAC /* DynamicSchema.swift in Sources */,
|
||||||
18166884232B9ED00097C275 /* KeyPath+KeyPaths.swift in Sources */,
|
18166884232B9ED00097C275 /* KeyPath+KeyPaths.swift in Sources */,
|
||||||
B5E8A72121C1015300EF006A /* CoreStoreObject+Observing.swift in Sources */,
|
B5E8A72121C1015300EF006A /* CoreStoreObject+Observing.swift in Sources */,
|
||||||
|
B50E175D2351848E004F033C /* Internals.DiffableDataUIDispatcher.DiffResult.swift in Sources */,
|
||||||
B5474D162227C08700B21FEC /* Internals.CoreStoreFetchRequest.swift in Sources */,
|
B5474D162227C08700B21FEC /* Internals.CoreStoreFetchRequest.swift in Sources */,
|
||||||
B501322B2346A9AE00FC238B /* LiveList.swift in Sources */,
|
B501322B2346A9AE00FC238B /* LiveList.swift in Sources */,
|
||||||
B56924001EB82976007C4DC9 /* CSUnsafeDataModelSchema.swift in Sources */,
|
B56924001EB82976007C4DC9 /* CSUnsafeDataModelSchema.swift in Sources */,
|
||||||
@@ -2507,7 +2547,7 @@
|
|||||||
B546F9761C9C553300D5AC55 /* SetupResult.swift in Sources */,
|
B546F9761C9C553300D5AC55 /* SetupResult.swift in Sources */,
|
||||||
B53FBA161CAB63CB00F0D40A /* Progress+ObjectiveC.swift in Sources */,
|
B53FBA161CAB63CB00F0D40A /* Progress+ObjectiveC.swift in Sources */,
|
||||||
B5ECDC271CA81A3900C7F112 /* CSCoreStore+Querying.swift in Sources */,
|
B5ECDC271CA81A3900C7F112 /* CSCoreStore+Querying.swift in Sources */,
|
||||||
B5BF7FBF234C99190070E741 /* Internals.FallbackDiffableDataUIDispatcher.swift in Sources */,
|
B5BF7FBF234C99190070E741 /* Internals.DiffableDataUIDispatcher.swift in Sources */,
|
||||||
B5D339DB1E9489AB00C880DE /* CoreStoreObject.swift in Sources */,
|
B5D339DB1E9489AB00C880DE /* CoreStoreObject.swift in Sources */,
|
||||||
B52DD1951BE1F92500949AFE /* CoreStoreError.swift in Sources */,
|
B52DD1951BE1F92500949AFE /* CoreStoreError.swift in Sources */,
|
||||||
B56923FD1EB82956007C4DC9 /* CSXcodeDataModelSchema.swift in Sources */,
|
B56923FD1EB82956007C4DC9 /* CSXcodeDataModelSchema.swift in Sources */,
|
||||||
@@ -2526,6 +2566,7 @@
|
|||||||
B5A992221EA898720091A2E3 /* UserInfo.swift in Sources */,
|
B5A992221EA898720091A2E3 /* UserInfo.swift in Sources */,
|
||||||
B5D7A5BA1CA3BF8F005C752B /* CSInto.swift in Sources */,
|
B5D7A5BA1CA3BF8F005C752B /* CSInto.swift in Sources */,
|
||||||
B5D339E51E948C3600C880DE /* Value.swift in Sources */,
|
B5D339E51E948C3600C880DE /* Value.swift in Sources */,
|
||||||
|
B50E175523517C6B004F033C /* Internals.DiffableDataUIDispatcher.Changeset.swift in Sources */,
|
||||||
B5A5F26A1CAEC50F004AB9AF /* CSSelect.swift in Sources */,
|
B5A5F26A1CAEC50F004AB9AF /* CSSelect.swift in Sources */,
|
||||||
B5220E1B1D13079B009BC71E /* CSCoreStore+Observing.swift in Sources */,
|
B5220E1B1D13079B009BC71E /* CSCoreStore+Observing.swift in Sources */,
|
||||||
B5FEC1911C9166E700532541 /* NSPersistentStore+Setup.swift in Sources */,
|
B5FEC1911C9166E700532541 /* NSPersistentStore+Setup.swift in Sources */,
|
||||||
@@ -2543,6 +2584,7 @@
|
|||||||
B5D339F41E94AF5800C880DE /* CoreStoreStrings.swift in Sources */,
|
B5D339F41E94AF5800C880DE /* CoreStoreStrings.swift in Sources */,
|
||||||
B5220E241D13085E009BC71E /* NSFetchedResultsController+Convenience.swift in Sources */,
|
B5220E241D13085E009BC71E /* NSFetchedResultsController+Convenience.swift in Sources */,
|
||||||
B559CD471CAA8B6300E4D58B /* CSSetupResult.swift in Sources */,
|
B559CD471CAA8B6300E4D58B /* CSSetupResult.swift in Sources */,
|
||||||
|
B50E17642351FA66004F033C /* Internals.Closure.swift in Sources */,
|
||||||
B5ECDBF01CA6BF2000C7F112 /* CSFrom.swift in Sources */,
|
B5ECDBF01CA6BF2000C7F112 /* CSFrom.swift in Sources */,
|
||||||
B549F6761E56A92800FBAB2D /* CoreDataNativeType.swift in Sources */,
|
B549F6761E56A92800FBAB2D /* CoreDataNativeType.swift in Sources */,
|
||||||
B509C7F71E54511B0061C547 /* ImportableAttributeType.swift in Sources */,
|
B509C7F71E54511B0061C547 /* ImportableAttributeType.swift in Sources */,
|
||||||
@@ -2615,11 +2657,13 @@
|
|||||||
B5220E1A1D130791009BC71E /* Internals.CoreStoreFetchedResultsController.swift in Sources */,
|
B5220E1A1D130791009BC71E /* Internals.CoreStoreFetchedResultsController.swift in Sources */,
|
||||||
B5215CAC1FA4810300139E3A /* QueryChainBuilder.swift in Sources */,
|
B5215CAC1FA4810300139E3A /* QueryChainBuilder.swift in Sources */,
|
||||||
B53FBA0F1CAB5E6500F0D40A /* CSCoreStore+Migrating.swift in Sources */,
|
B53FBA0F1CAB5E6500F0D40A /* CSCoreStore+Migrating.swift in Sources */,
|
||||||
|
B50E175A23517DE4004F033C /* Differentiable.swift in Sources */,
|
||||||
B52F744D1E9B8740005F3DAC /* CoreStoreSchema.swift in Sources */,
|
B52F744D1E9B8740005F3DAC /* CoreStoreSchema.swift in Sources */,
|
||||||
B52DD19A1BE1F92800949AFE /* CoreStore+Logging.swift in Sources */,
|
B52DD19A1BE1F92800949AFE /* CoreStore+Logging.swift in Sources */,
|
||||||
B52DD1A71BE1F93200949AFE /* BaseDataTransaction+Querying.swift in Sources */,
|
B52DD1A71BE1F93200949AFE /* BaseDataTransaction+Querying.swift in Sources */,
|
||||||
B546F96C1C9AF26D00D5AC55 /* CSInMemoryStore.swift in Sources */,
|
B546F96C1C9AF26D00D5AC55 /* CSInMemoryStore.swift in Sources */,
|
||||||
B56923EB1EB827F5007C4DC9 /* InferredSchemaMappingProvider.swift in Sources */,
|
B56923EB1EB827F5007C4DC9 /* InferredSchemaMappingProvider.swift in Sources */,
|
||||||
|
B50E175023517C03004F033C /* Internals.DiffableDataUIDispatcher.StagedChangeset.swift in Sources */,
|
||||||
B53B27621EE3B92E00E9B352 /* CoreStoreManagedObject.swift in Sources */,
|
B53B27621EE3B92E00E9B352 /* CoreStoreManagedObject.swift in Sources */,
|
||||||
B5215CA71FA47DFD00139E3A /* FetchChainBuilder.swift in Sources */,
|
B5215CA71FA47DFD00139E3A /* FetchChainBuilder.swift in Sources */,
|
||||||
B5D33A041E96012400C880DE /* Relationship.swift in Sources */,
|
B5D33A041E96012400C880DE /* Relationship.swift in Sources */,
|
||||||
@@ -2655,6 +2699,7 @@
|
|||||||
B52F74401E9B8724005F3DAC /* DynamicSchema.swift in Sources */,
|
B52F74401E9B8724005F3DAC /* DynamicSchema.swift in Sources */,
|
||||||
18166886232B9ED20097C275 /* KeyPath+KeyPaths.swift in Sources */,
|
18166886232B9ED20097C275 /* KeyPath+KeyPaths.swift in Sources */,
|
||||||
B5E8A72321C1015300EF006A /* CoreStoreObject+Observing.swift in Sources */,
|
B5E8A72321C1015300EF006A /* CoreStoreObject+Observing.swift in Sources */,
|
||||||
|
B50E175F2351848E004F033C /* Internals.DiffableDataUIDispatcher.DiffResult.swift in Sources */,
|
||||||
B5474D182227C08700B21FEC /* Internals.CoreStoreFetchRequest.swift in Sources */,
|
B5474D182227C08700B21FEC /* Internals.CoreStoreFetchRequest.swift in Sources */,
|
||||||
B501322E2346A9B100FC238B /* LiveList.swift in Sources */,
|
B501322E2346A9B100FC238B /* LiveList.swift in Sources */,
|
||||||
B56924021EB82976007C4DC9 /* CSUnsafeDataModelSchema.swift in Sources */,
|
B56924021EB82976007C4DC9 /* CSUnsafeDataModelSchema.swift in Sources */,
|
||||||
@@ -2726,7 +2771,7 @@
|
|||||||
B56321AD1BD6521C006C9394 /* Internals.MigrationManager.swift in Sources */,
|
B56321AD1BD6521C006C9394 /* Internals.MigrationManager.swift in Sources */,
|
||||||
B563219D1BD65216006C9394 /* DataStack+Observing.swift in Sources */,
|
B563219D1BD65216006C9394 /* DataStack+Observing.swift in Sources */,
|
||||||
B56321961BD65216006C9394 /* From.swift in Sources */,
|
B56321961BD65216006C9394 /* From.swift in Sources */,
|
||||||
B5BF7FBE234C99190070E741 /* Internals.FallbackDiffableDataUIDispatcher.swift in Sources */,
|
B5BF7FBE234C99190070E741 /* Internals.DiffableDataUIDispatcher.swift in Sources */,
|
||||||
B5D339DA1E9489AB00C880DE /* CoreStoreObject.swift in Sources */,
|
B5D339DA1E9489AB00C880DE /* CoreStoreObject.swift in Sources */,
|
||||||
B5ECDC021CA80CBA00C7F112 /* CSWhere.swift in Sources */,
|
B5ECDC021CA80CBA00C7F112 /* CSWhere.swift in Sources */,
|
||||||
B56923FC1EB82956007C4DC9 /* CSXcodeDataModelSchema.swift in Sources */,
|
B56923FC1EB82956007C4DC9 /* CSXcodeDataModelSchema.swift in Sources */,
|
||||||
@@ -2745,6 +2790,7 @@
|
|||||||
B5A992211EA898720091A2E3 /* UserInfo.swift in Sources */,
|
B5A992211EA898720091A2E3 /* UserInfo.swift in Sources */,
|
||||||
B563218C1BD65216006C9394 /* DataStack+Transaction.swift in Sources */,
|
B563218C1BD65216006C9394 /* DataStack+Transaction.swift in Sources */,
|
||||||
B5D339E41E948C3600C880DE /* Value.swift in Sources */,
|
B5D339E41E948C3600C880DE /* Value.swift in Sources */,
|
||||||
|
B50E175423517C6B004F033C /* Internals.DiffableDataUIDispatcher.Changeset.swift in Sources */,
|
||||||
B53FBA0E1CAB5E6500F0D40A /* CSCoreStore+Migrating.swift in Sources */,
|
B53FBA0E1CAB5E6500F0D40A /* CSCoreStore+Migrating.swift in Sources */,
|
||||||
B563219E1BD65216006C9394 /* CoreStore+Observing.swift in Sources */,
|
B563219E1BD65216006C9394 /* CoreStore+Observing.swift in Sources */,
|
||||||
B5D7A5B91CA3BF8F005C752B /* CSInto.swift in Sources */,
|
B5D7A5B91CA3BF8F005C752B /* CSInto.swift in Sources */,
|
||||||
@@ -2762,6 +2808,7 @@
|
|||||||
B5D339F31E94AF5800C880DE /* CoreStoreStrings.swift in Sources */,
|
B5D339F31E94AF5800C880DE /* CoreStoreStrings.swift in Sources */,
|
||||||
B5E1B5A01CAA2568007FD580 /* CSDataStack+Observing.swift in Sources */,
|
B5E1B5A01CAA2568007FD580 /* CSDataStack+Observing.swift in Sources */,
|
||||||
B5ECDC261CA81A3900C7F112 /* CSCoreStore+Querying.swift in Sources */,
|
B5ECDC261CA81A3900C7F112 /* CSCoreStore+Querying.swift in Sources */,
|
||||||
|
B50E17632351FA66004F033C /* Internals.Closure.swift in Sources */,
|
||||||
B563217F1BD65216006C9394 /* CoreStore.swift in Sources */,
|
B563217F1BD65216006C9394 /* CoreStore.swift in Sources */,
|
||||||
B549F6751E56A92800FBAB2D /* CoreDataNativeType.swift in Sources */,
|
B549F6751E56A92800FBAB2D /* CoreDataNativeType.swift in Sources */,
|
||||||
B509C7F61E54511B0061C547 /* ImportableAttributeType.swift in Sources */,
|
B509C7F61E54511B0061C547 /* ImportableAttributeType.swift in Sources */,
|
||||||
@@ -2834,11 +2881,13 @@
|
|||||||
B56321A51BD65216006C9394 /* MigrationChain.swift in Sources */,
|
B56321A51BD65216006C9394 /* MigrationChain.swift in Sources */,
|
||||||
B5E222261CA4E12600BA2E95 /* CSSynchronousDataTransaction.swift in Sources */,
|
B5E222261CA4E12600BA2E95 /* CSSynchronousDataTransaction.swift in Sources */,
|
||||||
B56321A21BD65216006C9394 /* ListObserver.swift in Sources */,
|
B56321A21BD65216006C9394 /* ListObserver.swift in Sources */,
|
||||||
|
B50E175923517DE4004F033C /* Differentiable.swift in Sources */,
|
||||||
B5215CAB1FA4810300139E3A /* QueryChainBuilder.swift in Sources */,
|
B5215CAB1FA4810300139E3A /* QueryChainBuilder.swift in Sources */,
|
||||||
B563218A1BD65216006C9394 /* SynchronousDataTransaction.swift in Sources */,
|
B563218A1BD65216006C9394 /* SynchronousDataTransaction.swift in Sources */,
|
||||||
B52F744C1E9B8740005F3DAC /* CoreStoreSchema.swift in Sources */,
|
B52F744C1E9B8740005F3DAC /* CoreStoreSchema.swift in Sources */,
|
||||||
B5AEFAB71C9962AE00AD137F /* CoreStoreBridge.swift in Sources */,
|
B5AEFAB71C9962AE00AD137F /* CoreStoreBridge.swift in Sources */,
|
||||||
B5E2222D1CA51B6E00BA2E95 /* CSUnsafeDataTransaction.swift in Sources */,
|
B5E2222D1CA51B6E00BA2E95 /* CSUnsafeDataTransaction.swift in Sources */,
|
||||||
|
B50E174F23517C03004F033C /* Internals.DiffableDataUIDispatcher.StagedChangeset.swift in Sources */,
|
||||||
B563219F1BD65216006C9394 /* ObjectMonitor.swift in Sources */,
|
B563219F1BD65216006C9394 /* ObjectMonitor.swift in Sources */,
|
||||||
B56321B61BD6521C006C9394 /* Internals.WeakObject.swift in Sources */,
|
B56321B61BD6521C006C9394 /* Internals.WeakObject.swift in Sources */,
|
||||||
B56923EA1EB827F5007C4DC9 /* InferredSchemaMappingProvider.swift in Sources */,
|
B56923EA1EB827F5007C4DC9 /* InferredSchemaMappingProvider.swift in Sources */,
|
||||||
@@ -2874,6 +2923,7 @@
|
|||||||
B52F743F1E9B8724005F3DAC /* DynamicSchema.swift in Sources */,
|
B52F743F1E9B8724005F3DAC /* DynamicSchema.swift in Sources */,
|
||||||
18166885232B9ED10097C275 /* KeyPath+KeyPaths.swift in Sources */,
|
18166885232B9ED10097C275 /* KeyPath+KeyPaths.swift in Sources */,
|
||||||
B5E8A72221C1015300EF006A /* CoreStoreObject+Observing.swift in Sources */,
|
B5E8A72221C1015300EF006A /* CoreStoreObject+Observing.swift in Sources */,
|
||||||
|
B50E175E2351848E004F033C /* Internals.DiffableDataUIDispatcher.DiffResult.swift in Sources */,
|
||||||
B5474D172227C08700B21FEC /* Internals.CoreStoreFetchRequest.swift in Sources */,
|
B5474D172227C08700B21FEC /* Internals.CoreStoreFetchRequest.swift in Sources */,
|
||||||
B501322D2346A9B000FC238B /* LiveList.swift in Sources */,
|
B501322D2346A9B000FC238B /* LiveList.swift in Sources */,
|
||||||
B56924011EB82976007C4DC9 /* CSUnsafeDataModelSchema.swift in Sources */,
|
B56924011EB82976007C4DC9 /* CSUnsafeDataModelSchema.swift in Sources */,
|
||||||
|
|||||||
@@ -510,18 +510,18 @@
|
|||||||
<viewControllerLayoutGuide type="bottom" id="aI4-O3-OCi"/>
|
<viewControllerLayoutGuide type="bottom" id="aI4-O3-OCi"/>
|
||||||
</layoutGuides>
|
</layoutGuides>
|
||||||
<view key="view" contentMode="scaleToFill" id="w8K-eN-RvU">
|
<view key="view" contentMode="scaleToFill" id="w8K-eN-RvU">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="309"/>
|
<rect key="frame" x="0.0" y="0.0" width="375" height="311.5"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<view contentMode="scaleToFill" verticalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="NhC-oM-bkd">
|
<view contentMode="scaleToFill" verticalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="NhC-oM-bkd">
|
||||||
<rect key="frame" x="16" y="69.5" width="343" height="77.5"/>
|
<rect key="frame" x="16" y="69.5" width="343" height="80"/>
|
||||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" id="TIX-qi-B34"/>
|
<constraint firstAttribute="height" relation="greaterThanOrEqual" id="TIX-qi-B34"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
</view>
|
</view>
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="250" text="Label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" adjustsLetterSpacingToFitWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Vfe-Yq-3Xa">
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="250" text="Label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" adjustsLetterSpacingToFitWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Vfe-Yq-3Xa">
|
||||||
<rect key="frame" x="16" y="157" width="343" height="18"/>
|
<rect key="frame" x="16" y="159.5" width="343" height="18"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" id="4h9-ha-EzR"/>
|
<constraint firstAttribute="height" relation="greaterThanOrEqual" id="4h9-ha-EzR"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
@@ -530,37 +530,37 @@
|
|||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Hue" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="sgg-Md-Nf3">
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Hue" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="sgg-Md-Nf3">
|
||||||
<rect key="frame" x="16" y="195" width="74" height="18"/>
|
<rect key="frame" x="16" y="197.5" width="74" height="18"/>
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
||||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Saturation" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="rry-vh-bRK">
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Saturation" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="rry-vh-bRK">
|
||||||
<rect key="frame" x="16" y="233" width="74" height="18"/>
|
<rect key="frame" x="16" y="235.5" width="74" height="18"/>
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
||||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Brightness" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vTa-ly-eyO">
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Brightness" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vTa-ly-eyO">
|
||||||
<rect key="frame" x="16" y="271" width="74" height="18"/>
|
<rect key="frame" x="16" y="273.5" width="74" height="18"/>
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
||||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
<slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" minValue="0.0" maxValue="359" translatesAutoresizingMaskIntoConstraints="NO" id="YQ6-fq-3Wb">
|
<slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" minValue="0.0" maxValue="359" translatesAutoresizingMaskIntoConstraints="NO" id="YQ6-fq-3Wb">
|
||||||
<rect key="frame" x="98" y="189" width="263" height="31"/>
|
<rect key="frame" x="98" y="191.5" width="263" height="31"/>
|
||||||
<connections>
|
<connections>
|
||||||
<action selector="hueSliderValueDidChange:" destination="dX3-kR-CYC" eventType="valueChanged" id="9Hy-3h-llE"/>
|
<action selector="hueSliderValueDidChange:" destination="dX3-kR-CYC" eventType="valueChanged" id="9Hy-3h-llE"/>
|
||||||
</connections>
|
</connections>
|
||||||
</slider>
|
</slider>
|
||||||
<slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="1" minValue="0.0" maxValue="1" translatesAutoresizingMaskIntoConstraints="NO" id="xXz-78-tAd">
|
<slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="1" minValue="0.0" maxValue="1" translatesAutoresizingMaskIntoConstraints="NO" id="xXz-78-tAd">
|
||||||
<rect key="frame" x="98" y="227" width="263" height="31"/>
|
<rect key="frame" x="98" y="229.5" width="263" height="31"/>
|
||||||
<connections>
|
<connections>
|
||||||
<action selector="saturationSliderValueDidChange:" destination="dX3-kR-CYC" eventType="valueChanged" id="qtU-ua-ZTc"/>
|
<action selector="saturationSliderValueDidChange:" destination="dX3-kR-CYC" eventType="valueChanged" id="qtU-ua-ZTc"/>
|
||||||
</connections>
|
</connections>
|
||||||
</slider>
|
</slider>
|
||||||
<slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="0.5" minValue="0.0" maxValue="1" translatesAutoresizingMaskIntoConstraints="NO" id="hpy-2d-eOP">
|
<slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="0.5" minValue="0.0" maxValue="1" translatesAutoresizingMaskIntoConstraints="NO" id="hpy-2d-eOP">
|
||||||
<rect key="frame" x="98" y="265" width="263" height="31"/>
|
<rect key="frame" x="98" y="267.5" width="263" height="31"/>
|
||||||
<connections>
|
<connections>
|
||||||
<action selector="brightnessSliderValueDidChange:" destination="dX3-kR-CYC" eventType="valueChanged" id="F09-EP-2iD"/>
|
<action selector="brightnessSliderValueDidChange:" destination="dX3-kR-CYC" eventType="valueChanged" id="F09-EP-2iD"/>
|
||||||
</connections>
|
</connections>
|
||||||
@@ -637,34 +637,52 @@
|
|||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<containerView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="L5f-tW-lXf">
|
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="fillEqually" translatesAutoresizingMaskIntoConstraints="NO" id="cce-yT-4dn">
|
||||||
<rect key="frame" x="0.0" y="44" width="375" height="311.5"/>
|
<rect key="frame" x="0.0" y="44" width="375" height="623"/>
|
||||||
<connections>
|
<subviews>
|
||||||
<segue destination="5Fw-je-9gI" kind="embed" id="YcI-2Z-ijV"/>
|
<containerView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="L5f-tW-lXf">
|
||||||
</connections>
|
<rect key="frame" x="0.0" y="0.0" width="375" height="311.5"/>
|
||||||
</containerView>
|
<connections>
|
||||||
<containerView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="6So-f3-4Gp">
|
<segue destination="5Fw-je-9gI" kind="embed" id="YcI-2Z-ijV"/>
|
||||||
<rect key="frame" x="0.0" y="355.5" width="375" height="311.5"/>
|
</connections>
|
||||||
<connections>
|
</containerView>
|
||||||
<segue destination="bLr-DM-i2t" kind="embed" id="OdS-gZ-CUA"/>
|
<containerView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="6So-f3-4Gp">
|
||||||
</connections>
|
<rect key="frame" x="0.0" y="311.5" width="375" height="311.5"/>
|
||||||
</containerView>
|
<connections>
|
||||||
|
<segue destination="bLr-DM-i2t" kind="embed" id="OdS-gZ-CUA"/>
|
||||||
|
</connections>
|
||||||
|
</containerView>
|
||||||
|
</subviews>
|
||||||
|
</stackView>
|
||||||
</subviews>
|
</subviews>
|
||||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstItem="6So-f3-4Gp" firstAttribute="top" secondItem="L5f-tW-lXf" secondAttribute="bottom" id="3m8-tj-Nd4"/>
|
<constraint firstAttribute="trailing" secondItem="cce-yT-4dn" secondAttribute="trailing" id="DE1-u6-1mr"/>
|
||||||
<constraint firstAttribute="trailing" secondItem="6So-f3-4Gp" secondAttribute="trailing" id="4L8-wZ-F59"/>
|
<constraint firstItem="LNL-mj-D7l" firstAttribute="top" secondItem="cce-yT-4dn" secondAttribute="bottom" id="WIZ-M3-bdr"/>
|
||||||
<constraint firstItem="L5f-tW-lXf" firstAttribute="height" secondItem="6So-f3-4Gp" secondAttribute="height" id="8XS-L3-hvN"/>
|
<constraint firstItem="cce-yT-4dn" firstAttribute="leading" secondItem="6x3-vn-Egt" secondAttribute="leading" id="ZiS-j0-ANp"/>
|
||||||
<constraint firstAttribute="bottom" secondItem="6So-f3-4Gp" secondAttribute="bottom" id="8wL-zm-wnt"/>
|
<constraint firstItem="cce-yT-4dn" firstAttribute="top" secondItem="IML-3o-caw" secondAttribute="bottom" id="zjs-CM-7r5"/>
|
||||||
<constraint firstItem="L5f-tW-lXf" firstAttribute="leading" secondItem="6x3-vn-Egt" secondAttribute="leading" id="CbE-2f-7wk"/>
|
|
||||||
<constraint firstAttribute="trailing" secondItem="L5f-tW-lXf" secondAttribute="trailing" id="dso-2g-fgA"/>
|
|
||||||
<constraint firstItem="6So-f3-4Gp" firstAttribute="leading" secondItem="6x3-vn-Egt" secondAttribute="leading" id="eXM-D3-NLv"/>
|
|
||||||
<constraint firstItem="L5f-tW-lXf" firstAttribute="top" secondItem="IML-3o-caw" secondAttribute="bottom" id="zJ5-sE-iJA"/>
|
|
||||||
</constraints>
|
</constraints>
|
||||||
</view>
|
</view>
|
||||||
<extendedEdge key="edgesForExtendedLayout" top="YES"/>
|
<extendedEdge key="edgesForExtendedLayout" top="YES"/>
|
||||||
<navigationItem key="navigationItem" title="Colors" id="7Gd-Ad-Bzu"/>
|
<navigationItem key="navigationItem" title="Colors" id="7Gd-Ad-Bzu">
|
||||||
|
<rightBarButtonItems>
|
||||||
|
<barButtonItem title="▼" width="36" id="aPM-OC-EB8">
|
||||||
|
<connections>
|
||||||
|
<action selector="toggleBottomContainerView" destination="YOI-b7-Nxn" id="7fn-z6-pne"/>
|
||||||
|
</connections>
|
||||||
|
</barButtonItem>
|
||||||
|
<barButtonItem title="▲" width="36" id="6q3-Zk-A9p">
|
||||||
|
<connections>
|
||||||
|
<action selector="toggleTopContainerView" destination="YOI-b7-Nxn" id="APN-Kk-C3n"/>
|
||||||
|
</connections>
|
||||||
|
</barButtonItem>
|
||||||
|
</rightBarButtonItems>
|
||||||
|
</navigationItem>
|
||||||
<nil key="simulatedBottomBarMetrics"/>
|
<nil key="simulatedBottomBarMetrics"/>
|
||||||
|
<connections>
|
||||||
|
<outlet property="bottomContainerView" destination="6So-f3-4Gp" id="C7O-e2-k0l"/>
|
||||||
|
<outlet property="topContainerView" destination="L5f-tW-lXf" id="DRV-mm-rBY"/>
|
||||||
|
</connections>
|
||||||
</viewController>
|
</viewController>
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="C9h-Ba-WoL" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
<placeholder placeholderIdentifier="IBFirstResponder" id="C9h-Ba-WoL" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
@@ -700,7 +718,7 @@
|
|||||||
<objects>
|
<objects>
|
||||||
<tableViewController id="3AE-ED-0oj" customClass="ListObserverDemoViewController" customModule="CoreStoreDemo" customModuleProvider="target" sceneMemberID="viewController">
|
<tableViewController id="3AE-ED-0oj" customClass="ListObserverDemoViewController" customModule="CoreStoreDemo" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" id="DAz-BE-6Ca">
|
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" id="DAz-BE-6Ca">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="309"/>
|
<rect key="frame" x="0.0" y="0.0" width="375" height="311.5"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<prototypes>
|
<prototypes>
|
||||||
<tableViewCell contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="PaletteTableViewCell" id="G3X-70-BCD" customClass="PaletteTableViewCell" customModule="CoreStoreDemo" customModuleProvider="target">
|
<tableViewCell contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="PaletteTableViewCell" id="G3X-70-BCD" customClass="PaletteTableViewCell" customModule="CoreStoreDemo" customModuleProvider="target">
|
||||||
@@ -885,11 +903,11 @@
|
|||||||
<viewControllerLayoutGuide type="bottom" id="Q1w-Zn-fPm"/>
|
<viewControllerLayoutGuide type="bottom" id="Q1w-Zn-fPm"/>
|
||||||
</layoutGuides>
|
</layoutGuides>
|
||||||
<view key="view" contentMode="scaleToFill" id="kof-sr-Xm1">
|
<view key="view" contentMode="scaleToFill" id="kof-sr-Xm1">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="309"/>
|
<rect key="frame" x="0.0" y="0.0" width="375" height="311.5"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="250" text="SwiftUI is not supported on this device/OS version" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" adjustsLetterSpacingToFitWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="M6C-fv-74B">
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="250" text="SwiftUI is not supported on this device/OS version" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" adjustsLetterSpacingToFitWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="M6C-fv-74B">
|
||||||
<rect key="frame" x="16" y="124.5" width="343" height="60"/>
|
<rect key="frame" x="16" y="126" width="343" height="60"/>
|
||||||
<fontDescription key="fontDescription" name="HelveticaNeue-Bold" family="Helvetica Neue" pointSize="25"/>
|
<fontDescription key="fontDescription" name="HelveticaNeue-Bold" family="Helvetica Neue" pointSize="25"/>
|
||||||
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
|
|||||||
@@ -43,10 +43,10 @@ struct ColorsDemo {
|
|||||||
|
|
||||||
didSet {
|
didSet {
|
||||||
|
|
||||||
self.palettes.refetch(
|
// self.palettes.refetch(
|
||||||
self.filter.whereClause(),
|
// self.filter.whereClause(),
|
||||||
OrderBy<Palette>(.ascending(\.hue))
|
// OrderBy<Palette>(.ascending(\.hue))
|
||||||
)
|
// )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,9 +73,9 @@ struct ColorsDemo {
|
|||||||
return dataStack
|
return dataStack
|
||||||
}()
|
}()
|
||||||
|
|
||||||
static let palettes: ListMonitor<Palette> = {
|
static let palettes: LiveList<Palette> = {
|
||||||
|
|
||||||
return ColorsDemo.stack.monitorSectionedList(
|
return ColorsDemo.stack.liveList(
|
||||||
From<Palette>()
|
From<Palette>()
|
||||||
.sectionBy(\.colorName)
|
.sectionBy(\.colorName)
|
||||||
.orderBy(.ascending(\.hue))
|
.orderBy(.ascending(\.hue))
|
||||||
@@ -86,15 +86,7 @@ struct ColorsDemo {
|
|||||||
|
|
||||||
// MARK: - ListObserverDemoViewController
|
// MARK: - ListObserverDemoViewController
|
||||||
|
|
||||||
class ListObserverDemoViewController: UITableViewController, ListSectionObserver {
|
class ListObserverDemoViewController: UITableViewController {
|
||||||
|
|
||||||
// MARK: NSObject
|
|
||||||
|
|
||||||
deinit {
|
|
||||||
|
|
||||||
ColorsDemo.palettes.removeObserver(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: UIViewController
|
// MARK: UIViewController
|
||||||
|
|
||||||
@@ -133,9 +125,25 @@ class ListObserverDemoViewController: UITableViewController, ListSectionObserver
|
|||||||
]
|
]
|
||||||
self.filterBarButton = filterBarButton
|
self.filterBarButton = filterBarButton
|
||||||
|
|
||||||
ColorsDemo.palettes.addObserver(self)
|
self.dataSource = DiffableDataSource.TableView<Palette>(
|
||||||
|
tableView: self.tableView,
|
||||||
|
dataStack: ColorsDemo.stack,
|
||||||
|
cellProvider: { (tableView, indexPath, palette) in
|
||||||
|
|
||||||
self.setTable(enabled: !ColorsDemo.palettes.isPendingRefetch)
|
let cell = tableView.dequeueReusableCell(withIdentifier: "PaletteTableViewCell") as! PaletteTableViewCell
|
||||||
|
cell.colorView?.backgroundColor = palette.color
|
||||||
|
cell.label?.text = palette.colorText
|
||||||
|
return cell
|
||||||
|
}
|
||||||
|
)
|
||||||
|
ColorsDemo.palettes.addObserver(self) { [weak self] (liveList, snapshot) in
|
||||||
|
|
||||||
|
guard let self = self else {
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.dataSource?.apply(snapshot, animatingDifferences: true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
|
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
|
||||||
@@ -153,30 +161,6 @@ class ListObserverDemoViewController: UITableViewController, ListSectionObserver
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// MARK: UITableViewDataSource
|
|
||||||
|
|
||||||
override func numberOfSections(in tableView: UITableView) -> Int {
|
|
||||||
|
|
||||||
return ColorsDemo.palettes.numberOfSections()
|
|
||||||
}
|
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
|
||||||
|
|
||||||
return ColorsDemo.palettes.numberOfObjects(in: section)
|
|
||||||
}
|
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
|
||||||
|
|
||||||
let cell = tableView.dequeueReusableCell(withIdentifier: "PaletteTableViewCell") as! PaletteTableViewCell
|
|
||||||
|
|
||||||
let palette = ColorsDemo.palettes[indexPath]
|
|
||||||
cell.colorView?.backgroundColor = palette.color
|
|
||||||
cell.label?.text = palette.colorText
|
|
||||||
|
|
||||||
return cell
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: UITableViewDelegate
|
// MARK: UITableViewDelegate
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||||
@@ -185,7 +169,7 @@ class ListObserverDemoViewController: UITableViewController, ListSectionObserver
|
|||||||
|
|
||||||
self.performSegue(
|
self.performSegue(
|
||||||
withIdentifier: "ObjectObserverDemoViewController",
|
withIdentifier: "ObjectObserverDemoViewController",
|
||||||
sender: ColorsDemo.palettes[indexPath]
|
sender: ColorsDemo.palettes[indexPath: indexPath]?.object
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,11 +178,11 @@ class ListObserverDemoViewController: UITableViewController, ListSectionObserver
|
|||||||
switch editingStyle {
|
switch editingStyle {
|
||||||
|
|
||||||
case .delete:
|
case .delete:
|
||||||
let palette = ColorsDemo.palettes[indexPath]
|
let palette = ColorsDemo.palettes[indexPath: indexPath]
|
||||||
ColorsDemo.stack.perform(
|
ColorsDemo.stack.perform(
|
||||||
asynchronous: { (transaction) in
|
asynchronous: { (transaction) in
|
||||||
|
|
||||||
transaction.delete(palette)
|
transaction.delete(palette?.object)
|
||||||
},
|
},
|
||||||
completion: { _ in }
|
completion: { _ in }
|
||||||
)
|
)
|
||||||
@@ -208,81 +192,16 @@ class ListObserverDemoViewController: UITableViewController, ListSectionObserver
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
|
||||||
|
|
||||||
return ColorsDemo.palettes.sectionInfo(at: section).name
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: ListObserver
|
|
||||||
|
|
||||||
func listMonitorWillChange(_ monitor: ListMonitor<Palette>) {
|
|
||||||
|
|
||||||
self.tableView.beginUpdates()
|
|
||||||
}
|
|
||||||
|
|
||||||
func listMonitorDidChange(_ monitor: ListMonitor<Palette>) {
|
|
||||||
|
|
||||||
self.tableView.endUpdates()
|
|
||||||
}
|
|
||||||
|
|
||||||
func listMonitorWillRefetch(_ monitor: ListMonitor<Palette>) {
|
|
||||||
|
|
||||||
self.setTable(enabled: false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func listMonitorDidRefetch(_ monitor: ListMonitor<Palette>) {
|
|
||||||
|
|
||||||
self.filterBarButton?.title = ColorsDemo.filter.rawValue
|
|
||||||
self.tableView.reloadData()
|
|
||||||
self.setTable(enabled: true)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: ListObjectObserver
|
|
||||||
|
|
||||||
func listMonitor(_ monitor: ListMonitor<Palette>, didInsertObject object: Palette, toIndexPath indexPath: IndexPath) {
|
|
||||||
|
|
||||||
self.tableView.insertRows(at: [indexPath], with: .automatic)
|
|
||||||
}
|
|
||||||
|
|
||||||
func listMonitor(_ monitor: ListMonitor<Palette>, didDeleteObject object: Palette, fromIndexPath indexPath: IndexPath) {
|
|
||||||
|
|
||||||
self.tableView.deleteRows(at: [indexPath], with: .automatic)
|
|
||||||
}
|
|
||||||
|
|
||||||
func listMonitor(_ monitor: ListMonitor<Palette>, didUpdateObject object: Palette, atIndexPath indexPath: IndexPath) {
|
|
||||||
|
|
||||||
if let cell = self.tableView.cellForRow(at: indexPath) as? PaletteTableViewCell {
|
|
||||||
|
|
||||||
let palette = ColorsDemo.palettes[indexPath]
|
|
||||||
cell.colorView?.backgroundColor = palette.color
|
|
||||||
cell.label?.text = palette.colorText
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func listMonitor(_ monitor: ListMonitor<Palette>, didMoveObject object: Palette, fromIndexPath: IndexPath, toIndexPath: IndexPath) {
|
|
||||||
|
|
||||||
self.tableView.moveRow(at: fromIndexPath, to: toIndexPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: ListSectionObserver
|
|
||||||
|
|
||||||
func listMonitor(_ monitor: ListMonitor<Palette>, didInsertSection sectionInfo: NSFetchedResultsSectionInfo, toSectionIndex sectionIndex: Int) {
|
|
||||||
|
|
||||||
self.tableView.insertSections(IndexSet(integer: sectionIndex), with: .automatic)
|
|
||||||
}
|
|
||||||
|
|
||||||
func listMonitor(_ monitor: ListMonitor<Palette>, didDeleteSection sectionInfo: NSFetchedResultsSectionInfo, fromSectionIndex sectionIndex: Int) {
|
|
||||||
|
|
||||||
self.tableView.deleteSections(IndexSet(integer: sectionIndex), with: .automatic)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: Private
|
// MARK: Private
|
||||||
|
|
||||||
private var filterBarButton: UIBarButtonItem?
|
private var filterBarButton: UIBarButtonItem?
|
||||||
|
private var dataSource: DiffableDataSource.TableView<Palette>?
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
|
||||||
|
ColorsDemo.palettes.removeObserver(self)
|
||||||
|
}
|
||||||
|
|
||||||
@IBAction private dynamic func resetBarButtonItemTouched(_ sender: AnyObject?) {
|
@IBAction private dynamic func resetBarButtonItemTouched(_ sender: AnyObject?) {
|
||||||
|
|
||||||
@@ -313,8 +232,6 @@ class ListObserverDemoViewController: UITableViewController, ListSectionObserver
|
|||||||
}
|
}
|
||||||
|
|
||||||
@IBAction private dynamic func shuffleBarButtonItemTouched(_ sender: AnyObject?) {
|
@IBAction private dynamic func shuffleBarButtonItemTouched(_ sender: AnyObject?) {
|
||||||
|
|
||||||
self.setTable(enabled: false)
|
|
||||||
ColorsDemo.stack.perform(
|
ColorsDemo.stack.perform(
|
||||||
asynchronous: { (transaction) in
|
asynchronous: { (transaction) in
|
||||||
|
|
||||||
@@ -324,28 +241,7 @@ class ListObserverDemoViewController: UITableViewController, ListSectionObserver
|
|||||||
palette.colorName .= nil
|
palette.colorName .= nil
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
completion: { _ in
|
completion: { _ in }
|
||||||
|
|
||||||
self.setTable(enabled: true)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func setTable(enabled: Bool) {
|
|
||||||
|
|
||||||
tableView.isUserInteractionEnabled = enabled
|
|
||||||
UIView.animate(
|
|
||||||
withDuration: 0.2,
|
|
||||||
delay: 0,
|
|
||||||
options: .beginFromCurrentState,
|
|
||||||
animations: { () -> Void in
|
|
||||||
|
|
||||||
if let tableView = self.tableView {
|
|
||||||
|
|
||||||
tableView.alpha = enabled ? 1.0 : 0.5
|
|
||||||
}
|
|
||||||
},
|
|
||||||
completion: nil
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,4 +27,20 @@ class ObserversViewController: UIViewController {
|
|||||||
alert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: nil))
|
alert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: nil))
|
||||||
self.present(alert, animated: true, completion: nil)
|
self.present(alert, animated: true, completion: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: Private
|
||||||
|
|
||||||
|
@IBOutlet private dynamic weak var topContainerView: UIView?
|
||||||
|
@IBOutlet private dynamic weak var bottomContainerView: UIView?
|
||||||
|
|
||||||
|
@IBAction private dynamic func toggleTopContainerView() {
|
||||||
|
|
||||||
|
self.topContainerView?.isHidden.toggle()
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction private dynamic func toggleBottomContainerView() {
|
||||||
|
|
||||||
|
self.bottomContainerView?.isHidden.toggle()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ import CoreData
|
|||||||
|
|
||||||
// MARK: - DataStack
|
// MARK: - DataStack
|
||||||
|
|
||||||
@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *)
|
|
||||||
extension DataStack {
|
extension DataStack {
|
||||||
|
|
||||||
public func liveList<D>(_ from: From<D>, _ fetchClauses: FetchClause...) -> LiveList<D> {
|
public func liveList<D>(_ from: From<D>, _ fetchClauses: FetchClause...) -> LiveList<D> {
|
||||||
|
|||||||
@@ -29,79 +29,324 @@ import UIKit
|
|||||||
import CoreData
|
import CoreData
|
||||||
|
|
||||||
|
|
||||||
//// MARK: - DiffableDataSource
|
// MARK: - DiffableDataSource
|
||||||
//
|
|
||||||
//extension DiffableDataSource {
|
extension DiffableDataSource {
|
||||||
//
|
|
||||||
// // MARK: - TableView
|
// MARK: - TableView
|
||||||
//
|
|
||||||
// public open class TableView<D: DynamicObject>: NSObject, UITableViewDataSource {
|
open class TableView<O: DynamicObject>: NSObject, UITableViewDataSource {
|
||||||
//
|
|
||||||
// // MARK: Public
|
// MARK: Open
|
||||||
//
|
|
||||||
// public typealias ObjectType = D
|
@nonobjc
|
||||||
//
|
open var defaultRowAnimation: UITableView.RowAnimation = .automatic
|
||||||
// public var defaultRowAnimation: UITableView.RowAnimation = .automatic
|
|
||||||
//
|
|
||||||
// public init(tableView: UITableView, cellProvider: @escaping (UITableView, IndexPath, ObjectType) -> UITableViewCell?) {
|
// MARK: Public
|
||||||
//
|
|
||||||
// self.tableView = tableView
|
public typealias ObjectType = O
|
||||||
// self.cellProvider = cellProvider
|
|
||||||
//
|
@nonobjc
|
||||||
|
public init(tableView: UITableView, dataStack: DataStack, cellProvider: @escaping (UITableView, IndexPath, ObjectType) -> UITableViewCell?) {
|
||||||
|
|
||||||
|
self.tableView = tableView
|
||||||
|
self.cellProvider = cellProvider
|
||||||
|
self.dataStack = dataStack
|
||||||
|
|
||||||
|
super.init()
|
||||||
|
|
||||||
// if #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) {
|
// if #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) {
|
||||||
//
|
//
|
||||||
// self.rawDataSource = UITableViewDiffableDataSource<String, D.ObjectID>(
|
// self.rawDataSource = UITableViewDiffableDataSource<String, O.ObjectID>(
|
||||||
// tableView: tableView,
|
// tableView: tableView,
|
||||||
// cellProvider: { (tableView, indexPath, managedObjectID) -> UITableViewCell? in
|
// cellProvider: { [weak self] (tableView, indexPath, objectID) -> UITableViewCell? in
|
||||||
//
|
//
|
||||||
// cellProvider(
|
// guard let self = self else {
|
||||||
|
//
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
// guard let object = self.dataStack.fetchExisting(objectID) as O? else {
|
||||||
|
//
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
// return self.cellProvider(tableView, indexPath, object)
|
||||||
// }
|
// }
|
||||||
// )
|
// )
|
||||||
// }
|
// }
|
||||||
// else {
|
// else {
|
||||||
//
|
|
||||||
// self.rawDataSource = nil
|
self.rawDataSource = Internals.DiffableDataUIDispatcher<O>(dataStack: dataStack)
|
||||||
// }
|
// }
|
||||||
//
|
|
||||||
// super.init()
|
tableView.dataSource = self
|
||||||
//
|
}
|
||||||
// tableView.dataSource = self
|
|
||||||
// }
|
public func apply(_ snapshot: ListSnapshot<ObjectType>, animatingDifferences: Bool = true) {
|
||||||
//
|
|
||||||
// public func apply(_ snapshot: ListSnapshot<ObjectType>, animatingDifferences: Bool = true) {
|
let diffableSnapshot = snapshot.diffableSnapshot
|
||||||
//
|
|
||||||
// let dataSource = UITableViewDiffableDataSource<String, D>.
|
|
||||||
// if #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) {
|
// if #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) {
|
||||||
//
|
//
|
||||||
// self.rawDataSource! as! UITableViewDiffableDataSource<String, D>
|
// self.modernDataSource.apply(
|
||||||
//
|
// diffableSnapshot as! NSDiffableDataSourceSnapshot<String, NSManagedObjectID>,
|
||||||
|
// animatingDifferences: animatingDifferences,
|
||||||
|
// completion: nil
|
||||||
|
// )
|
||||||
// }
|
// }
|
||||||
// else {
|
// else {
|
||||||
//
|
|
||||||
|
self.legacyDataSource.apply(
|
||||||
|
diffableSnapshot as! Internals.DiffableDataSourceSnapshot,
|
||||||
|
view: self.tableView,
|
||||||
|
animatingDifferences: animatingDifferences,
|
||||||
|
performUpdates: { tableView, changeset, setSections in
|
||||||
|
|
||||||
|
tableView.reload(
|
||||||
|
using: changeset,
|
||||||
|
with: self.defaultRowAnimation,
|
||||||
|
setData: setSections
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
// }
|
// }
|
||||||
// core.apply(
|
}
|
||||||
// snapshot,
|
|
||||||
// view: tableView,
|
public func itemIdentifier(for indexPath: IndexPath) -> O.ObjectID? {
|
||||||
// animatingDifferences: animatingDifferences,
|
|
||||||
// performUpdates: { tableView, changeset, setSections in
|
// if #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) {
|
||||||
// tableView.reload(using: changeset, with: self.defaultRowAnimation, setData: setSections)
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
//
|
//
|
||||||
|
// return self.modernDataSource.itemIdentifier(for: indexPath)
|
||||||
|
// }
|
||||||
|
// else {
|
||||||
|
|
||||||
|
return self.legacyDataSource.itemIdentifier(for: indexPath)
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
public func indexPath(for itemIdentifier: O.ObjectID) -> IndexPath? {
|
||||||
|
|
||||||
|
// if #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) {
|
||||||
//
|
//
|
||||||
// // MARK: Private
|
// return self.modernDataSource.indexPath(for: itemIdentifier)
|
||||||
|
// }
|
||||||
|
// else {
|
||||||
|
|
||||||
|
return self.legacyDataSource.indexPath(for: itemIdentifier)
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - UITableViewDataSource
|
||||||
|
|
||||||
|
@objc
|
||||||
|
public dynamic func numberOfSections(in tableView: UITableView) -> Int {
|
||||||
|
|
||||||
|
// if #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) {
|
||||||
//
|
//
|
||||||
// private weak var tableView: UITableView?
|
// return self.modernDataSource.numberOfSections(in: tableView)
|
||||||
// private let cellProvider: (UITableView, IndexPath, ObjectType) -> UITableViewCell?
|
// }
|
||||||
// private let rawDataSource: Any?
|
// else {
|
||||||
|
|
||||||
|
return self.legacyDataSource.numberOfSections()
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc
|
||||||
|
public dynamic func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||||
|
|
||||||
|
// if #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) {
|
||||||
//
|
//
|
||||||
|
// return self.modernDataSource.tableView(tableView, numberOfRowsInSection: section)
|
||||||
|
// }
|
||||||
|
// else {
|
||||||
|
|
||||||
|
return self.legacyDataSource.numberOfItems(inSection: section)
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc
|
||||||
|
open dynamic func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||||
|
|
||||||
|
// if #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) {
|
||||||
|
//
|
||||||
|
// return self.modernDataSource.snapshot().sectionIdentifiers[section]
|
||||||
|
// }
|
||||||
|
// else {
|
||||||
|
|
||||||
|
return self.legacyDataSource.sectionIdentifier(inSection: section)
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc
|
||||||
|
open dynamic func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
|
||||||
|
|
||||||
|
// if #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) {
|
||||||
|
//
|
||||||
|
// return self.modernDataSource.tableView(tableView, titleForFooterInSection: section)
|
||||||
|
// }
|
||||||
|
// else {
|
||||||
|
|
||||||
|
return nil
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc
|
||||||
|
open dynamic func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||||
|
|
||||||
|
// if #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) {
|
||||||
|
//
|
||||||
|
// return self.modernDataSource.tableView(tableView, cellForRowAt: indexPath)
|
||||||
|
// }
|
||||||
|
// else {
|
||||||
|
|
||||||
|
guard let objectID = self.legacyDataSource.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
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: Private
|
||||||
|
|
||||||
|
private weak var tableView: UITableView?
|
||||||
|
|
||||||
|
private let dataStack: DataStack
|
||||||
|
private let cellProvider: (UITableView, IndexPath, ObjectType) -> UITableViewCell?
|
||||||
|
private var rawDataSource: Any!
|
||||||
|
|
||||||
// @available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *)
|
// @available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *)
|
||||||
// private var diffableDataSource: UITableViewDiffableDataSource<String, D.ObjectID> {
|
// private var modernDataSource: UITableViewDiffableDataSource<String, O.ObjectID> {
|
||||||
//
|
//
|
||||||
// return self.rawDataSource! as! UITableViewDiffableDataSource<String, D.ObjectID>
|
// return self.rawDataSource as! UITableViewDiffableDataSource<String, O.ObjectID>
|
||||||
// }
|
// }
|
||||||
// }
|
|
||||||
//}
|
private var legacyDataSource: Internals.DiffableDataUIDispatcher<O> {
|
||||||
|
|
||||||
|
return self.rawDataSource as! Internals.DiffableDataUIDispatcher<O>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - UITableView
|
||||||
|
|
||||||
|
extension UITableView {
|
||||||
|
|
||||||
|
// MARK: FilePrivate
|
||||||
|
|
||||||
|
@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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@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
|
#endif
|
||||||
|
|||||||
62
Sources/Differentiable.swift
Normal file
62
Sources/Differentiable.swift
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
//
|
||||||
|
// Differentiable.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: - Differentiable
|
||||||
|
|
||||||
|
@usableFromInline
|
||||||
|
internal protocol Differentiable {
|
||||||
|
|
||||||
|
associatedtype DifferenceIdentifier: Hashable
|
||||||
|
|
||||||
|
var differenceIdentifier: DifferenceIdentifier { get }
|
||||||
|
|
||||||
|
func isContentEqual(to source: Self) -> Bool
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Differentiable where Self: AnyObject {
|
||||||
|
|
||||||
|
// MARK: Internal
|
||||||
|
|
||||||
|
internal var differenceIdentifier: ObjectIdentifier {
|
||||||
|
|
||||||
|
return .init(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - DifferentiableSection
|
||||||
|
|
||||||
|
@usableFromInline
|
||||||
|
internal protocol DifferentiableSection: Differentiable {
|
||||||
|
|
||||||
|
associatedtype Collection: Swift.Collection where Collection.Element: Differentiable
|
||||||
|
|
||||||
|
var elements: Collection { get }
|
||||||
|
|
||||||
|
init<S: Sequence>(source: Self, elements: S) where S.Element == Collection.Element
|
||||||
|
}
|
||||||
54
Sources/Internals.Closure.swift
Normal file
54
Sources/Internals.Closure.swift
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
//
|
||||||
|
// Internals.Closure.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: - Internals
|
||||||
|
|
||||||
|
extension Internals {
|
||||||
|
|
||||||
|
// MARK: - Closure
|
||||||
|
|
||||||
|
internal final class Closure<T, U> {
|
||||||
|
|
||||||
|
// MARK: FilePrivate
|
||||||
|
|
||||||
|
internal init(_ closure: @escaping (T) -> U) {
|
||||||
|
|
||||||
|
self.closure = closure
|
||||||
|
}
|
||||||
|
|
||||||
|
internal func invoke(with argument: T) -> U {
|
||||||
|
|
||||||
|
return self.closure(argument)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: Private
|
||||||
|
|
||||||
|
private let closure: (T) -> U
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -52,6 +52,18 @@ extension Internals {
|
|||||||
self.structure = .init(sections: sections)
|
self.structure = .init(sections: sections)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var sections: [Section] {
|
||||||
|
|
||||||
|
get {
|
||||||
|
|
||||||
|
return self.structure.sections
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
|
||||||
|
self.structure.sections = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// MARK: DiffableDataSourceSnapshotProtocol
|
// MARK: DiffableDataSourceSnapshotProtocol
|
||||||
|
|
||||||
@@ -186,48 +198,66 @@ extension Internals {
|
|||||||
private var structure: BackingStructure
|
private var structure: BackingStructure
|
||||||
|
|
||||||
|
|
||||||
// MARK: - ItemStateID
|
// MARK: - Section
|
||||||
|
|
||||||
internal struct ItemStateID: Identifiable, Equatable {
|
internal struct Section: DifferentiableSection, Equatable {
|
||||||
|
|
||||||
let stateTag: UUID
|
var isReloaded: Bool
|
||||||
|
|
||||||
init(id: NSManagedObjectID, stateTag: UUID) {
|
init(differenceIdentifier: String, items: [Item] = [], isReloaded: Bool = false) {
|
||||||
|
|
||||||
self.id = id
|
self.differenceIdentifier = differenceIdentifier
|
||||||
self.stateTag = stateTag
|
self.elements = items
|
||||||
|
self.isReloaded = isReloaded
|
||||||
}
|
}
|
||||||
|
|
||||||
func isContentEqual(to source: ItemStateID) -> Bool {
|
// MARK: Differentiable
|
||||||
|
|
||||||
return self.id == source.id && self.stateTag == source.stateTag
|
let differenceIdentifier: String
|
||||||
|
|
||||||
|
func isContentEqual(to source: Section) -> Bool {
|
||||||
|
|
||||||
|
return !self.isReloaded
|
||||||
|
&& self.differenceIdentifier == source.differenceIdentifier
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Identifiable
|
|
||||||
|
|
||||||
let id: NSManagedObjectID
|
// MARK: DifferentiableSection
|
||||||
|
|
||||||
|
var elements: [Item] = []
|
||||||
|
|
||||||
|
init<S: Sequence>(source: Section, elements: S) where S.Element == Item {
|
||||||
|
|
||||||
|
self.init(
|
||||||
|
differenceIdentifier: source.differenceIdentifier,
|
||||||
|
items: Array(elements),
|
||||||
|
isReloaded: source.isReloaded
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// MARK: - SectionStateID
|
// MARK: - Item
|
||||||
|
|
||||||
internal struct SectionStateID: Identifiable, Equatable {
|
internal struct Item: Differentiable, Equatable {
|
||||||
|
|
||||||
let stateTag: UUID
|
var isReloaded: Bool
|
||||||
|
|
||||||
init(id: String, stateTag: UUID) {
|
init(differenceIdentifier: NSManagedObjectID, isReloaded: Bool = false) {
|
||||||
self.id = id
|
|
||||||
self.stateTag = stateTag
|
self.differenceIdentifier = differenceIdentifier
|
||||||
|
self.isReloaded = isReloaded
|
||||||
}
|
}
|
||||||
|
|
||||||
func isContentEqual(to source: SectionStateID) -> Bool {
|
// MARK: Differentiable
|
||||||
|
|
||||||
return self.id == source.id && self.stateTag == source.stateTag
|
let differenceIdentifier: NSManagedObjectID
|
||||||
|
|
||||||
|
func isContentEqual(to source: Item) -> Bool {
|
||||||
|
|
||||||
|
return !self.isReloaded
|
||||||
|
&& self.differenceIdentifier == source.differenceIdentifier
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Identifiable
|
|
||||||
|
|
||||||
let id: String
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -249,22 +279,22 @@ extension Internals {
|
|||||||
self.sections = sections.map {
|
self.sections = sections.map {
|
||||||
|
|
||||||
Section(
|
Section(
|
||||||
id: $0.name,
|
differenceIdentifier: $0.name,
|
||||||
items: $0.objects?
|
items: $0.objects?
|
||||||
.compactMap({ ($0 as? NSManagedObject)?.objectID })
|
.compactMap({ ($0 as? NSManagedObject)?.objectID })
|
||||||
.map({ Item(id: $0) }) ?? []
|
.map({ Item(differenceIdentifier: $0) }) ?? []
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var allSectionIDs: [String] {
|
var allSectionIDs: [String] {
|
||||||
|
|
||||||
return self.sections.map({ $0.id })
|
return self.sections.map({ $0.differenceIdentifier })
|
||||||
}
|
}
|
||||||
|
|
||||||
var allItemIDs: [NSManagedObjectID] {
|
var allItemIDs: [NSManagedObjectID] {
|
||||||
|
|
||||||
return self.sections.lazy.flatMap({ $0.elements }).map({ $0.id })
|
return self.sections.lazy.flatMap({ $0.elements }).map({ $0.differenceIdentifier })
|
||||||
}
|
}
|
||||||
|
|
||||||
func items(in sectionID: String) -> [NSManagedObjectID] {
|
func items(in sectionID: String) -> [NSManagedObjectID] {
|
||||||
@@ -273,12 +303,12 @@ extension Internals {
|
|||||||
|
|
||||||
Internals.abort("Section \"\(sectionID)\" does not exist")
|
Internals.abort("Section \"\(sectionID)\" does not exist")
|
||||||
}
|
}
|
||||||
return self.sections[sectionIndex].elements.map({ $0.id })
|
return self.sections[sectionIndex].elements.map({ $0.differenceIdentifier })
|
||||||
}
|
}
|
||||||
|
|
||||||
func section(containing itemID: NSManagedObjectID) -> String? {
|
func section(containing itemID: NSManagedObjectID) -> String? {
|
||||||
|
|
||||||
return self.itemPositionMap()[itemID]?.section.id
|
return self.itemPositionMap()[itemID]?.section.differenceIdentifier
|
||||||
}
|
}
|
||||||
|
|
||||||
mutating func append(itemIDs: [NSManagedObjectID], to sectionID: String?) {
|
mutating func append(itemIDs: [NSManagedObjectID], to sectionID: String?) {
|
||||||
@@ -301,7 +331,7 @@ extension Internals {
|
|||||||
}
|
}
|
||||||
index = section.index(before: section.endIndex)
|
index = section.index(before: section.endIndex)
|
||||||
}
|
}
|
||||||
let items = itemIDs.lazy.map({ Item(id: $0) })
|
let items = itemIDs.lazy.map({ Item(differenceIdentifier: $0) })
|
||||||
self.sections[index].elements.append(contentsOf: items)
|
self.sections[index].elements.append(contentsOf: items)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -311,7 +341,7 @@ extension Internals {
|
|||||||
|
|
||||||
Internals.abort("Item \(beforeItemID) does not exist")
|
Internals.abort("Item \(beforeItemID) does not exist")
|
||||||
}
|
}
|
||||||
let items = itemIDs.lazy.map({ Item(id: $0) })
|
let items = itemIDs.lazy.map({ Item(differenceIdentifier: $0) })
|
||||||
self.sections[itemPosition.sectionIndex].elements
|
self.sections[itemPosition.sectionIndex].elements
|
||||||
.insert(contentsOf: items, at: itemPosition.itemRelativeIndex)
|
.insert(contentsOf: items, at: itemPosition.itemRelativeIndex)
|
||||||
}
|
}
|
||||||
@@ -324,7 +354,7 @@ extension Internals {
|
|||||||
}
|
}
|
||||||
let itemIndex = self.sections[itemPosition.sectionIndex].elements
|
let itemIndex = self.sections[itemPosition.sectionIndex].elements
|
||||||
.index(after: itemPosition.itemRelativeIndex)
|
.index(after: itemPosition.itemRelativeIndex)
|
||||||
let items = itemIDs.lazy.map({ Item(id: $0) })
|
let items = itemIDs.lazy.map({ Item(differenceIdentifier: $0) })
|
||||||
self.sections[itemPosition.sectionIndex].elements
|
self.sections[itemPosition.sectionIndex].elements
|
||||||
.insert(contentsOf: items, at: itemIndex)
|
.insert(contentsOf: items, at: itemIndex)
|
||||||
}
|
}
|
||||||
@@ -406,7 +436,7 @@ extension Internals {
|
|||||||
|
|
||||||
mutating func append(sectionIDs: [String]) {
|
mutating func append(sectionIDs: [String]) {
|
||||||
|
|
||||||
let newSections = sectionIDs.lazy.map({ Section(id: $0) })
|
let newSections = sectionIDs.lazy.map({ Section(differenceIdentifier: $0) })
|
||||||
self.sections.append(contentsOf: newSections)
|
self.sections.append(contentsOf: newSections)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -416,7 +446,7 @@ extension Internals {
|
|||||||
|
|
||||||
Internals.abort("Section \"\(beforeSectionID)\" does not exist")
|
Internals.abort("Section \"\(beforeSectionID)\" does not exist")
|
||||||
}
|
}
|
||||||
let newSections = sectionIDs.lazy.map({ Section(id: $0) })
|
let newSections = sectionIDs.lazy.map({ Section(differenceIdentifier: $0) })
|
||||||
self.sections.insert(contentsOf: newSections, at: sectionIndex)
|
self.sections.insert(contentsOf: newSections, at: sectionIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -427,7 +457,7 @@ extension Internals {
|
|||||||
Internals.abort("Section \"\(afterSectionID)\" does not exist")
|
Internals.abort("Section \"\(afterSectionID)\" does not exist")
|
||||||
}
|
}
|
||||||
let sectionIndex = self.sections.index(after: beforeIndex)
|
let sectionIndex = self.sections.index(after: beforeIndex)
|
||||||
let newSections = sectionIDs.lazy.map({ Section(id: $0) })
|
let newSections = sectionIDs.lazy.map({ Section(differenceIdentifier: $0) })
|
||||||
self.sections.insert(contentsOf: newSections, at: sectionIndex)
|
self.sections.insert(contentsOf: newSections, at: sectionIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -481,11 +511,9 @@ extension Internals {
|
|||||||
|
|
||||||
// MARK: Private
|
// MARK: Private
|
||||||
|
|
||||||
private static let zeroUUID: UUID = .init(uuid: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0))
|
|
||||||
|
|
||||||
private func sectionIndex(of sectionID: String) -> Array<Section>.Index? {
|
private func sectionIndex(of sectionID: String) -> Array<Section>.Index? {
|
||||||
|
|
||||||
return self.sections.firstIndex(where: { $0.id == sectionID })
|
return self.sections.firstIndex(where: { $0.differenceIdentifier == sectionID })
|
||||||
}
|
}
|
||||||
|
|
||||||
@discardableResult
|
@discardableResult
|
||||||
@@ -515,7 +543,7 @@ extension Internals {
|
|||||||
|
|
||||||
for (itemRelativeIndex, item) in section.element.elements.enumerated() {
|
for (itemRelativeIndex, item) in section.element.elements.enumerated() {
|
||||||
|
|
||||||
result[item.id] = ItemPosition(
|
result[item.differenceIdentifier] = ItemPosition(
|
||||||
item: item,
|
item: item,
|
||||||
itemRelativeIndex: itemRelativeIndex,
|
itemRelativeIndex: itemRelativeIndex,
|
||||||
section: section.element,
|
section: section.element,
|
||||||
@@ -526,58 +554,6 @@ extension Internals {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// MARK: - Item
|
|
||||||
|
|
||||||
fileprivate struct Item: Identifiable, Equatable {
|
|
||||||
|
|
||||||
var isReloaded: Bool
|
|
||||||
|
|
||||||
init(id: NSManagedObjectID, isReloaded: Bool = false) {
|
|
||||||
|
|
||||||
self.id = id
|
|
||||||
self.isReloaded = isReloaded
|
|
||||||
}
|
|
||||||
|
|
||||||
func isContentEqual(to source: Item) -> Bool {
|
|
||||||
|
|
||||||
return !self.isReloaded && self.id == source.id
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Identifiable
|
|
||||||
|
|
||||||
let id: NSManagedObjectID
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: - Section
|
|
||||||
|
|
||||||
fileprivate struct Section: Identifiable, Equatable {
|
|
||||||
|
|
||||||
var elements: [Item] = []
|
|
||||||
var isReloaded: Bool
|
|
||||||
|
|
||||||
init(id: String, items: [Item] = [], isReloaded: Bool = false) {
|
|
||||||
self.id = id
|
|
||||||
self.elements = items
|
|
||||||
self.isReloaded = isReloaded
|
|
||||||
}
|
|
||||||
|
|
||||||
init<S: Sequence>(source: Section, elements: S) where S.Element == Item {
|
|
||||||
|
|
||||||
self.init(id: source.id, items: Array(elements), isReloaded: source.isReloaded)
|
|
||||||
}
|
|
||||||
|
|
||||||
func isContentEqual(to source: Section) -> Bool {
|
|
||||||
|
|
||||||
return !self.isReloaded && self.id == source.id
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Identifiable
|
|
||||||
|
|
||||||
let id: String
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: - ItemPosition
|
// MARK: - ItemPosition
|
||||||
|
|
||||||
fileprivate struct ItemPosition {
|
fileprivate struct ItemPosition {
|
||||||
@@ -595,73 +571,7 @@ extension Internals {
|
|||||||
// MARK: - NSDiffableDataSourceSnapshot: DiffableDataSourceSnapshotProtocol
|
// MARK: - NSDiffableDataSourceSnapshot: DiffableDataSourceSnapshotProtocol
|
||||||
|
|
||||||
@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 15.0, *)
|
@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 15.0, *)
|
||||||
extension NSDiffableDataSourceSnapshot: DiffableDataSourceSnapshotProtocol where SectionIdentifierType == NSString, ItemIdentifierType == NSManagedObjectID {
|
extension NSDiffableDataSourceSnapshot: DiffableDataSourceSnapshotProtocol where SectionIdentifierType == String, ItemIdentifierType == NSManagedObjectID {}
|
||||||
|
|
||||||
internal var sectionIdentifiers: [String] {
|
|
||||||
|
|
||||||
return self.sectionIdentifiers as [NSString] as [String]
|
|
||||||
}
|
|
||||||
|
|
||||||
internal func numberOfItems(inSection identifier: String) -> Int {
|
|
||||||
|
|
||||||
return self.numberOfItems(inSection: identifier as NSString)
|
|
||||||
}
|
|
||||||
|
|
||||||
internal func itemIdentifiers(inSection identifier: String) -> [NSManagedObjectID] {
|
|
||||||
|
|
||||||
return self.itemIdentifiers(inSection: identifier as NSString)
|
|
||||||
}
|
|
||||||
|
|
||||||
internal func sectionIdentifier(containingItem identifier: NSManagedObjectID) -> String? {
|
|
||||||
|
|
||||||
return self.sectionIdentifier(containingItem: identifier) as NSString? as String?
|
|
||||||
}
|
|
||||||
|
|
||||||
internal func indexOfSection(_ identifier: String) -> Int? {
|
|
||||||
|
|
||||||
return self.indexOfSection(identifier as NSString)
|
|
||||||
}
|
|
||||||
|
|
||||||
internal mutating func appendItems(_ identifiers: [NSManagedObjectID], toSection sectionIdentifier: String?) {
|
|
||||||
|
|
||||||
self.appendItems(identifiers, toSection: sectionIdentifier as NSString?)
|
|
||||||
}
|
|
||||||
|
|
||||||
internal mutating func appendSections(_ identifiers: [String]) {
|
|
||||||
|
|
||||||
self.appendSections(identifiers as [NSString])
|
|
||||||
}
|
|
||||||
|
|
||||||
internal mutating func insertSections(_ identifiers: [String], beforeSection toIdentifier: String) {
|
|
||||||
|
|
||||||
self.insertSections(identifiers as [NSString], beforeSection: toIdentifier as NSString)
|
|
||||||
}
|
|
||||||
|
|
||||||
internal mutating func insertSections(_ identifiers: [String], afterSection toIdentifier: String) {
|
|
||||||
|
|
||||||
return self.insertSections(identifiers as [NSString], afterSection: toIdentifier as NSString)
|
|
||||||
}
|
|
||||||
|
|
||||||
internal mutating func deleteSections(_ identifiers: [String]) {
|
|
||||||
|
|
||||||
self.deleteSections(identifiers as [NSString])
|
|
||||||
}
|
|
||||||
|
|
||||||
internal mutating func moveSection(_ identifier: String, beforeSection toIdentifier: String) {
|
|
||||||
|
|
||||||
self.moveSection(identifier as NSString, beforeSection: toIdentifier as NSString)
|
|
||||||
}
|
|
||||||
|
|
||||||
internal mutating func moveSection(_ identifier: String, afterSection toIdentifier: String) {
|
|
||||||
|
|
||||||
self.moveSection(identifier as NSString, afterSection: toIdentifier as NSString)
|
|
||||||
}
|
|
||||||
|
|
||||||
internal mutating func reloadSections(_ identifiers: [String]) {
|
|
||||||
|
|
||||||
self.reloadSections(identifiers as [NSString])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
141
Sources/Internals.DiffableDataUIDispatcher.Changeset.swift
Normal file
141
Sources/Internals.DiffableDataUIDispatcher.Changeset.swift
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
//
|
||||||
|
// Internals.DiffableDataUIDispatcher.Changeset.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) || canImport(AppKit)
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - Internals.DiffableDataUIDispatcher
|
||||||
|
|
||||||
|
extension Internals.DiffableDataUIDispatcher {
|
||||||
|
|
||||||
|
// MARK: - ChangeSet
|
||||||
|
|
||||||
|
internal struct Changeset<C: Collection>: Equatable where C: Equatable {
|
||||||
|
|
||||||
|
var data: C
|
||||||
|
var sectionDeleted: [Int]
|
||||||
|
var sectionInserted: [Int]
|
||||||
|
var sectionUpdated: [Int]
|
||||||
|
var sectionMoved: [(source: Int, target: Int)]
|
||||||
|
|
||||||
|
var elementDeleted: [ElementPath]
|
||||||
|
var elementInserted: [ElementPath]
|
||||||
|
var elementUpdated: [ElementPath]
|
||||||
|
var elementMoved: [(source: ElementPath, target: ElementPath)]
|
||||||
|
|
||||||
|
@inlinable
|
||||||
|
init(
|
||||||
|
data: C,
|
||||||
|
sectionDeleted: [Int] = [],
|
||||||
|
sectionInserted: [Int] = [],
|
||||||
|
sectionUpdated: [Int] = [],
|
||||||
|
sectionMoved: [(source: Int, target: Int)] = [],
|
||||||
|
elementDeleted: [ElementPath] = [],
|
||||||
|
elementInserted: [ElementPath] = [],
|
||||||
|
elementUpdated: [ElementPath] = [],
|
||||||
|
elementMoved: [(source: ElementPath, target: ElementPath)] = []
|
||||||
|
) {
|
||||||
|
self.data = data
|
||||||
|
self.sectionDeleted = sectionDeleted
|
||||||
|
self.sectionInserted = sectionInserted
|
||||||
|
self.sectionUpdated = sectionUpdated
|
||||||
|
self.sectionMoved = sectionMoved
|
||||||
|
self.elementDeleted = elementDeleted
|
||||||
|
self.elementInserted = elementInserted
|
||||||
|
self.elementUpdated = elementUpdated
|
||||||
|
self.elementMoved = elementMoved
|
||||||
|
}
|
||||||
|
|
||||||
|
@inlinable
|
||||||
|
var sectionChangeCount: Int {
|
||||||
|
|
||||||
|
return self.sectionDeleted.count
|
||||||
|
+ self.sectionInserted.count
|
||||||
|
+ self.sectionUpdated.count
|
||||||
|
+ self.sectionMoved.count
|
||||||
|
}
|
||||||
|
|
||||||
|
@inlinable
|
||||||
|
var elementChangeCount: Int {
|
||||||
|
|
||||||
|
return self.elementDeleted.count
|
||||||
|
+ self.elementInserted.count
|
||||||
|
+ self.elementUpdated.count
|
||||||
|
+ self.elementMoved.count
|
||||||
|
}
|
||||||
|
|
||||||
|
@inlinable
|
||||||
|
var changeCount: Int {
|
||||||
|
|
||||||
|
return self.sectionChangeCount + self.elementChangeCount
|
||||||
|
}
|
||||||
|
|
||||||
|
@inlinable
|
||||||
|
var hasSectionChanges: Bool {
|
||||||
|
|
||||||
|
return self.sectionChangeCount > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
@inlinable
|
||||||
|
var hasElementChanges: Bool {
|
||||||
|
|
||||||
|
return self.elementChangeCount > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
@inlinable
|
||||||
|
var hasChanges: Bool {
|
||||||
|
|
||||||
|
return self.changeCount > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: Equatable
|
||||||
|
|
||||||
|
static func == (lhs: Changeset, rhs: Changeset) -> Bool {
|
||||||
|
return lhs.data == rhs.data
|
||||||
|
&& Set(lhs.sectionDeleted) == Set(rhs.sectionDeleted)
|
||||||
|
&& Set(lhs.sectionInserted) == Set(rhs.sectionInserted)
|
||||||
|
&& Set(lhs.sectionUpdated) == Set(rhs.sectionUpdated)
|
||||||
|
&& Set(lhs.sectionMoved.map(HashablePair.init)) == Set(rhs.sectionMoved.map(HashablePair.init))
|
||||||
|
&& Set(lhs.elementDeleted) == Set(rhs.elementDeleted)
|
||||||
|
&& Set(lhs.elementInserted) == Set(rhs.elementInserted)
|
||||||
|
&& Set(lhs.elementUpdated) == Set(rhs.elementUpdated)
|
||||||
|
&& Set(lhs.elementMoved.map(HashablePair.init)) == Set(rhs.elementMoved.map(HashablePair.init))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - HashablePair
|
||||||
|
|
||||||
|
private struct HashablePair<H: Hashable>: Hashable {
|
||||||
|
|
||||||
|
let first: H
|
||||||
|
let second: H
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
320
Sources/Internals.DiffableDataUIDispatcher.DiffResult.swift
Normal file
320
Sources/Internals.DiffableDataUIDispatcher.DiffResult.swift
Normal file
@@ -0,0 +1,320 @@
|
|||||||
|
//
|
||||||
|
// Internals.DiffableDataUIDispatcher.DiffResult.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) || canImport(AppKit)
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - Internals.DiffableDataUIDispatcher
|
||||||
|
|
||||||
|
extension Internals.DiffableDataUIDispatcher {
|
||||||
|
|
||||||
|
// MARK: - DiffResult
|
||||||
|
|
||||||
|
@usableFromInline
|
||||||
|
internal struct DiffResult<Index> {
|
||||||
|
|
||||||
|
@usableFromInline
|
||||||
|
internal let deleted: [Index]
|
||||||
|
@usableFromInline
|
||||||
|
internal let inserted: [Index]
|
||||||
|
@usableFromInline
|
||||||
|
internal let updated: [Index]
|
||||||
|
@usableFromInline
|
||||||
|
internal let moved: [(source: Index, target: Index)]
|
||||||
|
@usableFromInline
|
||||||
|
internal let sourceTraces: ContiguousArray<Trace<Int>>
|
||||||
|
@usableFromInline
|
||||||
|
internal let targetReferences: ContiguousArray<Int?>
|
||||||
|
|
||||||
|
@inlinable
|
||||||
|
@discardableResult
|
||||||
|
static func diff<E: Differentiable>(
|
||||||
|
source: ContiguousArray<E>,
|
||||||
|
target: ContiguousArray<E>,
|
||||||
|
useTargetIndexForUpdated: Bool,
|
||||||
|
mapIndex: (Int) -> Index,
|
||||||
|
updatedElementsPointer: UnsafeMutablePointer<ContiguousArray<E>>? = nil,
|
||||||
|
notDeletedElementsPointer: UnsafeMutablePointer<ContiguousArray<E>>? = nil
|
||||||
|
) -> DiffResult<Index> {
|
||||||
|
|
||||||
|
var deleted = [Index]()
|
||||||
|
var inserted = [Index]()
|
||||||
|
var updated = [Index]()
|
||||||
|
var moved = [(source: Index, target: Index)]()
|
||||||
|
|
||||||
|
var sourceTraces = ContiguousArray<Trace<Int>>()
|
||||||
|
var sourceIdentifiers = ContiguousArray<E.DifferenceIdentifier>()
|
||||||
|
var targetReferences = ContiguousArray<Int?>(repeating: nil, count: target.count)
|
||||||
|
|
||||||
|
sourceTraces.reserveCapacity(source.count)
|
||||||
|
sourceIdentifiers.reserveCapacity(source.count)
|
||||||
|
|
||||||
|
for sourceElement in source {
|
||||||
|
|
||||||
|
sourceTraces.append(Trace())
|
||||||
|
sourceIdentifiers.append(sourceElement.differenceIdentifier)
|
||||||
|
}
|
||||||
|
sourceIdentifiers.withUnsafeBufferPointer { bufferPointer in
|
||||||
|
|
||||||
|
var sourceOccurrencesTable = [TableKey<E.DifferenceIdentifier>: Occurrence](minimumCapacity: source.count)
|
||||||
|
|
||||||
|
for sourceIndex in sourceIdentifiers.indices {
|
||||||
|
|
||||||
|
let pointer = bufferPointer.baseAddress!.advanced(by: sourceIndex)
|
||||||
|
let key = TableKey(pointer: pointer)
|
||||||
|
|
||||||
|
switch sourceOccurrencesTable[key] {
|
||||||
|
case .none:
|
||||||
|
sourceOccurrencesTable[key] = .unique(index: sourceIndex)
|
||||||
|
|
||||||
|
case .unique(let otherIndex)?:
|
||||||
|
let reference = IndicesReference([otherIndex, sourceIndex])
|
||||||
|
sourceOccurrencesTable[key] = .duplicate(reference: reference)
|
||||||
|
|
||||||
|
case .duplicate(let reference)?:
|
||||||
|
reference.push(sourceIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for targetIndex in target.indices {
|
||||||
|
|
||||||
|
var targetIdentifier = target[targetIndex].differenceIdentifier
|
||||||
|
let key = TableKey(pointer: &targetIdentifier)
|
||||||
|
|
||||||
|
switch sourceOccurrencesTable[key] {
|
||||||
|
|
||||||
|
case .none:
|
||||||
|
break
|
||||||
|
|
||||||
|
case .unique(let sourceIndex)?:
|
||||||
|
if case .none = sourceTraces[sourceIndex].reference {
|
||||||
|
|
||||||
|
targetReferences[targetIndex] = sourceIndex
|
||||||
|
sourceTraces[sourceIndex].reference = targetIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
case .duplicate(let reference)?:
|
||||||
|
if let sourceIndex = reference.next() {
|
||||||
|
|
||||||
|
targetReferences[targetIndex] = sourceIndex
|
||||||
|
sourceTraces[sourceIndex].reference = targetIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var offsetByDelete = 0
|
||||||
|
var untrackedSourceIndex: Int? = 0
|
||||||
|
|
||||||
|
for sourceIndex in source.indices {
|
||||||
|
|
||||||
|
sourceTraces[sourceIndex].deleteOffset = offsetByDelete
|
||||||
|
|
||||||
|
if let targetIndex = sourceTraces[sourceIndex].reference {
|
||||||
|
|
||||||
|
let targetElement = target[targetIndex]
|
||||||
|
updatedElementsPointer?.pointee.append(targetElement)
|
||||||
|
notDeletedElementsPointer?.pointee.append(targetElement)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
let sourceElement = source[sourceIndex]
|
||||||
|
deleted.append(mapIndex(sourceIndex))
|
||||||
|
sourceTraces[sourceIndex].isTracked = true
|
||||||
|
offsetByDelete += 1
|
||||||
|
updatedElementsPointer?.pointee.append(sourceElement)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for targetIndex in target.indices {
|
||||||
|
|
||||||
|
untrackedSourceIndex = untrackedSourceIndex.flatMap { index in
|
||||||
|
|
||||||
|
sourceTraces.suffix(from: index).firstIndex { !$0.isTracked }
|
||||||
|
}
|
||||||
|
if let sourceIndex = targetReferences[targetIndex] {
|
||||||
|
|
||||||
|
sourceTraces[sourceIndex].isTracked = true
|
||||||
|
|
||||||
|
let sourceElement = source[sourceIndex]
|
||||||
|
let targetElement = target[targetIndex]
|
||||||
|
|
||||||
|
if !targetElement.isContentEqual(to: sourceElement) {
|
||||||
|
|
||||||
|
updated.append(mapIndex(useTargetIndexForUpdated ? targetIndex : sourceIndex))
|
||||||
|
}
|
||||||
|
if sourceIndex != untrackedSourceIndex {
|
||||||
|
|
||||||
|
let deleteOffset = sourceTraces[sourceIndex].deleteOffset
|
||||||
|
moved.append((source: mapIndex(sourceIndex - deleteOffset), target: mapIndex(targetIndex)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
inserted.append(mapIndex(targetIndex))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return DiffResult(
|
||||||
|
deleted: deleted,
|
||||||
|
inserted: inserted,
|
||||||
|
updated: updated,
|
||||||
|
moved: moved,
|
||||||
|
sourceTraces: sourceTraces,
|
||||||
|
targetReferences: targetReferences
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: Private
|
||||||
|
|
||||||
|
@inlinable
|
||||||
|
internal init(
|
||||||
|
deleted: [Index] = [],
|
||||||
|
inserted: [Index] = [],
|
||||||
|
updated: [Index] = [],
|
||||||
|
moved: [(source: Index, target: Index)] = [],
|
||||||
|
sourceTraces: ContiguousArray<Trace<Int>>,
|
||||||
|
targetReferences: ContiguousArray<Int?>
|
||||||
|
) {
|
||||||
|
|
||||||
|
self.deleted = deleted
|
||||||
|
self.inserted = inserted
|
||||||
|
self.updated = updated
|
||||||
|
self.moved = moved
|
||||||
|
self.sourceTraces = sourceTraces
|
||||||
|
self.targetReferences = targetReferences
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - Trace
|
||||||
|
|
||||||
|
@usableFromInline
|
||||||
|
internal struct Trace<Index> {
|
||||||
|
|
||||||
|
@usableFromInline
|
||||||
|
internal var reference: Index?
|
||||||
|
|
||||||
|
@usableFromInline
|
||||||
|
internal var deleteOffset = 0
|
||||||
|
|
||||||
|
@usableFromInline
|
||||||
|
internal var isTracked = false
|
||||||
|
|
||||||
|
@inlinable
|
||||||
|
init() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - Occurrence
|
||||||
|
|
||||||
|
@usableFromInline
|
||||||
|
internal enum Occurrence {
|
||||||
|
|
||||||
|
case unique(index: Int)
|
||||||
|
case duplicate(reference: IndicesReference)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - IndicesReference
|
||||||
|
|
||||||
|
@usableFromInline
|
||||||
|
internal final class IndicesReference {
|
||||||
|
|
||||||
|
@usableFromInline
|
||||||
|
internal var indices: ContiguousArray<Int>
|
||||||
|
|
||||||
|
@usableFromInline
|
||||||
|
internal var position = 0
|
||||||
|
|
||||||
|
@inlinable
|
||||||
|
internal init(_ indices: ContiguousArray<Int>) {
|
||||||
|
|
||||||
|
self.indices = indices
|
||||||
|
}
|
||||||
|
|
||||||
|
@inlinable
|
||||||
|
internal func push(_ index: Int) {
|
||||||
|
|
||||||
|
self.indices.append(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
@inlinable
|
||||||
|
internal func next() -> Int? {
|
||||||
|
|
||||||
|
guard self.position < self.indices.endIndex else {
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
defer {
|
||||||
|
|
||||||
|
self.position += 1
|
||||||
|
}
|
||||||
|
return self.indices[self.position]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - TableKey
|
||||||
|
|
||||||
|
@usableFromInline
|
||||||
|
internal struct TableKey<T: Hashable>: Hashable {
|
||||||
|
|
||||||
|
@usableFromInline
|
||||||
|
internal let pointeeHashValue: Int
|
||||||
|
|
||||||
|
@usableFromInline
|
||||||
|
internal let pointer: UnsafePointer<T>
|
||||||
|
|
||||||
|
@inlinable
|
||||||
|
internal init(pointer: UnsafePointer<T>) {
|
||||||
|
|
||||||
|
self.pointeeHashValue = pointer.pointee.hashValue
|
||||||
|
self.pointer = pointer
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: Equatable
|
||||||
|
|
||||||
|
@inlinable
|
||||||
|
internal static func == (lhs: TableKey, rhs: TableKey) -> Bool {
|
||||||
|
|
||||||
|
return lhs.pointeeHashValue == rhs.pointeeHashValue
|
||||||
|
&& (lhs.pointer.distance(to: rhs.pointer) == 0
|
||||||
|
|| lhs.pointer.pointee == rhs.pointer.pointee)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: Hashable
|
||||||
|
|
||||||
|
@inlinable
|
||||||
|
internal func hash(into hasher: inout Hasher) {
|
||||||
|
|
||||||
|
hasher.combine(pointeeHashValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
516
Sources/Internals.DiffableDataUIDispatcher.StagedChangeset.swift
Normal file
516
Sources/Internals.DiffableDataUIDispatcher.StagedChangeset.swift
Normal file
@@ -0,0 +1,516 @@
|
|||||||
|
//
|
||||||
|
// Internals.DiffableDataUIDispatcher.StagedChangeset.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) || canImport(AppKit)
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - Internals.DiffableDataUIDispatcher
|
||||||
|
|
||||||
|
extension Internals.DiffableDataUIDispatcher {
|
||||||
|
|
||||||
|
// MARK: - StagedChangeset
|
||||||
|
|
||||||
|
internal struct StagedChangeset<C: Collection>: ExpressibleByArrayLiteral, Equatable, RandomAccessCollection, RangeReplaceableCollection where C: Equatable {
|
||||||
|
|
||||||
|
@usableFromInline
|
||||||
|
var changesets: ContiguousArray<Changeset<C>>
|
||||||
|
|
||||||
|
@inlinable
|
||||||
|
init<S: Sequence>(_ changesets: S) where S.Element == Changeset<C> {
|
||||||
|
|
||||||
|
self.changesets = ContiguousArray(changesets)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: ExpressibleByArrayLiteral
|
||||||
|
|
||||||
|
@inlinable
|
||||||
|
init(arrayLiteral elements: Changeset<C>...) {
|
||||||
|
|
||||||
|
self.init(elements)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: Equatable
|
||||||
|
|
||||||
|
@inlinable
|
||||||
|
static func == (lhs: StagedChangeset, rhs: StagedChangeset) -> Bool {
|
||||||
|
|
||||||
|
return lhs.changesets == rhs.changesets
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: Sequence
|
||||||
|
|
||||||
|
typealias Element = Changeset<C>
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: RandomAccessCollection
|
||||||
|
|
||||||
|
@inlinable
|
||||||
|
var startIndex: Int {
|
||||||
|
|
||||||
|
return self.changesets.startIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
@inlinable
|
||||||
|
var endIndex: Int {
|
||||||
|
|
||||||
|
return self.changesets.endIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
@inlinable
|
||||||
|
func index(after i: Int) -> Int {
|
||||||
|
|
||||||
|
return self.changesets.index(after: i)
|
||||||
|
}
|
||||||
|
|
||||||
|
@inlinable
|
||||||
|
subscript(position: Int) -> Changeset<C> {
|
||||||
|
|
||||||
|
get { return self.changesets[position] }
|
||||||
|
set { self.changesets[position] = newValue }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: RangeReplaceableCollection
|
||||||
|
|
||||||
|
@inlinable
|
||||||
|
init() {
|
||||||
|
|
||||||
|
self.init([])
|
||||||
|
}
|
||||||
|
|
||||||
|
@inlinable
|
||||||
|
mutating func replaceSubrange<C2: Collection, R: RangeExpression>(_ subrange: R, with newElements: C2) where C2.Element == Changeset<C>, R.Bound == Int {
|
||||||
|
|
||||||
|
self.changesets.replaceSubrange(subrange, with: newElements)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - Internals.DiffableDataUIDispatcher.StagedChangeset where C: RangeReplaceableCollection, C.Element: Differentiable
|
||||||
|
|
||||||
|
extension Internals.DiffableDataUIDispatcher.StagedChangeset where C: RangeReplaceableCollection, C.Element: Differentiable {
|
||||||
|
|
||||||
|
@inlinable
|
||||||
|
internal init(source: C, target: C) {
|
||||||
|
|
||||||
|
self.init(source: source, target: target, section: 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
@inlinable
|
||||||
|
internal init(source: C, target: C, section: Int) {
|
||||||
|
|
||||||
|
typealias Changeset = Internals.DiffableDataUIDispatcher<O>.Changeset
|
||||||
|
typealias ElementPath = Internals.DiffableDataUIDispatcher<O>.ElementPath
|
||||||
|
typealias DiffResult = Internals.DiffableDataUIDispatcher<O>.DiffResult
|
||||||
|
|
||||||
|
let sourceElements = ContiguousArray(source)
|
||||||
|
let targetElements = ContiguousArray(target)
|
||||||
|
if sourceElements.isEmpty && targetElements.isEmpty {
|
||||||
|
|
||||||
|
self.init()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !sourceElements.isEmpty && targetElements.isEmpty {
|
||||||
|
|
||||||
|
self.init(
|
||||||
|
[
|
||||||
|
Changeset(
|
||||||
|
data: target,
|
||||||
|
elementDeleted: sourceElements.indices.map {
|
||||||
|
ElementPath(
|
||||||
|
element: $0,
|
||||||
|
section: section
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if sourceElements.isEmpty && !targetElements.isEmpty {
|
||||||
|
|
||||||
|
self.init(
|
||||||
|
[
|
||||||
|
Changeset(
|
||||||
|
data: target,
|
||||||
|
elementInserted: targetElements.indices.map {
|
||||||
|
ElementPath(
|
||||||
|
element: $0,
|
||||||
|
section: section
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var firstStageElements = ContiguousArray<C.Element>()
|
||||||
|
var secondStageElements = ContiguousArray<C.Element>()
|
||||||
|
let result = DiffResult.diff(
|
||||||
|
source: sourceElements,
|
||||||
|
target: targetElements,
|
||||||
|
useTargetIndexForUpdated: false,
|
||||||
|
mapIndex: { ElementPath(element: $0, section: section) },
|
||||||
|
updatedElementsPointer: &firstStageElements,
|
||||||
|
notDeletedElementsPointer: &secondStageElements
|
||||||
|
)
|
||||||
|
|
||||||
|
var changesets = ContiguousArray<Changeset<C>>()
|
||||||
|
if !result.updated.isEmpty {
|
||||||
|
|
||||||
|
changesets.append(
|
||||||
|
Changeset(
|
||||||
|
data: C(firstStageElements),
|
||||||
|
elementUpdated: result.updated
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if !result.deleted.isEmpty {
|
||||||
|
|
||||||
|
changesets.append(
|
||||||
|
Changeset(
|
||||||
|
data: C(secondStageElements),
|
||||||
|
elementDeleted: result.deleted
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if !result.inserted.isEmpty || !result.moved.isEmpty {
|
||||||
|
|
||||||
|
changesets.append(
|
||||||
|
Changeset(
|
||||||
|
data: target,
|
||||||
|
elementInserted: result.inserted,
|
||||||
|
elementMoved: result.moved
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if !changesets.isEmpty {
|
||||||
|
|
||||||
|
let index = changesets.index(before: changesets.endIndex)
|
||||||
|
changesets[index].data = target
|
||||||
|
}
|
||||||
|
self.init(changesets)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - Internals.DiffableDataUIDispatcher.StagedChangeset where C: RangeReplaceableCollection, C.Element: DifferentiableSection
|
||||||
|
|
||||||
|
extension Internals.DiffableDataUIDispatcher.StagedChangeset where C: RangeReplaceableCollection, C.Element: DifferentiableSection {
|
||||||
|
|
||||||
|
@inlinable
|
||||||
|
internal init(source: C, target: C) {
|
||||||
|
|
||||||
|
typealias Section = C.Element
|
||||||
|
typealias SectionIdentifier = C.Element.DifferenceIdentifier
|
||||||
|
typealias Element = C.Element.Collection.Element
|
||||||
|
typealias ElementIdentifier = C.Element.Collection.Element.DifferenceIdentifier
|
||||||
|
|
||||||
|
typealias Changeset = Internals.DiffableDataUIDispatcher<O>.Changeset
|
||||||
|
typealias ElementPath = Internals.DiffableDataUIDispatcher<O>.ElementPath
|
||||||
|
typealias DiffResult = Internals.DiffableDataUIDispatcher<O>.DiffResult
|
||||||
|
|
||||||
|
typealias Trace = Internals.DiffableDataUIDispatcher<O>.DiffResult<Section>.Trace
|
||||||
|
typealias TableKey = Internals.DiffableDataUIDispatcher<O>.DiffResult<Section>.TableKey
|
||||||
|
typealias Occurrence = Internals.DiffableDataUIDispatcher<O>.DiffResult<Section>.Occurrence
|
||||||
|
typealias IndicesReference = Internals.DiffableDataUIDispatcher<O>.DiffResult<Section>.IndicesReference
|
||||||
|
|
||||||
|
let sourceSections = ContiguousArray(source)
|
||||||
|
let targetSections = ContiguousArray(target)
|
||||||
|
|
||||||
|
let contiguousSourceSections = ContiguousArray(sourceSections.map { ContiguousArray($0.elements) })
|
||||||
|
let contiguousTargetSections = ContiguousArray(targetSections.map { ContiguousArray($0.elements) })
|
||||||
|
|
||||||
|
var firstStageSections = sourceSections
|
||||||
|
var secondStageSections = ContiguousArray<Section>()
|
||||||
|
var thirdStageSections = ContiguousArray<Section>()
|
||||||
|
var fourthStageSections = ContiguousArray<Section>()
|
||||||
|
|
||||||
|
var sourceElementTraces = contiguousSourceSections.map { section in
|
||||||
|
|
||||||
|
ContiguousArray(repeating: Trace<ElementPath>(), count: section.count)
|
||||||
|
}
|
||||||
|
var targetElementReferences = contiguousTargetSections.map { section in
|
||||||
|
|
||||||
|
ContiguousArray<ElementPath?>(repeating: nil, count: section.count)
|
||||||
|
}
|
||||||
|
|
||||||
|
let flattenSourceCount = contiguousSourceSections.reduce(into: 0) { $0 += $1.count }
|
||||||
|
var flattenSourceIdentifiers = ContiguousArray<ElementIdentifier>()
|
||||||
|
var flattenSourceElementPaths = ContiguousArray<ElementPath>()
|
||||||
|
|
||||||
|
thirdStageSections.reserveCapacity(contiguousTargetSections.count)
|
||||||
|
fourthStageSections.reserveCapacity(contiguousTargetSections.count)
|
||||||
|
|
||||||
|
flattenSourceIdentifiers.reserveCapacity(flattenSourceCount)
|
||||||
|
flattenSourceElementPaths.reserveCapacity(flattenSourceCount)
|
||||||
|
|
||||||
|
let sectionResult = DiffResult.diff(
|
||||||
|
source: sourceSections,
|
||||||
|
target: targetSections,
|
||||||
|
useTargetIndexForUpdated: true,
|
||||||
|
mapIndex: { $0 }
|
||||||
|
)
|
||||||
|
|
||||||
|
var elementDeleted = [ElementPath]()
|
||||||
|
var elementInserted = [ElementPath]()
|
||||||
|
var elementUpdated = [ElementPath]()
|
||||||
|
var elementMoved = [(source: ElementPath, target: ElementPath)]()
|
||||||
|
|
||||||
|
for sourceSectionIndex in contiguousSourceSections.indices {
|
||||||
|
|
||||||
|
for sourceElementIndex in contiguousSourceSections[sourceSectionIndex].indices {
|
||||||
|
|
||||||
|
let sourceElementPath = ElementPath(element: sourceElementIndex, section: sourceSectionIndex)
|
||||||
|
let sourceElement = contiguousSourceSections[sourceElementPath]
|
||||||
|
flattenSourceIdentifiers.append(sourceElement.differenceIdentifier)
|
||||||
|
flattenSourceElementPaths.append(sourceElementPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
flattenSourceIdentifiers.withUnsafeBufferPointer { bufferPointer in
|
||||||
|
|
||||||
|
var sourceOccurrencesTable = [TableKey<ElementIdentifier>: Occurrence](minimumCapacity: flattenSourceCount)
|
||||||
|
|
||||||
|
for flattenSourceIndex in flattenSourceIdentifiers.indices {
|
||||||
|
let pointer = bufferPointer.baseAddress!.advanced(by: flattenSourceIndex)
|
||||||
|
let key = TableKey(pointer: pointer)
|
||||||
|
|
||||||
|
switch sourceOccurrencesTable[key] {
|
||||||
|
|
||||||
|
case .none:
|
||||||
|
sourceOccurrencesTable[key] = .unique(index: flattenSourceIndex)
|
||||||
|
|
||||||
|
case .unique(let otherIndex)?:
|
||||||
|
let reference = IndicesReference([otherIndex, flattenSourceIndex])
|
||||||
|
sourceOccurrencesTable[key] = .duplicate(reference: reference)
|
||||||
|
|
||||||
|
case .duplicate(let reference)?:
|
||||||
|
reference.push(flattenSourceIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for targetSectionIndex in contiguousTargetSections.indices {
|
||||||
|
|
||||||
|
let targetElements = contiguousTargetSections[targetSectionIndex]
|
||||||
|
for targetElementIndex in targetElements.indices {
|
||||||
|
|
||||||
|
var targetIdentifier = targetElements[targetElementIndex].differenceIdentifier
|
||||||
|
let key = TableKey(pointer: &targetIdentifier)
|
||||||
|
|
||||||
|
switch sourceOccurrencesTable[key] {
|
||||||
|
|
||||||
|
case .none:
|
||||||
|
break
|
||||||
|
|
||||||
|
case .unique(let flattenSourceIndex)?:
|
||||||
|
let sourceElementPath = flattenSourceElementPaths[flattenSourceIndex]
|
||||||
|
let targetElementPath = ElementPath(element: targetElementIndex, section: targetSectionIndex)
|
||||||
|
if case .none = sourceElementTraces[sourceElementPath].reference {
|
||||||
|
|
||||||
|
targetElementReferences[targetElementPath] = sourceElementPath
|
||||||
|
sourceElementTraces[sourceElementPath].reference = targetElementPath
|
||||||
|
}
|
||||||
|
|
||||||
|
case .duplicate(let reference)?:
|
||||||
|
if let flattenSourceIndex = reference.next() {
|
||||||
|
|
||||||
|
let sourceElementPath = flattenSourceElementPaths[flattenSourceIndex]
|
||||||
|
let targetElementPath = ElementPath(element: targetElementIndex, section: targetSectionIndex)
|
||||||
|
targetElementReferences[targetElementPath] = sourceElementPath
|
||||||
|
sourceElementTraces[sourceElementPath].reference = targetElementPath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for sourceSectionIndex in contiguousSourceSections.indices {
|
||||||
|
|
||||||
|
let sourceSection = sourceSections[sourceSectionIndex]
|
||||||
|
let sourceElements = contiguousSourceSections[sourceSectionIndex]
|
||||||
|
var firstStageElements = sourceElements
|
||||||
|
|
||||||
|
if case .some = sectionResult.sourceTraces[sourceSectionIndex].reference {
|
||||||
|
|
||||||
|
var offsetByDelete = 0
|
||||||
|
var secondStageElements = ContiguousArray<Element>()
|
||||||
|
for sourceElementIndex in sourceElements.indices {
|
||||||
|
|
||||||
|
let sourceElementPath = ElementPath(element: sourceElementIndex, section: sourceSectionIndex)
|
||||||
|
sourceElementTraces[sourceElementPath].deleteOffset = offsetByDelete
|
||||||
|
|
||||||
|
if let targetElementPath = sourceElementTraces[sourceElementPath].reference,
|
||||||
|
case .some = sectionResult.targetReferences[targetElementPath.section] {
|
||||||
|
|
||||||
|
let targetElement = contiguousTargetSections[targetElementPath]
|
||||||
|
firstStageElements[sourceElementIndex] = targetElement
|
||||||
|
secondStageElements.append(targetElement)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
elementDeleted.append(sourceElementPath)
|
||||||
|
sourceElementTraces[sourceElementPath].isTracked = true
|
||||||
|
offsetByDelete += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
let secondStageSection = Section(source: sourceSection, elements: secondStageElements)
|
||||||
|
secondStageSections.append(secondStageSection)
|
||||||
|
}
|
||||||
|
|
||||||
|
let firstStageSection = Section(source: sourceSection, elements: firstStageElements)
|
||||||
|
firstStageSections[sourceSectionIndex] = firstStageSection
|
||||||
|
}
|
||||||
|
for targetSectionIndex in contiguousTargetSections.indices {
|
||||||
|
|
||||||
|
guard let sourceSectionIndex = sectionResult.targetReferences[targetSectionIndex] else {
|
||||||
|
|
||||||
|
thirdStageSections.append(targetSections[targetSectionIndex])
|
||||||
|
fourthStageSections.append(targetSections[targetSectionIndex])
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var untrackedSourceIndex: Int? = 0
|
||||||
|
let targetElements = contiguousTargetSections[targetSectionIndex]
|
||||||
|
let sectionDeleteOffset = sectionResult.sourceTraces[sourceSectionIndex].deleteOffset
|
||||||
|
let thirdStageSection = secondStageSections[sourceSectionIndex - sectionDeleteOffset]
|
||||||
|
thirdStageSections.append(thirdStageSection)
|
||||||
|
|
||||||
|
var fourthStageElements = ContiguousArray<Element>()
|
||||||
|
fourthStageElements.reserveCapacity(targetElements.count)
|
||||||
|
|
||||||
|
for targetElementIndex in targetElements.indices {
|
||||||
|
|
||||||
|
untrackedSourceIndex = untrackedSourceIndex.flatMap { index in
|
||||||
|
|
||||||
|
sourceElementTraces[sourceSectionIndex].suffix(from: index).firstIndex { !$0.isTracked }
|
||||||
|
}
|
||||||
|
let targetElementPath = ElementPath(element: targetElementIndex, section: targetSectionIndex)
|
||||||
|
let targetElement = contiguousTargetSections[targetElementPath]
|
||||||
|
|
||||||
|
guard
|
||||||
|
let sourceElementPath = targetElementReferences[targetElementPath],
|
||||||
|
let movedSourceSectionIndex = sectionResult.sourceTraces[sourceElementPath.section].reference
|
||||||
|
else {
|
||||||
|
|
||||||
|
fourthStageElements.append(targetElement)
|
||||||
|
elementInserted.append(targetElementPath)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
sourceElementTraces[sourceElementPath].isTracked = true
|
||||||
|
|
||||||
|
let sourceElement = contiguousSourceSections[sourceElementPath]
|
||||||
|
fourthStageElements.append(targetElement)
|
||||||
|
|
||||||
|
if !targetElement.isContentEqual(to: sourceElement) {
|
||||||
|
|
||||||
|
elementUpdated.append(sourceElementPath)
|
||||||
|
}
|
||||||
|
if sourceElementPath.section != sourceSectionIndex || sourceElementPath.element != untrackedSourceIndex {
|
||||||
|
|
||||||
|
let deleteOffset = sourceElementTraces[sourceElementPath].deleteOffset
|
||||||
|
let moveSourceElementPath = ElementPath(element: sourceElementPath.element - deleteOffset, section: movedSourceSectionIndex)
|
||||||
|
elementMoved.append((source: moveSourceElementPath, target: targetElementPath))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let fourthStageSection = Section(source: thirdStageSection, elements: fourthStageElements)
|
||||||
|
fourthStageSections.append(fourthStageSection)
|
||||||
|
}
|
||||||
|
|
||||||
|
var changesets = ContiguousArray<Changeset<C>>()
|
||||||
|
if !elementUpdated.isEmpty {
|
||||||
|
|
||||||
|
changesets.append(
|
||||||
|
Changeset(
|
||||||
|
data: C(firstStageSections),
|
||||||
|
elementUpdated: elementUpdated
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if !sectionResult.deleted.isEmpty || !elementDeleted.isEmpty {
|
||||||
|
|
||||||
|
changesets.append(
|
||||||
|
Changeset(
|
||||||
|
data: C(secondStageSections),
|
||||||
|
sectionDeleted: sectionResult.deleted,
|
||||||
|
elementDeleted: elementDeleted
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if !sectionResult.inserted.isEmpty || !sectionResult.moved.isEmpty {
|
||||||
|
|
||||||
|
changesets.append(
|
||||||
|
Changeset(
|
||||||
|
data: C(thirdStageSections),
|
||||||
|
sectionInserted: sectionResult.inserted,
|
||||||
|
sectionMoved: sectionResult.moved
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if !elementInserted.isEmpty || !elementMoved.isEmpty {
|
||||||
|
|
||||||
|
changesets.append(
|
||||||
|
Changeset(
|
||||||
|
data: C(fourthStageSections),
|
||||||
|
elementInserted: elementInserted,
|
||||||
|
elementMoved: elementMoved
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if !sectionResult.updated.isEmpty {
|
||||||
|
|
||||||
|
changesets.append(
|
||||||
|
Changeset(
|
||||||
|
data: target,
|
||||||
|
sectionUpdated: sectionResult.updated
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if !changesets.isEmpty {
|
||||||
|
|
||||||
|
let index = changesets.index(before: changesets.endIndex)
|
||||||
|
changesets[index].data = target
|
||||||
|
}
|
||||||
|
self.init(changesets)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - MutableCollection
|
||||||
|
|
||||||
|
extension MutableCollection where Element: MutableCollection, Index == Int, Element.Index == Int {
|
||||||
|
|
||||||
|
@inlinable
|
||||||
|
internal subscript<O>(path: Internals.DiffableDataUIDispatcher<O>.ElementPath) -> Element.Element {
|
||||||
|
|
||||||
|
get { return self[path.section][path.element] }
|
||||||
|
set { self[path.section][path.element] = newValue }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
229
Sources/Internals.DiffableDataUIDispatcher.swift
Normal file
229
Sources/Internals.DiffableDataUIDispatcher.swift
Normal file
@@ -0,0 +1,229 @@
|
|||||||
|
//
|
||||||
|
// Internals.DiffableDataUIDispatcher.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) || canImport(AppKit)
|
||||||
|
|
||||||
|
import CoreData
|
||||||
|
|
||||||
|
#if canImport(QuartzCore)
|
||||||
|
import QuartzCore
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - Internals
|
||||||
|
|
||||||
|
extension Internals {
|
||||||
|
|
||||||
|
// MARK: Internal
|
||||||
|
|
||||||
|
// Implementation based on https://github.com/ra1028/DiffableDataSources
|
||||||
|
@usableFromInline
|
||||||
|
internal final class DiffableDataUIDispatcher<O: DynamicObject> {
|
||||||
|
|
||||||
|
// MARK: Internal
|
||||||
|
|
||||||
|
typealias ObjectType = O
|
||||||
|
|
||||||
|
init(dataStack: DataStack) {
|
||||||
|
|
||||||
|
self.dataStack = dataStack
|
||||||
|
}
|
||||||
|
|
||||||
|
func apply<View: AnyObject>(_ snapshot: DiffableDataSourceSnapshot, view: View?, animatingDifferences: Bool, performUpdates: @escaping (View, StagedChangeset<[Internals.DiffableDataSourceSnapshot.Section]>, @escaping ([Internals.DiffableDataSourceSnapshot.Section]) -> Void) -> Void) {
|
||||||
|
|
||||||
|
self.dispatcher.dispatch { [weak self] in
|
||||||
|
|
||||||
|
guard let self = self else {
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
self.currentSnapshot = snapshot
|
||||||
|
|
||||||
|
let newSections = snapshot.sections
|
||||||
|
guard let view = view else {
|
||||||
|
|
||||||
|
return self.sections = newSections
|
||||||
|
}
|
||||||
|
|
||||||
|
let performDiffingUpdates: () -> Void = {
|
||||||
|
|
||||||
|
let changeset = StagedChangeset(source: self.sections, target: newSections)
|
||||||
|
performUpdates(view, changeset) { sections in
|
||||||
|
|
||||||
|
self.sections = sections
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if canImport(QuartzCore)
|
||||||
|
|
||||||
|
if !animatingDifferences {
|
||||||
|
|
||||||
|
CATransaction.begin()
|
||||||
|
CATransaction.setDisableActions(true)
|
||||||
|
|
||||||
|
performDiffingUpdates()
|
||||||
|
|
||||||
|
CATransaction.commit()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
performDiffingUpdates()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func snapshot() -> DiffableDataSourceSnapshot {
|
||||||
|
|
||||||
|
var snapshot: DiffableDataSourceSnapshot = .init()
|
||||||
|
snapshot.sections = self.currentSnapshot.sections
|
||||||
|
return snapshot
|
||||||
|
}
|
||||||
|
|
||||||
|
func itemIdentifier(for indexPath: IndexPath) -> O.ObjectID? {
|
||||||
|
|
||||||
|
guard (0 ..< self.sections.endIndex) ~= indexPath.section else {
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
let items = self.sections[indexPath.section].elements
|
||||||
|
guard (0 ..< items.endIndex) ~= indexPath.item else {
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return items[indexPath.item].differenceIdentifier
|
||||||
|
}
|
||||||
|
|
||||||
|
func indexPath(for itemIdentifier: O.ObjectID) -> IndexPath? {
|
||||||
|
|
||||||
|
let indexPathMap: [O.ObjectID: IndexPath] = self.sections.enumerated().reduce(into: [:]) { result, section in
|
||||||
|
|
||||||
|
for (itemIndex, item) in section.element.elements.enumerated() {
|
||||||
|
|
||||||
|
result[item.differenceIdentifier] = IndexPath(
|
||||||
|
item: itemIndex,
|
||||||
|
section: section.offset
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return indexPathMap[itemIdentifier]
|
||||||
|
}
|
||||||
|
|
||||||
|
func numberOfSections() -> Int {
|
||||||
|
|
||||||
|
return self.sections.count
|
||||||
|
}
|
||||||
|
|
||||||
|
func numberOfItems(inSection section: Int) -> Int {
|
||||||
|
|
||||||
|
return self.sections[section].elements.count
|
||||||
|
}
|
||||||
|
|
||||||
|
func sectionIdentifier(inSection section: Int) -> String {
|
||||||
|
|
||||||
|
return self.sections[section].differenceIdentifier
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: Private
|
||||||
|
|
||||||
|
private let dispatcher: MainThreadSerialDispatcher = .init()
|
||||||
|
private let dataStack: DataStack
|
||||||
|
|
||||||
|
private var currentSnapshot: Internals.DiffableDataSourceSnapshot = .init()
|
||||||
|
private var sections: [Internals.DiffableDataSourceSnapshot.Section] = []
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - ElementPath
|
||||||
|
|
||||||
|
@usableFromInline
|
||||||
|
internal struct ElementPath: Hashable {
|
||||||
|
|
||||||
|
@usableFromInline
|
||||||
|
var element: Int
|
||||||
|
|
||||||
|
@usableFromInline
|
||||||
|
var section: Int
|
||||||
|
|
||||||
|
@inlinable
|
||||||
|
init(element: Int, section: Int) {
|
||||||
|
|
||||||
|
self.element = element
|
||||||
|
self.section = section
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - MainThreadSerialDispatcher
|
||||||
|
|
||||||
|
fileprivate final class MainThreadSerialDispatcher {
|
||||||
|
|
||||||
|
// MARK: FilePrivate
|
||||||
|
|
||||||
|
fileprivate init() {
|
||||||
|
|
||||||
|
self.executingCount.initialize(to: 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
|
||||||
|
self.executingCount.deinitialize(count: 1)
|
||||||
|
self.executingCount.deallocate()
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate func dispatch(_ action: @escaping () -> Void) {
|
||||||
|
|
||||||
|
let count = OSAtomicIncrement32(self.executingCount)
|
||||||
|
if Thread.isMainThread && count == 1 {
|
||||||
|
|
||||||
|
action()
|
||||||
|
OSAtomicDecrement32(executingCount)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
DispatchQueue.main.async { [weak self] in
|
||||||
|
|
||||||
|
guard let self = self else {
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
action()
|
||||||
|
OSAtomicDecrement32(self.executingCount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: Private
|
||||||
|
|
||||||
|
private let executingCount: UnsafeMutablePointer<Int32> = .allocate(capacity: 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,157 +0,0 @@
|
|||||||
//
|
|
||||||
// Internals.FallbackDiffableDataUIDispatcher.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) || canImport(AppKit)
|
|
||||||
|
|
||||||
import CoreData
|
|
||||||
|
|
||||||
#if canImport(QuartzCore)
|
|
||||||
import QuartzCore
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: - Internals
|
|
||||||
|
|
||||||
extension Internals {
|
|
||||||
|
|
||||||
// MARK: Internal
|
|
||||||
|
|
||||||
// // Implementation based on https://github.com/ra1028/DiffableDataSources
|
|
||||||
// internal final class FallbackDiffableDataUIDispatcher {
|
|
||||||
//
|
|
||||||
// // MARK: Internal
|
|
||||||
//
|
|
||||||
// internal func apply() {
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func snapshot() -> DiffableDataSourceSnapshot<SectionIdentifierType, ItemIdentifierType> {
|
|
||||||
// var snapshot = DiffableDataSourceSnapshot<SectionIdentifierType, ItemIdentifierType>()
|
|
||||||
// snapshot.structure.sections = currentSnapshot.structure.sections
|
|
||||||
// return snapshot
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func itemIdentifier(for indexPath: IndexPath) -> ItemIdentifierType? {
|
|
||||||
// guard 0..<sections.endIndex ~= indexPath.section else {
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// let items = sections[indexPath.section].elements
|
|
||||||
//
|
|
||||||
// guard 0..<items.endIndex ~= indexPath.item else {
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return items[indexPath.item].differenceIdentifier
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func unsafeItemIdentifier(for indexPath: IndexPath, file: StaticString = #file, line: UInt = #line) -> ItemIdentifierType {
|
|
||||||
// guard let itemIdentifier = itemIdentifier(for: indexPath) else {
|
|
||||||
// universalError("Item not found at the specified index path(\(indexPath)).")
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return itemIdentifier
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func indexPath(for itemIdentifier: ItemIdentifierType) -> IndexPath? {
|
|
||||||
// let indexPathMap: [ItemIdentifierType: IndexPath] = sections.enumerated()
|
|
||||||
// .reduce(into: [:]) { result, section in
|
|
||||||
// for (itemIndex, item) in section.element.elements.enumerated() {
|
|
||||||
// result[item.differenceIdentifier] = IndexPath(
|
|
||||||
// item: itemIndex,
|
|
||||||
// section: section.offset
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// return indexPathMap[itemIdentifier]
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func numberOfSections() -> Int {
|
|
||||||
// return sections.count
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func numberOfItems(inSection section: Int) -> Int {
|
|
||||||
// return sections[section].elements.count
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// // MARK: Private
|
|
||||||
//
|
|
||||||
// private let dispatcher: MainThreadSerialDispatcher = .init()
|
|
||||||
//
|
|
||||||
// private var currentSnapshot: Internals.DiffableDataSourceSnapshot = FallbackDiffableDataSourceSnapshot()
|
|
||||||
// private var sections: [FallbackDiffableDataSourceSnapshot.BackingStructure.Section] = []
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// // MARK: - MainThreadSerialDispatcher
|
|
||||||
//
|
|
||||||
// fileprivate final class MainThreadSerialDispatcher {
|
|
||||||
//
|
|
||||||
// // MARK: FilePrivate
|
|
||||||
//
|
|
||||||
// fileprivate init() {
|
|
||||||
//
|
|
||||||
// self.executingCount.initialize(to: 0)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// deinit {
|
|
||||||
//
|
|
||||||
// self.executingCount.deinitialize(count: 1)
|
|
||||||
// self.executingCount.deallocate()
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// fileprivate func dispatch(_ action: @escaping () -> Void) {
|
|
||||||
//
|
|
||||||
// let count = OSAtomicIncrement32(self.executingCount)
|
|
||||||
// if Thread.isMainThread && count == 1 {
|
|
||||||
//
|
|
||||||
// action()
|
|
||||||
// OSAtomicDecrement32(executingCount)
|
|
||||||
// }
|
|
||||||
// else {
|
|
||||||
//
|
|
||||||
// DispatchQueue.main.async { [weak self] in
|
|
||||||
//
|
|
||||||
// guard let self = self else {
|
|
||||||
//
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// action()
|
|
||||||
// OSAtomicDecrement32(self.executingCount)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// // MARK: Private
|
|
||||||
//
|
|
||||||
// private let executingCount: UnsafeMutablePointer<Int32> = .allocate(capacity: 1)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -41,7 +41,12 @@ import AppKit
|
|||||||
|
|
||||||
internal protocol FetchedDiffableDataSourceSnapshotHandler: AnyObject {
|
internal protocol FetchedDiffableDataSourceSnapshotHandler: AnyObject {
|
||||||
|
|
||||||
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChangeContentWith snapshot: DiffableDataSourceSnapshotProtocol)
|
// @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?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -75,14 +80,14 @@ extension Internals {
|
|||||||
|
|
||||||
internal func initialFetch() {
|
internal func initialFetch() {
|
||||||
|
|
||||||
#if canImport(UIKit) || canImport(AppKit)
|
// #if canImport(UIKit) || canImport(AppKit)
|
||||||
|
//
|
||||||
if #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) {
|
// if #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) {
|
||||||
|
//
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
#endif
|
// #endif
|
||||||
|
|
||||||
guard let fetchedResultsController = self.fetchedResultsController else {
|
guard let fetchedResultsController = self.fetchedResultsController else {
|
||||||
|
|
||||||
@@ -94,19 +99,19 @@ extension Internals {
|
|||||||
|
|
||||||
// MARK: NSFetchedResultsControllerDelegate
|
// MARK: NSFetchedResultsControllerDelegate
|
||||||
|
|
||||||
#if canImport(UIKit) || canImport(AppKit)
|
// #if canImport(UIKit) || canImport(AppKit)
|
||||||
|
//
|
||||||
@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *)
|
// @available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *)
|
||||||
@objc
|
// @objc
|
||||||
dynamic func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChangeContentWith snapshot: NSDiffableDataSourceSnapshotReference) {
|
// dynamic func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChangeContentWith snapshot: NSDiffableDataSourceSnapshotReference) {
|
||||||
|
//
|
||||||
self.handler?.controller(
|
// self.handler?.controller(
|
||||||
controller,
|
// controller,
|
||||||
didChangeContentWith: snapshot as NSDiffableDataSourceSnapshot<NSString, NSManagedObjectID>
|
// didChangeContentWith: snapshot as NSDiffableDataSourceSnapshot<String, NSManagedObjectID>
|
||||||
)
|
// )
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
#endif
|
// #endif
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
dynamic func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
|
dynamic func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
|
||||||
@@ -118,5 +123,14 @@ extension Internals {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc
|
||||||
|
dynamic func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, sectionIndexTitleForSectionName sectionName: String) -> String? {
|
||||||
|
|
||||||
|
return self.handler?.controller(
|
||||||
|
controller,
|
||||||
|
sectionIndexTitleForSectionName: sectionName
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,18 +56,19 @@ extension Internals {
|
|||||||
deinit {
|
deinit {
|
||||||
|
|
||||||
self.observer.map(NotificationCenter.default.removeObserver(_:))
|
self.observer.map(NotificationCenter.default.removeObserver(_:))
|
||||||
|
self.observers.removeAllObjects()
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func addObserver<U: AnyObject>(_ observer: U, closure: @escaping (T) -> Void) {
|
internal func addObserver<U: AnyObject>(_ observer: U, closure: @escaping (T) -> Void) {
|
||||||
|
|
||||||
self.observers.setObject(Closure(closure), forKey: observer)
|
self.observers.setObject(Closure<T, Void>(closure), forKey: observer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// MARK: Private
|
// MARK: Private
|
||||||
|
|
||||||
private var observer: NSObjectProtocol!
|
private var observer: NSObjectProtocol!
|
||||||
private let observers: NSMapTable<AnyObject, Closure> = .weakToStrongObjects()
|
private let observers: NSMapTable<AnyObject, Closure<T, Void>> = .weakToStrongObjects()
|
||||||
|
|
||||||
private func notifyObservers(_ sharedValue: T) {
|
private func notifyObservers(_ sharedValue: T) {
|
||||||
|
|
||||||
@@ -77,31 +78,8 @@ extension Internals {
|
|||||||
}
|
}
|
||||||
for closure in enumerator {
|
for closure in enumerator {
|
||||||
|
|
||||||
(closure as! Closure).invoke(with: sharedValue)
|
(closure as! Closure<T, Void>).invoke(with: sharedValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// MARK: - Closure
|
|
||||||
|
|
||||||
fileprivate final class Closure {
|
|
||||||
|
|
||||||
// MARK: FilePrivate
|
|
||||||
|
|
||||||
fileprivate init(_ closure: @escaping (T) -> Void) {
|
|
||||||
|
|
||||||
self.closure = closure
|
|
||||||
}
|
|
||||||
|
|
||||||
fileprivate func invoke(with argument: T) {
|
|
||||||
|
|
||||||
self.closure(argument)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: Private
|
|
||||||
|
|
||||||
private let closure: (T) -> Void
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import Foundation
|
|||||||
|
|
||||||
// MARK: - Internals
|
// MARK: - Internals
|
||||||
|
|
||||||
|
@usableFromInline
|
||||||
internal enum Internals {
|
internal enum Internals {
|
||||||
|
|
||||||
// MARK: Associated Objects
|
// MARK: Associated Objects
|
||||||
|
|||||||
@@ -1453,7 +1453,7 @@ extension ListMonitor: FetchedResultsControllerHandler {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, sectionIndexTitleForSectionName sectionName: String?) -> String? {
|
internal func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, sectionIndexTitleForSectionName sectionName: String?) -> String? {
|
||||||
|
|
||||||
return self.sectionIndexTransformer(sectionName)
|
return self.sectionIndexTransformer(sectionName)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,24 @@ public struct ListSnapshot<O: DynamicObject>: SnapshotResult, RandomAccessCollec
|
|||||||
public typealias SectionID = String
|
public typealias SectionID = String
|
||||||
public typealias ItemID = O.ObjectID
|
public typealias ItemID = O.ObjectID
|
||||||
|
|
||||||
|
public init(byCloning snapshot: ListSnapshot<O>, for dataStack: DataStack) {
|
||||||
|
|
||||||
|
// if #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) {
|
||||||
|
//
|
||||||
|
// self.init(
|
||||||
|
// diffableSnapshot: snapshot.diffableSnapshot as! NSDiffableDataSourceSnapshot<String, NSManagedObjectID>,
|
||||||
|
// context: dataStack.mainContext
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
// else {
|
||||||
|
|
||||||
|
self.init(
|
||||||
|
diffableSnapshot: snapshot.diffableSnapshot as! Internals.DiffableDataSourceSnapshot,
|
||||||
|
context: dataStack.mainContext
|
||||||
|
)
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
public subscript<S: Sequence>(indices indices: S) -> [LiveObject<O>] where S.Element == Index {
|
public subscript<S: Sequence>(indices indices: S) -> [LiveObject<O>] where S.Element == Index {
|
||||||
|
|
||||||
let context = self.context!
|
let context = self.context!
|
||||||
@@ -252,13 +270,29 @@ public struct ListSnapshot<O: DynamicObject>: SnapshotResult, RandomAccessCollec
|
|||||||
|
|
||||||
// MARK: Internal
|
// MARK: Internal
|
||||||
|
|
||||||
|
internal private(set) var diffableSnapshot: DiffableDataSourceSnapshotProtocol
|
||||||
|
|
||||||
internal init() {
|
internal init() {
|
||||||
|
|
||||||
self.diffableSnapshot = Internals.DiffableDataSourceSnapshot()
|
// 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.context = nil
|
self.context = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
internal init(diffableSnapshot: DiffableDataSourceSnapshotProtocol, context: NSManagedObjectContext) {
|
// @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
|
self.diffableSnapshot = diffableSnapshot
|
||||||
self.context = context
|
self.context = context
|
||||||
@@ -270,5 +304,4 @@ public struct ListSnapshot<O: DynamicObject>: SnapshotResult, RandomAccessCollec
|
|||||||
private let id: UUID = .init()
|
private let id: UUID = .init()
|
||||||
private let context: NSManagedObjectContext?
|
private let context: NSManagedObjectContext?
|
||||||
|
|
||||||
private var diffableSnapshot: DiffableDataSourceSnapshotProtocol
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,29 +45,6 @@ public final class LiveList<O: DynamicObject>: Hashable {
|
|||||||
public typealias SectionID = SnapshotType.SectionID
|
public typealias SectionID = SnapshotType.SectionID
|
||||||
public typealias ItemID = SnapshotType.ItemID
|
public typealias ItemID = SnapshotType.ItemID
|
||||||
|
|
||||||
public fileprivate(set) var snapshot: SnapshotType = .init() {
|
|
||||||
|
|
||||||
willSet {
|
|
||||||
|
|
||||||
self.willChange()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public var numberOfItems: Int {
|
|
||||||
|
|
||||||
return self.snapshot.numberOfItems
|
|
||||||
}
|
|
||||||
|
|
||||||
public var numberOfSections: Int {
|
|
||||||
|
|
||||||
return self.snapshot.numberOfSections
|
|
||||||
}
|
|
||||||
|
|
||||||
public var sectionIdentifiers: [SectionID] {
|
|
||||||
|
|
||||||
return self.snapshot.sectionIdentifiers
|
|
||||||
}
|
|
||||||
|
|
||||||
public subscript(section sectionID: SectionID) -> [LiveObject<O>] {
|
public subscript(section sectionID: SectionID) -> [LiveObject<O>] {
|
||||||
|
|
||||||
let context = self.context
|
let context = self.context
|
||||||
@@ -85,6 +62,24 @@ public final class LiveList<O: DynamicObject>: Hashable {
|
|||||||
return self.context.liveObject(id: validID)
|
return self.context.liveObject(id: validID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public subscript(indexPath indexPath: IndexPath) -> LiveObject<O>? {
|
||||||
|
|
||||||
|
let snapshot = self.snapshot
|
||||||
|
let sectionIdentifiers = snapshot.sectionIdentifiers
|
||||||
|
guard sectionIdentifiers.indices.contains(indexPath.section) else {
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
let sectionID = sectionIdentifiers[indexPath.section]
|
||||||
|
let itemIdentifiers = snapshot.itemIdentifiers(inSection: sectionID)
|
||||||
|
guard itemIdentifiers.indices.contains(indexPath.item) else {
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
let itemID = itemIdentifiers[indexPath.item]
|
||||||
|
return self.context.liveObject(id: itemID)
|
||||||
|
}
|
||||||
|
|
||||||
public subscript<S: Sequence>(section sectionID: SectionID, itemIndices itemIndices: S) -> [LiveObject<O>] where S.Element == Int {
|
public subscript<S: Sequence>(section sectionID: SectionID, itemIndices itemIndices: S) -> [LiveObject<O>] where S.Element == Int {
|
||||||
|
|
||||||
let context = self.context
|
let context = self.context
|
||||||
@@ -96,6 +91,34 @@ public final class LiveList<O: DynamicObject>: Hashable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public fileprivate(set) var snapshot: SnapshotType = .init() {
|
||||||
|
|
||||||
|
willSet {
|
||||||
|
|
||||||
|
self.willChange()
|
||||||
|
}
|
||||||
|
didSet {
|
||||||
|
|
||||||
|
self.notifyObservers(self.snapshot)
|
||||||
|
self.didChange()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public var numberOfItems: Int {
|
||||||
|
|
||||||
|
return self.snapshot.numberOfItems
|
||||||
|
}
|
||||||
|
|
||||||
|
public var numberOfSections: Int {
|
||||||
|
|
||||||
|
return self.snapshot.numberOfSections
|
||||||
|
}
|
||||||
|
|
||||||
|
public var sectionIdentifiers: [SectionID] {
|
||||||
|
|
||||||
|
return self.snapshot.sectionIdentifiers
|
||||||
|
}
|
||||||
|
|
||||||
public var items: [LiveObject<O>] {
|
public var items: [LiveObject<O>] {
|
||||||
|
|
||||||
let context = self.context
|
let context = self.context
|
||||||
@@ -142,6 +165,26 @@ public final class LiveList<O: DynamicObject>: Hashable {
|
|||||||
return self.snapshot.indexOfSection(identifier)
|
return self.snapshot.indexOfSection(identifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func addObserver<T: AnyObject>(_ observer: T, _ callback: @escaping (LiveList<O>, ListSnapshot<O>) -> Void) {
|
||||||
|
|
||||||
|
self.observers.setObject(
|
||||||
|
Internals.Closure(callback),
|
||||||
|
forKey: observer
|
||||||
|
)
|
||||||
|
|
||||||
|
callback(self, self.snapshot)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func removeObserver<T: AnyObject>(_ observer: T) {
|
||||||
|
|
||||||
|
self.observers.removeObject(forKey: observer)
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
|
||||||
|
self.observers.removeAllObjects()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// MARK: Equatable
|
// MARK: Equatable
|
||||||
|
|
||||||
@@ -222,12 +265,14 @@ public final class LiveList<O: DynamicObject>: Hashable {
|
|||||||
|
|
||||||
private var fetchedResultsController: Internals.CoreStoreFetchedResultsController
|
private var fetchedResultsController: Internals.CoreStoreFetchedResultsController
|
||||||
private var fetchedResultsControllerDelegate: Internals.FetchedDiffableDataSourceSnapshotDelegate
|
private var fetchedResultsControllerDelegate: Internals.FetchedDiffableDataSourceSnapshotDelegate
|
||||||
|
private let sectionIndexTransformer: (_ sectionName: KeyPathString?) -> String?
|
||||||
private var applyFetchClauses: (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void
|
private var applyFetchClauses: (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void
|
||||||
private var observerForWillChangePersistentStore: Internals.NotificationObserver!
|
private var observerForWillChangePersistentStore: Internals.NotificationObserver!
|
||||||
private var observerForDidChangePersistentStore: Internals.NotificationObserver!
|
private var observerForDidChangePersistentStore: Internals.NotificationObserver!
|
||||||
|
|
||||||
private let from: From<ObjectType>
|
private let from: From<ObjectType>
|
||||||
private let sectionBy: SectionBy<ObjectType>?
|
private let sectionBy: SectionBy<ObjectType>?
|
||||||
|
private let observers: NSMapTable<AnyObject, Internals.Closure<(LiveList<O>, ListSnapshot<O>), Void>> = .weakToStrongObjects()
|
||||||
|
|
||||||
private lazy var context: NSManagedObjectContext = self.fetchedResultsController.managedObjectContext
|
private lazy var context: NSManagedObjectContext = self.fetchedResultsController.managedObjectContext
|
||||||
|
|
||||||
@@ -279,20 +324,31 @@ public final class LiveList<O: DynamicObject>: Hashable {
|
|||||||
self.rawObjectWillChange = nil
|
self.rawObjectWillChange = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let sectionIndexTransformer = sectionBy?.sectionIndexTransformer {
|
||||||
|
|
||||||
// if let sectionIndexTransformer = sectionBy?.sectionIndexTransformer {
|
self.sectionIndexTransformer = sectionIndexTransformer
|
||||||
//
|
}
|
||||||
// self.sectionIndexTransformer = sectionIndexTransformer
|
else {
|
||||||
// }
|
|
||||||
// else {
|
self.sectionIndexTransformer = { $0 }
|
||||||
//
|
}
|
||||||
// self.sectionIndexTransformer = { $0 }
|
|
||||||
// }
|
|
||||||
self.applyFetchClauses = applyFetchClauses
|
self.applyFetchClauses = applyFetchClauses
|
||||||
self.fetchedResultsControllerDelegate.handler = self
|
self.fetchedResultsControllerDelegate.handler = self
|
||||||
|
|
||||||
try! self.fetchedResultsController.performFetchFromSpecifiedStores()
|
try! self.fetchedResultsController.performFetchFromSpecifiedStores()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func notifyObservers(_ snapshot: ListSnapshot<O>) {
|
||||||
|
|
||||||
|
guard let enumerator = self.observers.objectEnumerator() else {
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for closure in enumerator {
|
||||||
|
|
||||||
|
(closure as! Internals.Closure<(LiveList<O>, ListSnapshot<O>), Void>).invoke(with: (self, snapshot))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -302,13 +358,27 @@ extension LiveList: FetchedDiffableDataSourceSnapshotHandler {
|
|||||||
|
|
||||||
// MARK: FetchedDiffableDataSourceSnapshotHandler
|
// MARK: FetchedDiffableDataSourceSnapshotHandler
|
||||||
|
|
||||||
internal func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChangeContentWith snapshot: DiffableDataSourceSnapshotProtocol) {
|
// @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) {
|
||||||
|
|
||||||
self.snapshot = .init(
|
self.snapshot = .init(
|
||||||
diffableSnapshot: snapshot,
|
diffableSnapshot: snapshot,
|
||||||
context: controller.managedObjectContext
|
context: controller.managedObjectContext
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, sectionIndexTitleForSectionName sectionName: String?) -> String? {
|
||||||
|
|
||||||
|
return self.sectionIndexTransformer(sectionName)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user