mirror of
https://github.com/JohnEstropia/CoreStore.git
synced 2026-01-15 05:33:31 +01:00
WIP: editable datasources
This commit is contained in:
@@ -188,6 +188,14 @@
|
||||
B52557881D02DE8100E51965 /* FetchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52557871D02DE8100E51965 /* FetchTests.swift */; };
|
||||
B52557891D02DE8100E51965 /* FetchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52557871D02DE8100E51965 /* FetchTests.swift */; };
|
||||
B525578A1D02DE8100E51965 /* FetchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52557871D02DE8100E51965 /* FetchTests.swift */; };
|
||||
B5277672234F1AEB0056BE9F /* NSManagedObjectContext+Logging.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5277671234F1AEB0056BE9F /* NSManagedObjectContext+Logging.swift */; };
|
||||
B5277673234F1AEB0056BE9F /* NSManagedObjectContext+Logging.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5277671234F1AEB0056BE9F /* NSManagedObjectContext+Logging.swift */; };
|
||||
B5277674234F1AEB0056BE9F /* NSManagedObjectContext+Logging.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5277671234F1AEB0056BE9F /* NSManagedObjectContext+Logging.swift */; };
|
||||
B5277675234F1AEB0056BE9F /* NSManagedObjectContext+Logging.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5277671234F1AEB0056BE9F /* NSManagedObjectContext+Logging.swift */; };
|
||||
B5277677234F265F0056BE9F /* Internals.SharedNotificationObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5277676234F265F0056BE9F /* Internals.SharedNotificationObserver.swift */; };
|
||||
B5277678234F265F0056BE9F /* Internals.SharedNotificationObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5277676234F265F0056BE9F /* Internals.SharedNotificationObserver.swift */; };
|
||||
B5277679234F265F0056BE9F /* Internals.SharedNotificationObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5277676234F265F0056BE9F /* Internals.SharedNotificationObserver.swift */; };
|
||||
B527767A234F265F0056BE9F /* Internals.SharedNotificationObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5277676234F265F0056BE9F /* Internals.SharedNotificationObserver.swift */; };
|
||||
B52DD17E1BE1F8CD00949AFE /* CoreStore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B52DD1741BE1F8CC00949AFE /* CoreStore.framework */; };
|
||||
B52DD1931BE1F8FD00949AFE /* CoreStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 2F03A53519C5C6DA005002A5 /* CoreStore.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
B52DD1941BE1F92500949AFE /* CoreStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F291E2619C6D3CF007AF63F /* CoreStore.swift */; };
|
||||
@@ -552,6 +560,18 @@
|
||||
B5BF7FBD234C99190070E741 /* Internals.FallbackDiffableDataUIDispatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FBB234C99190070E741 /* Internals.FallbackDiffableDataUIDispatcher.swift */; };
|
||||
B5BF7FBE234C99190070E741 /* Internals.FallbackDiffableDataUIDispatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FBB234C99190070E741 /* Internals.FallbackDiffableDataUIDispatcher.swift */; };
|
||||
B5BF7FBF234C99190070E741 /* Internals.FallbackDiffableDataUIDispatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FBB234C99190070E741 /* Internals.FallbackDiffableDataUIDispatcher.swift */; };
|
||||
B5BF7FC1234D7B2E0070E741 /* 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 */; };
|
||||
B5BF7FC4234D7B2E0070E741 /* LiveObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FC0234D7B2E0070E741 /* LiveObject.swift */; };
|
||||
B5BF7FC6234D7E460070E741 /* ObjectSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FC5234D7E460070E741 /* ObjectSnapshot.swift */; };
|
||||
B5BF7FC7234D7E460070E741 /* ObjectSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FC5234D7E460070E741 /* ObjectSnapshot.swift */; };
|
||||
B5BF7FC8234D7E460070E741 /* ObjectSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FC5234D7E460070E741 /* ObjectSnapshot.swift */; };
|
||||
B5BF7FC9234D7E460070E741 /* ObjectSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FC5234D7E460070E741 /* ObjectSnapshot.swift */; };
|
||||
B5BF7FCB234D80910070E741 /* Internals.LazyNonmutating.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FCA234D80910070E741 /* Internals.LazyNonmutating.swift */; };
|
||||
B5BF7FCC234D80910070E741 /* Internals.LazyNonmutating.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FCA234D80910070E741 /* Internals.LazyNonmutating.swift */; };
|
||||
B5BF7FCD234D80910070E741 /* Internals.LazyNonmutating.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FCA234D80910070E741 /* Internals.LazyNonmutating.swift */; };
|
||||
B5BF7FCE234D80910070E741 /* Internals.LazyNonmutating.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FCA234D80910070E741 /* Internals.LazyNonmutating.swift */; };
|
||||
B5C976E31C6C9F6A00B1AF90 /* UnsafeDataTransaction+Observing.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C976E21C6C9F6A00B1AF90 /* UnsafeDataTransaction+Observing.swift */; };
|
||||
B5C976E41C6C9F9A00B1AF90 /* UnsafeDataTransaction+Observing.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C976E21C6C9F6A00B1AF90 /* UnsafeDataTransaction+Observing.swift */; };
|
||||
B5C976E51C6C9F9B00B1AF90 /* UnsafeDataTransaction+Observing.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C976E21C6C9F6A00B1AF90 /* UnsafeDataTransaction+Observing.swift */; };
|
||||
@@ -670,10 +690,6 @@
|
||||
B5E41EC11EA9BB37006240F0 /* DynamicSchema+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E41EBF1EA9BB37006240F0 /* DynamicSchema+Convenience.swift */; };
|
||||
B5E41EC21EA9BB37006240F0 /* DynamicSchema+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E41EBF1EA9BB37006240F0 /* DynamicSchema+Convenience.swift */; };
|
||||
B5E41EC31EA9BB37006240F0 /* DynamicSchema+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E41EBF1EA9BB37006240F0 /* DynamicSchema+Convenience.swift */; };
|
||||
B5E5FA4E22D162F400330931 /* ObjectSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E5FA4D22D162F400330931 /* ObjectSnapshot.swift */; };
|
||||
B5E5FA4F22D162F400330931 /* ObjectSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E5FA4D22D162F400330931 /* ObjectSnapshot.swift */; };
|
||||
B5E5FA5022D162F400330931 /* ObjectSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E5FA4D22D162F400330931 /* ObjectSnapshot.swift */; };
|
||||
B5E5FA5122D162F400330931 /* ObjectSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E5FA4D22D162F400330931 /* ObjectSnapshot.swift */; };
|
||||
B5E834B91B76311F001D3D50 /* BaseDataTransaction+Importing.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E834B81B76311F001D3D50 /* BaseDataTransaction+Importing.swift */; };
|
||||
B5E84EDF1AFF84500064E85B /* DataStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84EDB1AFF84500064E85B /* DataStack.swift */; };
|
||||
B5E84EE61AFF84610064E85B /* DefaultLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84EE31AFF84610064E85B /* DefaultLogger.swift */; };
|
||||
@@ -872,6 +888,8 @@
|
||||
B525577F1D029D2500E51965 /* TweakTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TweakTests.swift; sourceTree = "<group>"; };
|
||||
B52557831D02A07400E51965 /* SectionByTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SectionByTests.swift; sourceTree = "<group>"; };
|
||||
B52557871D02DE8100E51965 /* FetchTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FetchTests.swift; sourceTree = "<group>"; };
|
||||
B5277671234F1AEB0056BE9F /* NSManagedObjectContext+Logging.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSManagedObjectContext+Logging.swift"; sourceTree = "<group>"; };
|
||||
B5277676234F265F0056BE9F /* Internals.SharedNotificationObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Internals.SharedNotificationObserver.swift; sourceTree = "<group>"; };
|
||||
B52DD1741BE1F8CC00949AFE /* CoreStore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CoreStore.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
B52DD17D1BE1F8CC00949AFE /* CoreStoreTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CoreStoreTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
B52F742E1E9B50D0005F3DAC /* SchemaHistory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchemaHistory.swift; sourceTree = "<group>"; };
|
||||
@@ -962,6 +980,9 @@
|
||||
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>"; };
|
||||
B5BF7FBB234C99190070E741 /* Internals.FallbackDiffableDataUIDispatcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Internals.FallbackDiffableDataUIDispatcher.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>"; };
|
||||
B5BF7FCA234D80910070E741 /* Internals.LazyNonmutating.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Internals.LazyNonmutating.swift; sourceTree = "<group>"; };
|
||||
B5C976E21C6C9F6A00B1AF90 /* UnsafeDataTransaction+Observing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UnsafeDataTransaction+Observing.swift"; sourceTree = "<group>"; };
|
||||
B5C976E61C6E3A5900B1AF90 /* Internals.CoreStoreFetchedResultsController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Internals.CoreStoreFetchedResultsController.swift; sourceTree = "<group>"; };
|
||||
B5CA2B071F7E5ACA004B1936 /* WhereClauseType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WhereClauseType.swift; sourceTree = "<group>"; };
|
||||
@@ -1002,7 +1023,6 @@
|
||||
B5E222291CA51B6E00BA2E95 /* CSUnsafeDataTransaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSUnsafeDataTransaction.swift; sourceTree = "<group>"; };
|
||||
B5E294DC2349F8E7003E5956 /* SnapshotResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SnapshotResult.swift; sourceTree = "<group>"; };
|
||||
B5E41EBF1EA9BB37006240F0 /* DynamicSchema+Convenience.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DynamicSchema+Convenience.swift"; sourceTree = "<group>"; };
|
||||
B5E5FA4D22D162F400330931 /* ObjectSnapshot.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObjectSnapshot.swift; sourceTree = "<group>"; };
|
||||
B5E834B81B76311F001D3D50 /* BaseDataTransaction+Importing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BaseDataTransaction+Importing.swift"; sourceTree = "<group>"; };
|
||||
B5E84ED81AFF82360064E85B /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = SOURCE_ROOT; };
|
||||
B5E84ED91AFF82360064E85B /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = SOURCE_ROOT; };
|
||||
@@ -1527,9 +1547,10 @@
|
||||
B5F849702348A6690029D57B /* EnvironmentKeys.swift */,
|
||||
B5F84966234887EB0029D57B /* LiveQuery.swift */,
|
||||
B50132292344ECB500FC238B /* LiveList.swift */,
|
||||
B5BF7FC0234D7B2E0070E741 /* LiveObject.swift */,
|
||||
B5F8496B234898240029D57B /* ListSnapshot.swift */,
|
||||
B5BF7FC5234D7E460070E741 /* ObjectSnapshot.swift */,
|
||||
B50EE14123473C92009B8C47 /* CoreStoreObject+DataSources.swift */,
|
||||
B5E5FA4D22D162F400330931 /* ObjectSnapshot.swift */,
|
||||
B5D8CA752346E7590055D7D1 /* DataStack+DataSources.swift */,
|
||||
B5F335572348D75D00FD649F /* LiveResult.swift */,
|
||||
B5E294DC2349F8E7003E5956 /* SnapshotResult.swift */,
|
||||
@@ -1664,13 +1685,15 @@
|
||||
B533C4DA1D7D4BFA001383CB /* DispatchQueue+CoreStore.swift */,
|
||||
B5C976E61C6E3A5900B1AF90 /* Internals.CoreStoreFetchedResultsController.swift */,
|
||||
B5474D142227C08700B21FEC /* Internals.CoreStoreFetchRequest.swift */,
|
||||
B51260921E9B28F100402229 /* Internals.EntityIdentifier.swift */,
|
||||
B5BF7FAC234C41E90070E741 /* Internals.DiffableDataSourceSnapshot.swift */,
|
||||
B51260921E9B28F100402229 /* Internals.EntityIdentifier.swift */,
|
||||
B5BF7FBB234C99190070E741 /* Internals.FallbackDiffableDataUIDispatcher.swift */,
|
||||
B54A6A541BA15F2A007870FD /* Internals.FetchedResultsControllerDelegate.swift */,
|
||||
B501322F2346B76E00FC238B /* Internals.FetchedDiffableDataSourceSnapshotDelegate.swift */,
|
||||
B54A6A541BA15F2A007870FD /* Internals.FetchedResultsControllerDelegate.swift */,
|
||||
B5BF7FCA234D80910070E741 /* Internals.LazyNonmutating.swift */,
|
||||
B5FAD6AB1B51285300714891 /* Internals.MigrationManager.swift */,
|
||||
B5E84F2B1AFF849C0064E85B /* Internals.NotificationObserver.swift */,
|
||||
B5277676234F265F0056BE9F /* Internals.SharedNotificationObserver.swift */,
|
||||
B5DE522A230BD7CC00A22534 /* Internals.swift */,
|
||||
B5E84F2D1AFF849C0064E85B /* Internals.WeakObject.swift */,
|
||||
B51260881E9B252B00402229 /* NSEntityDescription+DynamicModel.swift */,
|
||||
@@ -1678,6 +1701,7 @@
|
||||
B58D0C621EAA0C7E003EDD87 /* NSManagedObject+DynamicModel.swift */,
|
||||
B52FD3A91E3B3EF10001D919 /* NSManagedObject+Logging.swift */,
|
||||
B5E84F2C1AFF849C0064E85B /* NSManagedObjectContext+CoreStore.swift */,
|
||||
B5277671234F1AEB0056BE9F /* NSManagedObjectContext+Logging.swift */,
|
||||
B5E84F351AFF85470064E85B /* NSManagedObjectContext+Querying.swift */,
|
||||
B5E84F321AFF85470064E85B /* NSManagedObjectContext+Setup.swift */,
|
||||
B5E84F331AFF85470064E85B /* NSManagedObjectContext+Transaction.swift */,
|
||||
@@ -2049,7 +2073,7 @@
|
||||
B5831B751F34AC7A00A9F647 /* RelationshipProtocol.swift in Sources */,
|
||||
B51B5C2D22D43E38009FA3BA /* KeyPath+KeyPaths.swift in Sources */,
|
||||
B5D8CA762346E7590055D7D1 /* DataStack+DataSources.swift in Sources */,
|
||||
B5E5FA4E22D162F400330931 /* ObjectSnapshot.swift in Sources */,
|
||||
B5BF7FC1234D7B2E0070E741 /* LiveObject.swift in Sources */,
|
||||
B5E1B5A81CAA49E2007FD580 /* CSDataStack+Migrating.swift in Sources */,
|
||||
B50132302346B76E00FC238B /* Internals.FetchedDiffableDataSourceSnapshotDelegate.swift in Sources */,
|
||||
B5D339F11E94AF5800C880DE /* CoreStoreStrings.swift in Sources */,
|
||||
@@ -2061,6 +2085,7 @@
|
||||
B509C7F41E54511B0061C547 /* ImportableAttributeType.swift in Sources */,
|
||||
B5E84F0E1AFF847B0064E85B /* Tweak.swift in Sources */,
|
||||
B5E1B5931CAA0C15007FD580 /* CSObjectMonitor.swift in Sources */,
|
||||
B5277677234F265F0056BE9F /* Internals.SharedNotificationObserver.swift in Sources */,
|
||||
B5ECDC291CA81CC700C7F112 /* CSDataStack+Transaction.swift in Sources */,
|
||||
B56923F01EB827F6007C4DC9 /* XcodeSchemaMappingProvider.swift in Sources */,
|
||||
B5F335582348D75D00FD649F /* LiveResult.swift in Sources */,
|
||||
@@ -2103,12 +2128,14 @@
|
||||
B50EE14223473C92009B8C47 /* CoreStoreObject+DataSources.swift in Sources */,
|
||||
B5E84EF61AFF846E0064E85B /* DataStack+Transaction.swift in Sources */,
|
||||
B5FEC18E1C9166E200532541 /* NSPersistentStore+Setup.swift in Sources */,
|
||||
B5BF7FCB234D80910070E741 /* Internals.LazyNonmutating.swift in Sources */,
|
||||
B596BBB61DD5BC67001DCDD9 /* FetchableSource.swift in Sources */,
|
||||
B5E1B5A21CAA4365007FD580 /* CSCoreStore+Observing.swift in Sources */,
|
||||
B5E84EDF1AFF84500064E85B /* DataStack.swift in Sources */,
|
||||
B53304AA230BA4F7007C2BD8 /* DynamicObjectMeta.swift in Sources */,
|
||||
B59AFF411C6593E400C0ABE2 /* NSPersistentStoreCoordinator+Setup.swift in Sources */,
|
||||
B5E84F231AFF84860064E85B /* ListMonitor.swift in Sources */,
|
||||
B5BF7FC6234D7E460070E741 /* ObjectSnapshot.swift in Sources */,
|
||||
B5E84EF71AFF846E0064E85B /* UnsafeDataTransaction.swift in Sources */,
|
||||
B56964D41B22FFAD0075EE4A /* DataStack+Migration.swift in Sources */,
|
||||
B5D339DD1E9489C700C880DE /* DynamicObject.swift in Sources */,
|
||||
@@ -2170,6 +2197,7 @@
|
||||
B53FBA041CAB300C00F0D40A /* CSMigrationType.swift in Sources */,
|
||||
B5E84EF41AFF846E0064E85B /* AsynchronousDataTransaction.swift in Sources */,
|
||||
B5831B701F34AC3400A9F647 /* AttributeProtocol.swift in Sources */,
|
||||
B5277672234F1AEB0056BE9F /* NSManagedObjectContext+Logging.swift in Sources */,
|
||||
B5DBE2CD1C9914A900B5CEFA /* CSCoreStore.swift in Sources */,
|
||||
B546F95D1C9A12B800D5AC55 /* CSSQliteStore.swift in Sources */,
|
||||
B5D339E71E9493A500C880DE /* Entity.swift in Sources */,
|
||||
@@ -2236,11 +2264,13 @@
|
||||
82BA18CE1C4BBD7100A0916E /* Internals.FetchedResultsControllerDelegate.swift in Sources */,
|
||||
B56923FB1EB82956007C4DC9 /* CSXcodeDataModelSchema.swift in Sources */,
|
||||
B55514EB1EED8BF900BAB888 /* From+Querying.swift in Sources */,
|
||||
B5BF7FC2234D7B2E0070E741 /* LiveObject.swift in Sources */,
|
||||
B596BBBC1DD5C39F001DCDD9 /* QueryableSource.swift in Sources */,
|
||||
B5ECDC011CA80CBA00C7F112 /* CSWhere.swift in Sources */,
|
||||
B5ECDC071CA8138100C7F112 /* CSOrderBy.swift in Sources */,
|
||||
B5E1B59A1CAA0C23007FD580 /* CSObjectObserver.swift in Sources */,
|
||||
B5519A601CA21954002BEF78 /* CSAsynchronousDataTransaction.swift in Sources */,
|
||||
B5277678234F265F0056BE9F /* Internals.SharedNotificationObserver.swift in Sources */,
|
||||
B52FD3AB1E3B3EF10001D919 /* NSManagedObject+Logging.swift in Sources */,
|
||||
B52F74421E9B8724005F3DAC /* UnsafeDataModelSchema.swift in Sources */,
|
||||
B51FE5AD1CD4D00300E54258 /* CoreStore+CustomDebugStringConvertible.swift in Sources */,
|
||||
@@ -2254,11 +2284,11 @@
|
||||
82BA18A51C4BBD2200A0916E /* CoreStore+Setup.swift in Sources */,
|
||||
82BA18BD1C4BBD4A00A0916E /* GroupBy.swift in Sources */,
|
||||
B5ECDC1F1CA81A2100C7F112 /* CSDataStack+Querying.swift in Sources */,
|
||||
B5BF7FCC234D80910070E741 /* Internals.LazyNonmutating.swift in Sources */,
|
||||
B5E294DE2349F8E7003E5956 /* SnapshotResult.swift in Sources */,
|
||||
B5C976E41C6C9F9A00B1AF90 /* UnsafeDataTransaction+Observing.swift in Sources */,
|
||||
B53FBA141CAB63CB00F0D40A /* Progress+ObjectiveC.swift in Sources */,
|
||||
B5831B761F34AC7A00A9F647 /* RelationshipProtocol.swift in Sources */,
|
||||
B5E5FA4F22D162F400330931 /* ObjectSnapshot.swift in Sources */,
|
||||
B5BF7FB3234C97910070E741 /* DiffableDataSource.swift in Sources */,
|
||||
B5E1B5AA1CAA49E2007FD580 /* CSDataStack+Migrating.swift in Sources */,
|
||||
B5D339F21E94AF5800C880DE /* CoreStoreStrings.swift in Sources */,
|
||||
@@ -2362,6 +2392,7 @@
|
||||
82BA18D31C4BBD7100A0916E /* NSManagedObjectContext+CoreStore.swift in Sources */,
|
||||
82BA18AD1C4BBD3100A0916E /* UnsafeDataTransaction.swift in Sources */,
|
||||
B546F96A1C9AF26D00D5AC55 /* CSInMemoryStore.swift in Sources */,
|
||||
B5277673234F1AEB0056BE9F /* NSManagedObjectContext+Logging.swift in Sources */,
|
||||
B5831B7B1F34ACBA00A9F647 /* Transformable.swift in Sources */,
|
||||
82BA18A81C4BBD2900A0916E /* CoreStoreLogger.swift in Sources */,
|
||||
B549F65F1E569C7400FBAB2D /* QueryableAttributeType.swift in Sources */,
|
||||
@@ -2387,6 +2418,7 @@
|
||||
B546F95E1C9A12B800D5AC55 /* CSSQliteStore.swift in Sources */,
|
||||
B5ECDC0D1CA8161B00C7F112 /* CSGroupBy.swift in Sources */,
|
||||
B5D339E81E9493A500C880DE /* Entity.swift in Sources */,
|
||||
B5BF7FC7234D7E460070E741 /* ObjectSnapshot.swift in Sources */,
|
||||
82BA18CC1C4BBD6400A0916E /* Progress+Convenience.swift in Sources */,
|
||||
B5BF7FB8234C97CE0070E741 /* DiffableDataSource.TableView.swift in Sources */,
|
||||
82BA18C01C4BBD5300A0916E /* DataStack+Observing.swift in Sources */,
|
||||
@@ -2448,11 +2480,13 @@
|
||||
B52DD1951BE1F92500949AFE /* CoreStoreError.swift in Sources */,
|
||||
B56923FD1EB82956007C4DC9 /* CSXcodeDataModelSchema.swift in Sources */,
|
||||
B55514ED1EED8BF900BAB888 /* From+Querying.swift in Sources */,
|
||||
B5BF7FC4234D7B2E0070E741 /* LiveObject.swift in Sources */,
|
||||
B596BBBE1DD5C39F001DCDD9 /* QueryableSource.swift in Sources */,
|
||||
B546F9601C9A12B800D5AC55 /* CSSQliteStore.swift in Sources */,
|
||||
B5ECDC0F1CA8161B00C7F112 /* CSGroupBy.swift in Sources */,
|
||||
B5ECDC211CA81A2100C7F112 /* CSDataStack+Querying.swift in Sources */,
|
||||
B52DD1C21BE1F94600949AFE /* Internals.MigrationManager.swift in Sources */,
|
||||
B527767A234F265F0056BE9F /* Internals.SharedNotificationObserver.swift in Sources */,
|
||||
B52FD3AD1E3B3EF10001D919 /* NSManagedObject+Logging.swift in Sources */,
|
||||
B52F74441E9B8724005F3DAC /* UnsafeDataModelSchema.swift in Sources */,
|
||||
B5ECDC2D1CA81CC700C7F112 /* CSDataStack+Transaction.swift in Sources */,
|
||||
@@ -2466,11 +2500,11 @@
|
||||
B546F95B1C99B17400D5AC55 /* CSCoreStore+Setup.swift in Sources */,
|
||||
B52DD1A11BE1F92C00949AFE /* DataStack+Transaction.swift in Sources */,
|
||||
B5220E1C1D130801009BC71E /* Internals.FetchedResultsControllerDelegate.swift in Sources */,
|
||||
B5BF7FCE234D80910070E741 /* Internals.LazyNonmutating.swift in Sources */,
|
||||
B5E294E02349F8E7003E5956 /* SnapshotResult.swift in Sources */,
|
||||
B52DD19E1BE1F92C00949AFE /* AsynchronousDataTransaction.swift in Sources */,
|
||||
B5831B781F34AC7A00A9F647 /* RelationshipProtocol.swift in Sources */,
|
||||
B52DD1981BE1F92500949AFE /* CoreStore+Setup.swift in Sources */,
|
||||
B5E5FA5122D162F400330931 /* ObjectSnapshot.swift in Sources */,
|
||||
B5BF7FB5234C97910070E741 /* DiffableDataSource.swift in Sources */,
|
||||
B5D339F41E94AF5800C880DE /* CoreStoreStrings.swift in Sources */,
|
||||
B5220E241D13085E009BC71E /* NSFetchedResultsController+Convenience.swift in Sources */,
|
||||
@@ -2574,6 +2608,7 @@
|
||||
B5ECDC331CA81CDC00C7F112 /* CSCoreStore+Transaction.swift in Sources */,
|
||||
B52DD1BB1BE1F94000949AFE /* MigrationType.swift in Sources */,
|
||||
B5831B7D1F34ACBA00A9F647 /* Transformable.swift in Sources */,
|
||||
B5277675234F1AEB0056BE9F /* NSManagedObjectContext+Logging.swift in Sources */,
|
||||
B52DD1C91BE1F94600949AFE /* NSManagedObjectContext+Transaction.swift in Sources */,
|
||||
B5220E151D130663009BC71E /* CoreStore+Observing.swift in Sources */,
|
||||
B549F6611E569C7400FBAB2D /* QueryableAttributeType.swift in Sources */,
|
||||
@@ -2599,6 +2634,7 @@
|
||||
B5AEFAB81C9962AE00AD137F /* CoreStoreBridge.swift in Sources */,
|
||||
B598514B1C90289F00C99590 /* NSPersistentStoreCoordinator+Setup.swift in Sources */,
|
||||
B5D339EA1E9493A500C880DE /* Entity.swift in Sources */,
|
||||
B5BF7FC9234D7E460070E741 /* ObjectSnapshot.swift in Sources */,
|
||||
B52DD1AA1BE1F93500949AFE /* TypeErasedClauses.swift in Sources */,
|
||||
B5BF7FBA234C97CE0070E741 /* DiffableDataSource.TableView.swift in Sources */,
|
||||
B53FBA021CAB2D2F00F0D40A /* CSMigrationResult.swift in Sources */,
|
||||
@@ -2660,11 +2696,13 @@
|
||||
B5ECDC021CA80CBA00C7F112 /* CSWhere.swift in Sources */,
|
||||
B56923FC1EB82956007C4DC9 /* CSXcodeDataModelSchema.swift in Sources */,
|
||||
B55514EC1EED8BF900BAB888 /* From+Querying.swift in Sources */,
|
||||
B5BF7FC3234D7B2E0070E741 /* LiveObject.swift in Sources */,
|
||||
B596BBBD1DD5C39F001DCDD9 /* QueryableSource.swift in Sources */,
|
||||
B5ECDC081CA8138100C7F112 /* CSOrderBy.swift in Sources */,
|
||||
B5E1B59B1CAA0C23007FD580 /* CSObjectObserver.swift in Sources */,
|
||||
B5519A611CA21954002BEF78 /* CSAsynchronousDataTransaction.swift in Sources */,
|
||||
B5FE4DAE1C85D44E00FA6A91 /* SQLiteStore.swift in Sources */,
|
||||
B5277679234F265F0056BE9F /* Internals.SharedNotificationObserver.swift in Sources */,
|
||||
B52FD3AC1E3B3EF10001D919 /* NSManagedObject+Logging.swift in Sources */,
|
||||
B52F74431E9B8724005F3DAC /* UnsafeDataModelSchema.swift in Sources */,
|
||||
B51FE5AE1CD4D00300E54258 /* CoreStore+CustomDebugStringConvertible.swift in Sources */,
|
||||
@@ -2678,11 +2716,11 @@
|
||||
B56321831BD65216006C9394 /* CoreStore+Setup.swift in Sources */,
|
||||
B5ECDC201CA81A2100C7F112 /* CSDataStack+Querying.swift in Sources */,
|
||||
B5C976E51C6C9F9B00B1AF90 /* UnsafeDataTransaction+Observing.swift in Sources */,
|
||||
B5BF7FCD234D80910070E741 /* Internals.LazyNonmutating.swift in Sources */,
|
||||
B5E294DF2349F8E7003E5956 /* SnapshotResult.swift in Sources */,
|
||||
B53FBA151CAB63CB00F0D40A /* Progress+ObjectiveC.swift in Sources */,
|
||||
B5E1B5AB1CAA49E2007FD580 /* CSDataStack+Migrating.swift in Sources */,
|
||||
B5831B771F34AC7A00A9F647 /* RelationshipProtocol.swift in Sources */,
|
||||
B5E5FA5022D162F400330931 /* ObjectSnapshot.swift in Sources */,
|
||||
B5BF7FB4234C97910070E741 /* DiffableDataSource.swift in Sources */,
|
||||
B5D339F31E94AF5800C880DE /* CoreStoreStrings.swift in Sources */,
|
||||
B5E1B5A01CAA2568007FD580 /* CSDataStack+Observing.swift in Sources */,
|
||||
@@ -2786,6 +2824,7 @@
|
||||
B56321B11BD6521C006C9394 /* NSManagedObjectContext+CoreStore.swift in Sources */,
|
||||
B563218D1BD65216006C9394 /* CoreStore+Transaction.swift in Sources */,
|
||||
B546F96B1C9AF26D00D5AC55 /* CSInMemoryStore.swift in Sources */,
|
||||
B5277674234F1AEB0056BE9F /* NSManagedObjectContext+Logging.swift in Sources */,
|
||||
B5831B7C1F34ACBA00A9F647 /* Transformable.swift in Sources */,
|
||||
B563218B1BD65216006C9394 /* UnsafeDataTransaction.swift in Sources */,
|
||||
B549F6601E569C7400FBAB2D /* QueryableAttributeType.swift in Sources */,
|
||||
@@ -2811,6 +2850,7 @@
|
||||
B546F95F1C9A12B800D5AC55 /* CSSQliteStore.swift in Sources */,
|
||||
B5ECDC0E1CA8161B00C7F112 /* CSGroupBy.swift in Sources */,
|
||||
B5D339E91E9493A500C880DE /* Entity.swift in Sources */,
|
||||
B5BF7FC8234D7E460070E741 /* ObjectSnapshot.swift in Sources */,
|
||||
B56321A41BD65216006C9394 /* CoreStore+Migration.swift in Sources */,
|
||||
B5BF7FB9234C97CE0070E741 /* DiffableDataSource.TableView.swift in Sources */,
|
||||
B56321A01BD65216006C9394 /* ObjectObserver.swift in Sources */,
|
||||
|
||||
@@ -50,5 +50,7 @@
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
<key>UIUserInterfaceStyle</key>
|
||||
<string>light</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -19,30 +19,36 @@ struct SwiftUIView: View {
|
||||
|
||||
@ObservedObject
|
||||
var palettes: LiveList<Palette>
|
||||
|
||||
@State
|
||||
private var needsShowAlert = false
|
||||
|
||||
@available(iOS 13.0.0, *)
|
||||
struct ColorCell: View {
|
||||
|
||||
@ObservedObject
|
||||
var palette: LiveObject<Palette>
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
Color(palette.color)
|
||||
.cornerRadius(5)
|
||||
.frame(width: 30, height: 30, alignment: .leading)
|
||||
Text(palette.colorText)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
List {
|
||||
ForEach(palettes.snapshot.sectionIdentifiers, id: \.self) { (sectionID: String) in
|
||||
ForEach(palettes.sections, id: \.self) { (sectionID) in
|
||||
Section(header: Text(sectionID)) {
|
||||
ForEach(self.palettes.snapshot[section: sectionID], id: \.self) { palette in
|
||||
ForEach(self.palettes[section: sectionID], id: \.self) { palette in
|
||||
NavigationLink(
|
||||
destination: DetailView(palette: palette),
|
||||
label: {
|
||||
HStack {
|
||||
Color(palette.color)
|
||||
.cornerRadius(5)
|
||||
.frame(width: 30, height: 30, alignment: .leading)
|
||||
Text(palette.colorText)
|
||||
}
|
||||
}
|
||||
label: { ColorCell(palette: palette) }
|
||||
)
|
||||
}
|
||||
.onDelete { itemIndices in
|
||||
let objectsToDelete = self.palettes.snapshot[section: sectionID, itemIndices: itemIndices]
|
||||
let objectsToDelete = self.palettes[section: sectionID, itemIndices: itemIndices].map({ $0.object })
|
||||
self.dataStack.perform(
|
||||
asynchronous: { transaction in
|
||||
|
||||
@@ -91,6 +97,9 @@ struct SwiftUIView: View {
|
||||
}
|
||||
.colorScheme(.dark)
|
||||
}
|
||||
|
||||
@State
|
||||
private var needsShowAlert = false
|
||||
}
|
||||
|
||||
@available(iOS 13.0.0, *)
|
||||
@@ -98,19 +107,20 @@ struct DetailView: View {
|
||||
|
||||
@Environment(\.dataStack)
|
||||
var dataStack: DataStack
|
||||
|
||||
@ObservedObject var palette: Palette
|
||||
|
||||
@ObservedObject
|
||||
var palette: LiveObject<Palette>
|
||||
|
||||
@State var hue: Float = 0
|
||||
@State var saturation: Float = 0
|
||||
@State var brightness: Float = 0
|
||||
|
||||
init(palette: Palette) {
|
||||
init(palette: LiveObject<Palette>) {
|
||||
|
||||
self.palette = palette
|
||||
self.hue = Float(palette.hue.value)
|
||||
self.saturation = palette.saturation.value
|
||||
self.brightness = palette.brightness.value
|
||||
self.hue = Float(palette.hue)
|
||||
self.saturation = palette.saturation
|
||||
self.brightness = palette.brightness
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
|
||||
@@ -43,7 +43,12 @@ public protocol DynamicObject: AnyObject {
|
||||
Used internally by CoreStore. Do not call directly.
|
||||
*/
|
||||
static func cs_forceCreate(entityDescription: NSEntityDescription, into context: NSManagedObjectContext, assignTo store: NSPersistentStore) -> Self
|
||||
|
||||
|
||||
/**
|
||||
Used internally by CoreStore. Do not call directly.
|
||||
*/
|
||||
static func cs_snapshotDictionary(id: ObjectID, context: NSManagedObjectContext) -> [String: Any]
|
||||
|
||||
/**
|
||||
Used internally by CoreStore. Do not call directly.
|
||||
*/
|
||||
@@ -97,6 +102,13 @@ extension NSManagedObject: DynamicObject {
|
||||
}
|
||||
return object
|
||||
}
|
||||
|
||||
public class func cs_snapshotDictionary(id: ObjectID, context: NSManagedObjectContext) -> [String: Any] {
|
||||
|
||||
let object = context.fetchExisting(id)! as Self
|
||||
let rawObject = object.cs_toRaw()
|
||||
return rawObject.dictionaryWithValues(forKeys: rawObject.entity.properties.map({ $0.name }))
|
||||
}
|
||||
|
||||
public class func cs_fromRaw(object: NSManagedObject) -> Self {
|
||||
|
||||
@@ -119,16 +131,6 @@ extension NSManagedObject: DynamicObject {
|
||||
}
|
||||
}
|
||||
|
||||
extension DynamicObject where Self: NSManagedObject {
|
||||
|
||||
// MARK: Public
|
||||
|
||||
public func createSnapshot() -> ObjectSnapshot<Self> {
|
||||
|
||||
return ObjectSnapshot(from: self)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - CoreStoreObject
|
||||
|
||||
@@ -146,6 +148,43 @@ extension CoreStoreObject {
|
||||
}
|
||||
return self.cs_fromRaw(object: object)
|
||||
}
|
||||
|
||||
public class func cs_snapshotDictionary(id: ObjectID, context: NSManagedObjectContext) -> [String: Any] {
|
||||
|
||||
func initializeAttributes(mirror: Mirror, object: Self, into attributes: inout [KeyPathString: Any]) {
|
||||
|
||||
if let superClassMirror = mirror.superclassMirror {
|
||||
|
||||
initializeAttributes(
|
||||
mirror: superClassMirror,
|
||||
object: object,
|
||||
into: &attributes
|
||||
)
|
||||
}
|
||||
for child in mirror.children {
|
||||
|
||||
switch child.value {
|
||||
|
||||
case let property as AttributeProtocol:
|
||||
attributes[property.keyPath] = property.valueForSnapshot
|
||||
|
||||
case let property as RelationshipProtocol:
|
||||
attributes[property.keyPath] = property.valueForSnapshot
|
||||
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
let object = context.fetchExisting(id)! as Self
|
||||
var values: [KeyPathString: Any] = [:]
|
||||
initializeAttributes(
|
||||
mirror: Mirror(reflecting: object),
|
||||
object: object,
|
||||
into: &values
|
||||
)
|
||||
return values
|
||||
}
|
||||
|
||||
public class func cs_fromRaw(object: NSManagedObject) -> Self {
|
||||
|
||||
@@ -181,13 +220,3 @@ extension CoreStoreObject {
|
||||
return self.rawObject!
|
||||
}
|
||||
}
|
||||
|
||||
extension DynamicObject where Self: CoreStoreObject {
|
||||
|
||||
// MARK: Public
|
||||
|
||||
public func createSnapshot() -> ObjectSnapshot<Self> {
|
||||
|
||||
return ObjectSnapshot(from: self)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,88 +40,111 @@ import AppKit
|
||||
|
||||
extension Internals {
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
internal typealias DiffableDataSourceSnapshot = _Internal_DiffableDataSourceSnapshot
|
||||
|
||||
|
||||
// MARK: - FallbackDiffableDataSourceSnapshot
|
||||
// MARK: - DiffableDataSourceSnapshot
|
||||
|
||||
// Implementation based on https://github.com/ra1028/DiffableDataSources
|
||||
internal struct FallbackDiffableDataSourceSnapshot: DiffableDataSourceSnapshot {
|
||||
internal struct DiffableDataSourceSnapshot {
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
init(sections: [NSFetchedResultsSectionInfo]) {
|
||||
|
||||
self.structure = .init(sections: sections)
|
||||
}
|
||||
|
||||
// MARK: DiffableDataSourceSnapshot
|
||||
internal let nextStateTag: UUID
|
||||
|
||||
init() {
|
||||
|
||||
self.structure = .init()
|
||||
self.nextStateTag = .init()
|
||||
}
|
||||
|
||||
init(sections: [NSFetchedResultsSectionInfo], previousStateTag: UUID, nextStateTag: UUID) {
|
||||
|
||||
self.structure = .init(sections: sections, previousStateTag: previousStateTag)
|
||||
self.nextStateTag = nextStateTag
|
||||
}
|
||||
|
||||
var numberOfItems: Int {
|
||||
|
||||
return self.itemIdentifiers.count
|
||||
return self.structure.allItemIDs.count
|
||||
}
|
||||
|
||||
var numberOfSections: Int {
|
||||
|
||||
return self.sectionIdentifiers.count
|
||||
return self.structure.allSectionIDs.count
|
||||
}
|
||||
|
||||
var sectionIdentifiers: [NSString] {
|
||||
var allSectionIDs: [String] {
|
||||
|
||||
return self.structure.allSectionIDs
|
||||
}
|
||||
|
||||
var itemIdentifiers: [NSManagedObjectID] {
|
||||
var allSectionStateIDs: [SectionStateID] {
|
||||
|
||||
self.structure.allItemIDs
|
||||
return self.structure.allSectionStateIDs
|
||||
}
|
||||
|
||||
func numberOfItems(inSection identifier: NSString) -> Int {
|
||||
var allItemIDs: [NSManagedObjectID] {
|
||||
|
||||
return self.itemIdentifiers(inSection: identifier).count
|
||||
return self.structure.allItemIDs
|
||||
}
|
||||
|
||||
func itemIdentifiers(inSection identifier: NSString) -> [NSManagedObjectID] {
|
||||
var allItemStateIDs: [ItemStateID] {
|
||||
|
||||
return self.structure.allItemStateIDs
|
||||
}
|
||||
|
||||
func numberOfItems(inSection identifier: String) -> Int {
|
||||
|
||||
return self.itemIDs(inSection: identifier).count
|
||||
}
|
||||
|
||||
func itemIDs(inSection identifier: String) -> [NSManagedObjectID] {
|
||||
|
||||
return self.structure.items(in: identifier)
|
||||
}
|
||||
|
||||
func sectionIdentifier(containingItem identifier: NSManagedObjectID) -> NSString? {
|
||||
func itemStateIDs(inSection identifier: String) -> [ItemStateID] {
|
||||
|
||||
return self.structure.itemStateIDs(in: identifier)
|
||||
}
|
||||
|
||||
func sectionIDs(containingItem identifier: NSManagedObjectID) -> String? {
|
||||
|
||||
return self.structure.section(containing: identifier)
|
||||
}
|
||||
|
||||
func indexOfItem(_ identifier: NSManagedObjectID) -> Int? {
|
||||
func sectionStateIDs(containingItem identifier: NSManagedObjectID) -> SectionStateID? {
|
||||
|
||||
return self.itemIdentifiers.firstIndex(of: identifier)
|
||||
return self.structure.sectionStateID(containing: identifier)
|
||||
}
|
||||
|
||||
func indexOfSection(_ identifier: NSString) -> Int? {
|
||||
func indexOfItemID(_ identifier: NSManagedObjectID) -> Int? {
|
||||
|
||||
return self.sectionIdentifiers.firstIndex(of: identifier)
|
||||
return self.structure.allItemIDs.firstIndex(of: identifier)
|
||||
}
|
||||
|
||||
mutating func appendItems(_ identifiers: [NSManagedObjectID], toSection sectionIdentifier: NSString?) {
|
||||
func indexOfSectionID(_ identifier: String) -> Int? {
|
||||
|
||||
self.structure.append(itemIDs: identifiers, to: sectionIdentifier)
|
||||
return self.structure.allSectionIDs.firstIndex(of: identifier)
|
||||
}
|
||||
|
||||
mutating func insertItems(_ identifiers: [NSManagedObjectID], beforeItem beforeIdentifier: NSManagedObjectID) {
|
||||
func itemIDs(where stateCondition: @escaping (UUID) -> Bool) -> [NSManagedObjectID] {
|
||||
|
||||
self.structure.insert(itemIDs: identifiers, before: beforeIdentifier)
|
||||
return self.structure.itemsIDs(where: stateCondition)
|
||||
}
|
||||
|
||||
mutating func insertItems(_ identifiers: [NSManagedObjectID], afterItem afterIdentifier: NSManagedObjectID) {
|
||||
mutating func appendItems(_ identifiers: [NSManagedObjectID], toSection sectionIdentifier: String?, nextStateTag: UUID) {
|
||||
|
||||
self.structure.insert(itemIDs: identifiers, after: afterIdentifier)
|
||||
self.structure.append(itemIDs: identifiers, to: sectionIdentifier, nextStateTag: nextStateTag)
|
||||
}
|
||||
|
||||
mutating func insertItems(_ identifiers: [NSManagedObjectID], beforeItem beforeIdentifier: NSManagedObjectID, nextStateTag: UUID) {
|
||||
|
||||
self.structure.insert(itemIDs: identifiers, before: beforeIdentifier, nextStateTag: nextStateTag)
|
||||
}
|
||||
|
||||
mutating func insertItems(_ identifiers: [NSManagedObjectID], afterItem afterIdentifier: NSManagedObjectID, nextStateTag: UUID) {
|
||||
|
||||
self.structure.insert(itemIDs: identifiers, after: afterIdentifier, nextStateTag: nextStateTag)
|
||||
}
|
||||
|
||||
mutating func deleteItems(_ identifiers: [NSManagedObjectID]) {
|
||||
@@ -144,44 +167,44 @@ extension Internals {
|
||||
self.structure.move(itemID: identifier, after: toIdentifier)
|
||||
}
|
||||
|
||||
mutating func reloadItems(_ identifiers: [NSManagedObjectID]) {
|
||||
mutating func reloadItems<S: Sequence>(_ identifiers: S, nextStateTag: UUID) where S.Element == NSManagedObjectID {
|
||||
|
||||
self.structure.update(itemIDs: identifiers)
|
||||
self.structure.update(itemIDs: identifiers, nextStateTag: nextStateTag)
|
||||
}
|
||||
|
||||
mutating func appendSections(_ identifiers: [NSString]) {
|
||||
mutating func appendSections(_ identifiers: [String], nextStateTag: UUID) {
|
||||
|
||||
self.structure.append(sectionIDs: identifiers)
|
||||
self.structure.append(sectionIDs: identifiers, nextStateTag: nextStateTag)
|
||||
}
|
||||
|
||||
mutating func insertSections(_ identifiers: [NSString], beforeSection toIdentifier: NSString) {
|
||||
mutating func insertSections(_ identifiers: [String], beforeSection toIdentifier: String, nextStateTag: UUID) {
|
||||
|
||||
self.structure.insert(sectionIDs: identifiers, before: toIdentifier)
|
||||
self.structure.insert(sectionIDs: identifiers, before: toIdentifier, nextStateTag: nextStateTag)
|
||||
}
|
||||
|
||||
mutating func insertSections(_ identifiers: [NSString], afterSection toIdentifier: NSString) {
|
||||
mutating func insertSections(_ identifiers: [String], afterSection toIdentifier: String, nextStateTag: UUID) {
|
||||
|
||||
self.structure.insert(sectionIDs: identifiers, after: toIdentifier)
|
||||
self.structure.insert(sectionIDs: identifiers, after: toIdentifier, nextStateTag: nextStateTag)
|
||||
}
|
||||
|
||||
mutating func deleteSections(_ identifiers: [NSString]) {
|
||||
mutating func deleteSections(_ identifiers: [String]) {
|
||||
|
||||
self.structure.remove(sectionIDs: identifiers)
|
||||
}
|
||||
|
||||
mutating func moveSection(_ identifier: NSString, beforeSection toIdentifier: NSString) {
|
||||
mutating func moveSection(_ identifier: String, beforeSection toIdentifier: String) {
|
||||
|
||||
self.structure.move(sectionID: identifier, before: toIdentifier)
|
||||
}
|
||||
|
||||
mutating func moveSection(_ identifier: NSString, afterSection toIdentifier: NSString) {
|
||||
mutating func moveSection(_ identifier: String, afterSection toIdentifier: String) {
|
||||
|
||||
self.structure.move(sectionID: identifier, after: toIdentifier)
|
||||
}
|
||||
|
||||
mutating func reloadSections(_ identifiers: [NSString]) {
|
||||
mutating func reloadSections<S: Sequence>(_ identifiers: S, nextStateTag: UUID) where S.Element == String {
|
||||
|
||||
self.structure.update(sectionIDs: identifiers)
|
||||
self.structure.update(sectionIDs: identifiers, nextStateTag: nextStateTag)
|
||||
}
|
||||
|
||||
|
||||
@@ -190,9 +213,54 @@ extension Internals {
|
||||
private var structure: BackingStructure
|
||||
|
||||
|
||||
// MARK: - ItemStateID
|
||||
|
||||
internal struct ItemStateID: Identifiable, Equatable {
|
||||
|
||||
let stateTag: UUID
|
||||
|
||||
init(id: NSManagedObjectID, stateTag: UUID) {
|
||||
|
||||
self.id = id
|
||||
self.stateTag = stateTag
|
||||
}
|
||||
|
||||
func isContentEqual(to source: ItemStateID) -> Bool {
|
||||
|
||||
return self.id == source.id && self.stateTag == source.stateTag
|
||||
}
|
||||
|
||||
// MARK: Identifiable
|
||||
|
||||
let id: NSManagedObjectID
|
||||
}
|
||||
|
||||
|
||||
// MARK: - SectionStateID
|
||||
|
||||
internal struct SectionStateID: Identifiable, Equatable {
|
||||
|
||||
let stateTag: UUID
|
||||
|
||||
init(id: String, stateTag: UUID) {
|
||||
self.id = id
|
||||
self.stateTag = stateTag
|
||||
}
|
||||
|
||||
func isContentEqual(to source: SectionStateID) -> Bool {
|
||||
|
||||
return self.id == source.id && self.stateTag == source.stateTag
|
||||
}
|
||||
|
||||
// MARK: Identifiable
|
||||
|
||||
let id: String
|
||||
}
|
||||
|
||||
|
||||
// MARK: - BackingStructure
|
||||
|
||||
internal struct BackingStructure {
|
||||
fileprivate struct BackingStructure {
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
@@ -203,31 +271,41 @@ extension Internals {
|
||||
self.sections = []
|
||||
}
|
||||
|
||||
init(sections: [NSFetchedResultsSectionInfo]) {
|
||||
init(sections: [NSFetchedResultsSectionInfo], previousStateTag: UUID) {
|
||||
|
||||
self.sections = sections.map {
|
||||
|
||||
Section(
|
||||
id: $0.name as NSString,
|
||||
id: $0.name,
|
||||
items: $0.objects?
|
||||
.compactMap({ ($0 as? NSManagedObject)?.objectID })
|
||||
.map(Item.init(id:)) ?? [],
|
||||
isReloaded: false
|
||||
.map({ Item(id: $0, stateTag: previousStateTag) }) ?? [],
|
||||
stateTag: previousStateTag
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
var allSectionIDs: [NSString] {
|
||||
var allSectionIDs: [String] {
|
||||
|
||||
return self.sections.map({ $0.id })
|
||||
}
|
||||
|
||||
var allSectionStateIDs: [SectionStateID] {
|
||||
|
||||
return self.sections.map({ $0.stateID })
|
||||
}
|
||||
|
||||
var allItemIDs: [NSManagedObjectID] {
|
||||
|
||||
return self.sections.lazy.flatMap({ $0.elements }).map({ $0.id })
|
||||
}
|
||||
|
||||
func items(in sectionID: NSString) -> [NSManagedObjectID] {
|
||||
var allItemStateIDs: [ItemStateID] {
|
||||
|
||||
return self.sections.lazy.flatMap({ $0.elements }).map({ $0.stateID })
|
||||
}
|
||||
|
||||
func items(in sectionID: String) -> [NSManagedObjectID] {
|
||||
|
||||
guard let sectionIndex = self.sectionIndex(of: sectionID) else {
|
||||
|
||||
@@ -236,12 +314,33 @@ extension Internals {
|
||||
return self.sections[sectionIndex].elements.map({ $0.id })
|
||||
}
|
||||
|
||||
func section(containing itemID: NSManagedObjectID) -> NSString? {
|
||||
func itemsIDs(where stateCondition: @escaping (UUID) -> Bool) -> [NSManagedObjectID] {
|
||||
|
||||
return self.sections.lazy
|
||||
.flatMap({ $0.elements.filter({ stateCondition($0.stateTag) }) })
|
||||
.map({ $0.id })
|
||||
}
|
||||
|
||||
func itemStateIDs(in sectionID: String) -> [ItemStateID] {
|
||||
|
||||
guard let sectionIndex = self.sectionIndex(of: sectionID) else {
|
||||
|
||||
Internals.abort("Section \"\(sectionID)\" does not exist")
|
||||
}
|
||||
return self.sections[sectionIndex].elements.map({ $0.stateID })
|
||||
}
|
||||
|
||||
func section(containing itemID: NSManagedObjectID) -> String? {
|
||||
|
||||
return self.itemPositionMap()[itemID]?.section.id
|
||||
}
|
||||
|
||||
mutating func append(itemIDs: [NSManagedObjectID], to sectionID: NSString?) {
|
||||
func sectionStateID(containing itemID: NSManagedObjectID) -> SectionStateID? {
|
||||
|
||||
return self.itemPositionMap()[itemID]?.section.stateID
|
||||
}
|
||||
|
||||
mutating func append(itemIDs: [NSManagedObjectID], to sectionID: String?, nextStateTag: UUID) {
|
||||
|
||||
let index: Array<Section>.Index
|
||||
if let sectionID = sectionID {
|
||||
@@ -261,23 +360,22 @@ extension Internals {
|
||||
}
|
||||
index = section.index(before: section.endIndex)
|
||||
}
|
||||
|
||||
let items = itemIDs.lazy.map(Item.init)
|
||||
let items = itemIDs.lazy.map({ Item(id: $0, stateTag: nextStateTag) })
|
||||
self.sections[index].elements.append(contentsOf: items)
|
||||
}
|
||||
|
||||
mutating func insert(itemIDs: [NSManagedObjectID], before beforeItemID: NSManagedObjectID) {
|
||||
mutating func insert(itemIDs: [NSManagedObjectID], before beforeItemID: NSManagedObjectID, nextStateTag: UUID) {
|
||||
|
||||
guard let itemPosition = self.itemPositionMap()[beforeItemID] else {
|
||||
|
||||
Internals.abort("Item \(beforeItemID) does not exist")
|
||||
}
|
||||
let items = itemIDs.lazy.map(Item.init)
|
||||
let items = itemIDs.lazy.map({ Item(id: $0, stateTag: nextStateTag) })
|
||||
self.sections[itemPosition.sectionIndex].elements
|
||||
.insert(contentsOf: items, at: itemPosition.itemRelativeIndex)
|
||||
}
|
||||
|
||||
mutating func insert(itemIDs: [NSManagedObjectID], after afterItemID: NSManagedObjectID) {
|
||||
mutating func insert(itemIDs: [NSManagedObjectID], after afterItemID: NSManagedObjectID, nextStateTag: UUID) {
|
||||
|
||||
guard let itemPosition = self.itemPositionMap()[afterItemID] else {
|
||||
|
||||
@@ -285,7 +383,7 @@ extension Internals {
|
||||
}
|
||||
let itemIndex = self.sections[itemPosition.sectionIndex].elements
|
||||
.index(after: itemPosition.itemRelativeIndex)
|
||||
let items = itemIDs.lazy.map(Item.init)
|
||||
let items = itemIDs.lazy.map({ Item(id: $0, stateTag: nextStateTag) })
|
||||
self.sections[itemPosition.sectionIndex].elements
|
||||
.insert(contentsOf: items, at: itemIndex)
|
||||
}
|
||||
@@ -351,47 +449,48 @@ extension Internals {
|
||||
.insert(removed, at: itemIndex)
|
||||
}
|
||||
|
||||
mutating func update(itemIDs: [NSManagedObjectID]) {
|
||||
mutating func update<S: Sequence>(itemIDs: S, nextStateTag: UUID) where S.Element == NSManagedObjectID {
|
||||
|
||||
let itemPositionMap = self.itemPositionMap()
|
||||
for itemID in itemIDs {
|
||||
|
||||
guard let itemPosition = itemPositionMap[itemID] else {
|
||||
|
||||
Internals.abort("Item \(itemID) does not exist")
|
||||
continue
|
||||
}
|
||||
self.sections[itemPosition.sectionIndex].elements[itemPosition.itemRelativeIndex].isReloaded = true
|
||||
self.sections[itemPosition.sectionIndex]
|
||||
.elements[itemPosition.itemRelativeIndex].stateTag = nextStateTag
|
||||
}
|
||||
}
|
||||
|
||||
mutating func append(sectionIDs: [NSString]) {
|
||||
mutating func append(sectionIDs: [String], nextStateTag: UUID) {
|
||||
|
||||
let newSections = sectionIDs.lazy.map(Section.init)
|
||||
let newSections = sectionIDs.lazy.map({ Section(id: $0, stateTag: nextStateTag) })
|
||||
self.sections.append(contentsOf: newSections)
|
||||
}
|
||||
|
||||
mutating func insert(sectionIDs: [NSString], before beforeSectionID: NSString) {
|
||||
mutating func insert(sectionIDs: [String], before beforeSectionID: String, nextStateTag: UUID) {
|
||||
|
||||
guard let sectionIndex = self.sectionIndex(of: beforeSectionID) else {
|
||||
|
||||
Internals.abort("Section \"\(beforeSectionID)\" does not exist")
|
||||
}
|
||||
let newSections = sectionIDs.lazy.map(Section.init)
|
||||
let newSections = sectionIDs.lazy.map({ Section(id: $0, stateTag: nextStateTag) })
|
||||
self.sections.insert(contentsOf: newSections, at: sectionIndex)
|
||||
}
|
||||
|
||||
mutating func insert(sectionIDs: [NSString], after afterSectionID: NSString) {
|
||||
mutating func insert(sectionIDs: [String], after afterSectionID: String, nextStateTag: UUID) {
|
||||
|
||||
guard let beforeIndex = self.sectionIndex(of: afterSectionID) else {
|
||||
|
||||
Internals.abort("Section \"\(afterSectionID)\" does not exist")
|
||||
}
|
||||
let sectionIndex = self.sections.index(after: beforeIndex)
|
||||
let newSections = sectionIDs.lazy.map(Section.init)
|
||||
let newSections = sectionIDs.lazy.map({ Section(id: $0, stateTag: nextStateTag) })
|
||||
self.sections.insert(contentsOf: newSections, at: sectionIndex)
|
||||
}
|
||||
|
||||
mutating func remove(sectionIDs: [NSString]) {
|
||||
mutating func remove(sectionIDs: [String]) {
|
||||
|
||||
for sectionID in sectionIDs {
|
||||
|
||||
@@ -399,7 +498,7 @@ extension Internals {
|
||||
}
|
||||
}
|
||||
|
||||
mutating func move(sectionID: NSString, before beforeSectionID: NSString) {
|
||||
mutating func move(sectionID: String, before beforeSectionID: String) {
|
||||
|
||||
guard let removed = self.remove(sectionID: sectionID) else {
|
||||
|
||||
@@ -412,7 +511,7 @@ extension Internals {
|
||||
self.sections.insert(removed, at: sectionIndex)
|
||||
}
|
||||
|
||||
mutating func move(sectionID: NSString, after afterSectionID: NSString) {
|
||||
mutating func move(sectionID: String, after afterSectionID: String) {
|
||||
|
||||
guard let removed = self.remove(sectionID: sectionID) else {
|
||||
|
||||
@@ -426,7 +525,7 @@ extension Internals {
|
||||
self.sections.insert(removed, at: sectionIndex)
|
||||
}
|
||||
|
||||
mutating func update(sectionIDs: [NSString]) {
|
||||
mutating func update<S: Sequence>(sectionIDs: S, nextStateTag: UUID) where S.Element == String {
|
||||
|
||||
for sectionID in sectionIDs {
|
||||
|
||||
@@ -434,14 +533,16 @@ extension Internals {
|
||||
|
||||
continue
|
||||
}
|
||||
self.sections[sectionIndex].isReloaded = true
|
||||
self.sections[sectionIndex].stateTag = nextStateTag
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private func sectionIndex(of sectionID: NSString) -> Array<Section>.Index? {
|
||||
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? {
|
||||
|
||||
return self.sections.firstIndex(where: { $0.id == sectionID })
|
||||
}
|
||||
@@ -458,7 +559,7 @@ extension Internals {
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
private mutating func remove(sectionID: NSString) -> Section? {
|
||||
private mutating func remove(sectionID: String) -> Section? {
|
||||
|
||||
guard let sectionIndex = self.sectionIndex(of: sectionID) else {
|
||||
|
||||
@@ -486,63 +587,63 @@ extension Internals {
|
||||
|
||||
// MARK: - Item
|
||||
|
||||
internal struct Item: Identifiable, Equatable {
|
||||
fileprivate struct Item: Identifiable, Equatable {
|
||||
|
||||
var isReloaded: Bool
|
||||
var stateTag: UUID
|
||||
|
||||
init(id: NSManagedObjectID, isReloaded: Bool) {
|
||||
init(id: NSManagedObjectID, stateTag: UUID) {
|
||||
|
||||
self.id = id
|
||||
self.isReloaded = isReloaded
|
||||
self.stateTag = stateTag
|
||||
}
|
||||
|
||||
init(id: NSManagedObjectID) {
|
||||
var stateID: ItemStateID {
|
||||
|
||||
self.init(id: id, isReloaded: false)
|
||||
return .init(id: self.id, stateTag: self.stateTag)
|
||||
}
|
||||
|
||||
func isContentEqual(to source: Item) -> Bool {
|
||||
|
||||
return !self.isReloaded && self.id == source.id
|
||||
return self.id == source.id && self.stateTag == source.stateTag
|
||||
}
|
||||
|
||||
// MARK: Identifiable
|
||||
|
||||
var id: NSManagedObjectID
|
||||
let id: NSManagedObjectID
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Section
|
||||
|
||||
internal struct Section: Identifiable, Equatable {
|
||||
fileprivate struct Section: Identifiable, Equatable {
|
||||
|
||||
var elements: [Item] = []
|
||||
var isReloaded: Bool
|
||||
var stateTag: UUID
|
||||
|
||||
init(id: NSString, items: [Item], isReloaded: Bool) {
|
||||
init(id: String, items: [Item] = [], stateTag: UUID) {
|
||||
self.id = id
|
||||
self.elements = items
|
||||
self.isReloaded = isReloaded
|
||||
}
|
||||
|
||||
init(id: NSString) {
|
||||
|
||||
self.init(id: id, items: [], isReloaded: false)
|
||||
self.stateTag = stateTag
|
||||
}
|
||||
|
||||
init<S: Sequence>(source: Section, elements: S) where S.Element == Item {
|
||||
|
||||
self.init(id: source.id, items: Array(elements), isReloaded: source.isReloaded)
|
||||
self.init(id: source.id, items: Array(elements), stateTag: source.stateTag)
|
||||
}
|
||||
|
||||
var stateID: SectionStateID {
|
||||
|
||||
return .init(id: self.id, stateTag: self.stateTag)
|
||||
}
|
||||
|
||||
func isContentEqual(to source: Section) -> Bool {
|
||||
|
||||
return !self.isReloaded && self.id == source.id
|
||||
return self.id == source.id && self.stateTag == source.stateTag
|
||||
}
|
||||
|
||||
// MARK: Identifiable
|
||||
|
||||
var id: NSString
|
||||
let id: String
|
||||
}
|
||||
|
||||
|
||||
@@ -550,57 +651,14 @@ extension Internals {
|
||||
|
||||
fileprivate struct ItemPosition {
|
||||
|
||||
var item: Item
|
||||
var itemRelativeIndex: Int
|
||||
var section: Section
|
||||
var sectionIndex: Int
|
||||
let item: Item
|
||||
let itemRelativeIndex: Int
|
||||
let section: Section
|
||||
let sectionIndex: Int
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - NSDiffableDataSourceSnapshot: Internals.DiffableDataSourceSnapshot
|
||||
|
||||
@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 15.0, *)
|
||||
extension NSDiffableDataSourceSnapshot: Internals.DiffableDataSourceSnapshot where SectionIdentifierType == NSString, ItemIdentifierType == NSManagedObjectID {}
|
||||
|
||||
|
||||
// MARK: - Internals.DiffableDataSourceSnapshot
|
||||
|
||||
internal protocol _Internal_DiffableDataSourceSnapshot {
|
||||
|
||||
init()
|
||||
|
||||
var numberOfItems: Int { get }
|
||||
var numberOfSections: Int { get }
|
||||
var sectionIdentifiers: [NSString] { get }
|
||||
var itemIdentifiers: [NSManagedObjectID] { get }
|
||||
|
||||
func numberOfItems(inSection identifier: NSString) -> Int
|
||||
func itemIdentifiers(inSection identifier: NSString) -> [NSManagedObjectID]
|
||||
func sectionIdentifier(containingItem identifier: NSManagedObjectID) -> NSString?
|
||||
func indexOfItem(_ identifier: NSManagedObjectID) -> Int?
|
||||
func indexOfSection(_ identifier: NSString) -> Int?
|
||||
|
||||
mutating func appendItems(_ identifiers: [NSManagedObjectID], toSection sectionIdentifier: NSString?)
|
||||
mutating func insertItems(_ identifiers: [NSManagedObjectID], beforeItem beforeIdentifier: NSManagedObjectID)
|
||||
mutating func insertItems(_ identifiers: [NSManagedObjectID], afterItem afterIdentifier: NSManagedObjectID)
|
||||
mutating func deleteItems(_ identifiers: [NSManagedObjectID])
|
||||
mutating func deleteAllItems()
|
||||
mutating func moveItem(_ identifier: NSManagedObjectID, beforeItem toIdentifier: NSManagedObjectID)
|
||||
mutating func moveItem(_ identifier: NSManagedObjectID, afterItem toIdentifier: NSManagedObjectID)
|
||||
mutating func reloadItems(_ identifiers: [NSManagedObjectID])
|
||||
mutating func appendSections(_ identifiers: [NSString])
|
||||
mutating func insertSections(_ identifiers: [NSString], beforeSection toIdentifier: NSString)
|
||||
mutating func insertSections(_ identifiers: [NSString], afterSection toIdentifier: NSString)
|
||||
mutating func deleteSections(_ identifiers: [NSString])
|
||||
mutating func moveSection(_ identifier: NSString, beforeSection toIdentifier: NSString)
|
||||
mutating func moveSection(_ identifier: NSString, afterSection toIdentifier: NSString)
|
||||
mutating func reloadSections(_ identifiers: [NSString])
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -75,14 +75,14 @@ extension Internals {
|
||||
|
||||
internal func initialFetch() {
|
||||
|
||||
#if canImport(UIKit) || canImport(AppKit)
|
||||
|
||||
if #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 15.0, *) {
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
#endif
|
||||
// #if canImport(UIKit) || canImport(AppKit)
|
||||
//
|
||||
// if #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 15.0, *) {
|
||||
//
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// #endif
|
||||
|
||||
guard let fetchedResultsController = self.fetchedResultsController else {
|
||||
|
||||
@@ -94,50 +94,65 @@ extension Internals {
|
||||
|
||||
// MARK: NSFetchedResultsControllerDelegate
|
||||
|
||||
#if canImport(UIKit) || canImport(AppKit)
|
||||
|
||||
@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 15.0, *)
|
||||
@objc
|
||||
dynamic func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChangeContentWith snapshot: NSDiffableDataSourceSnapshotReference) {
|
||||
|
||||
self.handler?.controller(
|
||||
controller,
|
||||
didChangContentWith: snapshot as NSDiffableDataSourceSnapshot<NSString, NSManagedObjectID>
|
||||
)
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@objc
|
||||
dynamic func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
|
||||
|
||||
self.reloadedIDs = []
|
||||
}
|
||||
// #if canImport(UIKit) || canImport(AppKit)
|
||||
//
|
||||
// @available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 15.0, *)
|
||||
// @objc
|
||||
// dynamic func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChangeContentWith snapshot: NSDiffableDataSourceSnapshotReference) {
|
||||
//
|
||||
// self.handler?.controller(
|
||||
// controller,
|
||||
// didChangContentWith: snapshot as NSDiffableDataSourceSnapshot<NSString, NSManagedObjectID>
|
||||
// )
|
||||
// }
|
||||
//
|
||||
// #endif
|
||||
|
||||
@objc
|
||||
dynamic func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
|
||||
|
||||
let nextStateTag: UUID = .init()
|
||||
var dataSourceSnapshot = Internals.DiffableDataSourceSnapshot(
|
||||
sections: controller.sections ?? [],
|
||||
previousStateTag: self.previousStateTag,
|
||||
nextStateTag: nextStateTag
|
||||
)
|
||||
dataSourceSnapshot.reloadItems(self.reloadedItemIDs, nextStateTag: nextStateTag)
|
||||
dataSourceSnapshot.reloadSections(self.reloadedSectionIDs, nextStateTag: nextStateTag)
|
||||
|
||||
self.handler?.controller(
|
||||
controller,
|
||||
didChangContentWith: FallbackDiffableDataSourceSnapshot(
|
||||
sections: controller.sections ?? []
|
||||
)
|
||||
didChangContentWith: dataSourceSnapshot
|
||||
)
|
||||
self.reloadedIDs = []
|
||||
self.previousStateTag = nextStateTag
|
||||
}
|
||||
|
||||
// @objc
|
||||
// dynamic func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
|
||||
//
|
||||
//
|
||||
//// [1].difference(from: <#T##BidirectionalCollection#>)
|
||||
// let managedObject = anObject as! NSManagedObject
|
||||
// self.reloadedIDs.insert(managedObject.objectID)
|
||||
// }
|
||||
@objc
|
||||
dynamic func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
|
||||
|
||||
let managedObject = anObject as! NSManagedObject
|
||||
self.reloadedItemIDs.insert(managedObject.objectID)
|
||||
}
|
||||
|
||||
@objc
|
||||
dynamic func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) {
|
||||
|
||||
self.reloadedSectionIDs.insert(sectionInfo.name)
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private var reloadedIDs: Set<NSManagedObjectID> = []
|
||||
private var previousStateTag: UUID = .init() {
|
||||
|
||||
didSet {
|
||||
|
||||
self.reloadedItemIDs = []
|
||||
self.reloadedSectionIDs = []
|
||||
}
|
||||
}
|
||||
|
||||
private var reloadedItemIDs: Set<NSManagedObjectID> = []
|
||||
private var reloadedSectionIDs: Set<String> = []
|
||||
}
|
||||
}
|
||||
|
||||
94
Sources/Internals.LazyNonmutating.swift
Normal file
94
Sources/Internals.LazyNonmutating.swift
Normal file
@@ -0,0 +1,94 @@
|
||||
//
|
||||
// Internals.LazyNonmutating.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: - LazyNonmutating
|
||||
|
||||
@propertyWrapper
|
||||
internal final class LazyNonmutating<Value> {
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
init(_ initializer: @escaping () -> Value) {
|
||||
|
||||
self.initializer = initializer
|
||||
}
|
||||
|
||||
init(uninitialized: Void) {
|
||||
|
||||
self.initializer = { fatalError() }
|
||||
}
|
||||
|
||||
func initialize(_ initializer: @escaping () -> Value) {
|
||||
|
||||
self.initializer = initializer
|
||||
}
|
||||
|
||||
func reset(_ initializer: @escaping () -> Value) {
|
||||
|
||||
self.initializer = initializer
|
||||
self.initializedValue = nil
|
||||
}
|
||||
|
||||
|
||||
// MARK: @propertyWrapper
|
||||
|
||||
var wrappedValue: Value {
|
||||
|
||||
get {
|
||||
|
||||
if let initializedValue = self.initializedValue {
|
||||
|
||||
return initializedValue
|
||||
}
|
||||
let initializedValue = self.initializer()
|
||||
self.initializedValue = initializedValue
|
||||
return initializedValue
|
||||
}
|
||||
set {
|
||||
|
||||
self.initializer = { newValue }
|
||||
self.initializedValue = newValue
|
||||
}
|
||||
}
|
||||
|
||||
var projectedValue: Internals.LazyNonmutating<Value> {
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private var initializer: () -> Value
|
||||
private var initializedValue: Value? = nil
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,7 @@ extension Internals {
|
||||
|
||||
internal final class NotificationObserver {
|
||||
|
||||
// MARK: Public
|
||||
// MARK: Internal
|
||||
|
||||
let observer: NSObjectProtocol
|
||||
|
||||
|
||||
56
Sources/Internals.SharedNotificationObserver.swift
Normal file
56
Sources/Internals.SharedNotificationObserver.swift
Normal file
@@ -0,0 +1,56 @@
|
||||
//
|
||||
// Internals.SharedNotificationObserver.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: - Internal
|
||||
|
||||
extension Internals {
|
||||
|
||||
// MARK: - SharedNotificationObserver
|
||||
|
||||
internal final class SharedNotificationObserver {
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
let observer: NSObjectProtocol
|
||||
|
||||
init(notificationName: Notification.Name, object: Any?, queue: OperationQueue? = nil, closure: @escaping (_ note: Notification) -> Void) {
|
||||
|
||||
self.observer = NotificationCenter.default.addObserver(
|
||||
forName: notificationName,
|
||||
object: object,
|
||||
queue: queue,
|
||||
using: closure
|
||||
)
|
||||
}
|
||||
|
||||
deinit {
|
||||
|
||||
NotificationCenter.default.removeObserver(self.observer)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,44 +34,44 @@ import AppKit
|
||||
#endif
|
||||
|
||||
|
||||
// MARK: - LiveList
|
||||
// MARK: - ListSnapshot
|
||||
|
||||
public struct ListSnapshot<D: DynamicObject>: SnapshotResult, RandomAccessCollection, Hashable {
|
||||
public struct ListSnapshot<O: DynamicObject>: SnapshotResult, RandomAccessCollection, Hashable {
|
||||
|
||||
// MARK: Public
|
||||
|
||||
public typealias SectionID = String
|
||||
public typealias ItemID = D.ObjectID
|
||||
public typealias ItemID = O.ObjectID
|
||||
|
||||
public subscript<S: Sequence>(indices indices: S) -> [ObjectType] where S.Element == Index {
|
||||
public subscript<S: Sequence>(indices indices: S) -> [LiveObject<O>] where S.Element == Index {
|
||||
|
||||
let context = self.context!
|
||||
let objectIDs = self.diffableSnapshot.itemIdentifiers
|
||||
let itemIDs = self.diffableSnapshot.allItemIDs
|
||||
return indices.map { position in
|
||||
|
||||
let objectID = objectIDs[position]
|
||||
return context.fetchExisting(objectID)!
|
||||
let itemID = itemIDs[position]
|
||||
return LiveObject<O>(id: itemID, context: context)
|
||||
}
|
||||
}
|
||||
|
||||
public subscript(section sectionID: SectionID) -> [ObjectType] {
|
||||
public subscript(section sectionID: SectionID) -> [LiveObject<O>] {
|
||||
|
||||
let context = self.context!
|
||||
let objectIDs = self.itemIdentifiers(inSection: sectionID)
|
||||
return objectIDs.map {
|
||||
|
||||
return context.fetchExisting($0)!
|
||||
let itemIDs = self.diffableSnapshot.itemIDs(inSection: sectionID)
|
||||
return itemIDs.map {
|
||||
|
||||
return LiveObject<O>(id: $0, context: context)
|
||||
}
|
||||
}
|
||||
|
||||
public subscript<S: Sequence>(section sectionID: SectionID, itemIndices itemIndices: S) -> [ObjectType] 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 objectIDs = self.itemIdentifiers(inSection: sectionID)
|
||||
let itemIDs = self.diffableSnapshot.itemIDs(inSection: sectionID)
|
||||
return itemIndices.map { position in
|
||||
|
||||
let objectID = objectIDs[position]
|
||||
return context.fetchExisting(objectID)!
|
||||
let itemID = itemIDs[position]
|
||||
return LiveObject<O>(id: itemID, context: context)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,60 +85,60 @@ public struct ListSnapshot<D: DynamicObject>: SnapshotResult, RandomAccessCollec
|
||||
return self.diffableSnapshot.numberOfSections
|
||||
}
|
||||
|
||||
public var sectionIdentifiers: [String] {
|
||||
public var sectionIDs: [SectionID] {
|
||||
|
||||
return self.diffableSnapshot.sectionIdentifiers as [String]
|
||||
return self.diffableSnapshot.allSectionIDs
|
||||
}
|
||||
|
||||
public var itemIdentifiers: [ItemID] {
|
||||
|
||||
return self.diffableSnapshot.itemIdentifiers as [ItemID]
|
||||
return self.diffableSnapshot.allItemIDs
|
||||
}
|
||||
|
||||
public func numberOfItems(inSection identifier: SectionID) -> Int {
|
||||
|
||||
return self.diffableSnapshot.numberOfItems(inSection: identifier as NSString)
|
||||
return self.diffableSnapshot.numberOfItems(inSection: identifier)
|
||||
}
|
||||
|
||||
public func itemIdentifiers(inSection identifier: SectionID) -> [ItemID] {
|
||||
|
||||
return self.diffableSnapshot.itemIdentifiers(inSection: identifier as NSString)
|
||||
return self.diffableSnapshot.itemIDs(inSection: identifier)
|
||||
}
|
||||
|
||||
public func itemIdentifiers(inSection identifier: SectionID, atIndices indices: IndexSet) -> [ItemID] {
|
||||
|
||||
let itemIDs = self.itemIdentifiers(inSection: identifier)
|
||||
let itemIDs = self.diffableSnapshot.itemIDs(inSection: identifier)
|
||||
return indices.map({ itemIDs[$0] })
|
||||
}
|
||||
|
||||
public func sectionIdentifier(containingItem identifier: ItemID) -> SectionID? {
|
||||
|
||||
return self.diffableSnapshot.sectionIdentifier(containingItem: identifier) as SectionID?
|
||||
return self.diffableSnapshot.sectionIDs(containingItem: identifier)
|
||||
}
|
||||
|
||||
public func indexOfItem(_ identifier: ItemID) -> Index? {
|
||||
|
||||
return self.diffableSnapshot.indexOfItem(identifier)
|
||||
return self.diffableSnapshot.indexOfItemID(identifier)
|
||||
}
|
||||
|
||||
public func indexOfSection(_ identifier: SectionID) -> Int? {
|
||||
|
||||
return self.diffableSnapshot.indexOfSection(identifier as NSString)
|
||||
return self.diffableSnapshot.indexOfSectionID(identifier)
|
||||
}
|
||||
|
||||
public mutating func appendItems(_ identifiers: [ItemID], toSection sectionIdentifier: SectionID? = nil) {
|
||||
|
||||
self.diffableSnapshot.appendItems(identifiers, toSection: sectionIdentifier as NSString?)
|
||||
self.diffableSnapshot.appendItems(identifiers, toSection: sectionIdentifier, nextStateTag: .init())
|
||||
}
|
||||
|
||||
public mutating func insertItems(_ identifiers: [ItemID], beforeItem beforeIdentifier: ItemID) {
|
||||
|
||||
self.diffableSnapshot.insertItems(identifiers, beforeItem: beforeIdentifier)
|
||||
self.diffableSnapshot.insertItems(identifiers, beforeItem: beforeIdentifier, nextStateTag: .init())
|
||||
}
|
||||
|
||||
public mutating func insertItems(_ identifiers: [ItemID], afterItem afterIdentifier: ItemID) {
|
||||
|
||||
self.diffableSnapshot.insertItems(identifiers, afterItem: afterIdentifier)
|
||||
self.diffableSnapshot.insertItems(identifiers, afterItem: afterIdentifier, nextStateTag: .init())
|
||||
}
|
||||
|
||||
public mutating func deleteItems(_ identifiers: [ItemID]) {
|
||||
@@ -163,73 +163,73 @@ public struct ListSnapshot<D: DynamicObject>: SnapshotResult, RandomAccessCollec
|
||||
|
||||
public mutating func reloadItems(_ identifiers: [ItemID]) {
|
||||
|
||||
self.diffableSnapshot.reloadItems(identifiers)
|
||||
self.diffableSnapshot.reloadItems(identifiers, nextStateTag: .init())
|
||||
}
|
||||
|
||||
public mutating func appendSections(_ identifiers: [SectionID]) {
|
||||
|
||||
self.diffableSnapshot.appendSections(identifiers as [NSString])
|
||||
self.diffableSnapshot.appendSections(identifiers, nextStateTag: .init())
|
||||
}
|
||||
|
||||
public mutating func insertSections(_ identifiers: [SectionID], beforeSection toIdentifier: SectionID) {
|
||||
|
||||
self.diffableSnapshot.insertSections(identifiers as [NSString], beforeSection: toIdentifier as NSString)
|
||||
self.diffableSnapshot.insertSections(identifiers, beforeSection: toIdentifier, nextStateTag: .init())
|
||||
}
|
||||
|
||||
public mutating func insertSections(_ identifiers: [SectionID], afterSection toIdentifier: SectionID) {
|
||||
|
||||
self.diffableSnapshot.insertSections(identifiers as [NSString], afterSection: toIdentifier as NSString)
|
||||
self.diffableSnapshot.insertSections(identifiers, afterSection: toIdentifier, nextStateTag: .init())
|
||||
}
|
||||
|
||||
public mutating func deleteSections(_ identifiers: [SectionID]) {
|
||||
|
||||
self.diffableSnapshot.deleteSections(identifiers as [NSString])
|
||||
self.diffableSnapshot.deleteSections(identifiers)
|
||||
}
|
||||
|
||||
public mutating func moveSection(_ identifier: SectionID, beforeSection toIdentifier: SectionID) {
|
||||
|
||||
self.diffableSnapshot.moveSection(identifier as NSString, beforeSection: toIdentifier as NSString)
|
||||
self.diffableSnapshot.moveSection(identifier, beforeSection: toIdentifier)
|
||||
}
|
||||
|
||||
public mutating func moveSection(_ identifier: SectionID, afterSection toIdentifier: SectionID) {
|
||||
|
||||
self.diffableSnapshot.moveSection(identifier as NSString, afterSection: toIdentifier as NSString)
|
||||
self.diffableSnapshot.moveSection(identifier, afterSection: toIdentifier)
|
||||
}
|
||||
|
||||
public mutating func reloadSections(_ identifiers: [SectionID]) {
|
||||
|
||||
self.diffableSnapshot.reloadSections(identifiers as [NSString])
|
||||
self.diffableSnapshot.reloadSections(identifiers, nextStateTag: .init())
|
||||
}
|
||||
|
||||
|
||||
// MARK: SnapshotResult
|
||||
|
||||
public typealias ObjectType = D
|
||||
public typealias ObjectType = O
|
||||
|
||||
|
||||
// MARK: RandomAccessCollection
|
||||
|
||||
public var startIndex: Index {
|
||||
|
||||
return self.diffableSnapshot.itemIdentifiers.startIndex
|
||||
return self.diffableSnapshot.allItemIDs.startIndex
|
||||
}
|
||||
|
||||
public var endIndex: Index {
|
||||
|
||||
return self.diffableSnapshot.itemIdentifiers.endIndex
|
||||
return self.diffableSnapshot.allItemIDs.endIndex
|
||||
}
|
||||
|
||||
public subscript(position: Index) -> ObjectType {
|
||||
public subscript(position: Index) -> Element {
|
||||
|
||||
let context = self.context!
|
||||
let objectID = self.diffableSnapshot.itemIdentifiers[position]
|
||||
return context.fetchExisting(objectID)!
|
||||
let itemID = self.diffableSnapshot.allItemIDs[position]
|
||||
return LiveObject<O>(id: itemID, context: context)
|
||||
}
|
||||
|
||||
|
||||
// MARK: Sequence
|
||||
|
||||
public typealias Element = ObjectType
|
||||
public typealias Element = LiveObject<O>
|
||||
|
||||
public typealias Index = Int
|
||||
|
||||
@@ -254,7 +254,7 @@ public struct ListSnapshot<D: DynamicObject>: SnapshotResult, RandomAccessCollec
|
||||
|
||||
internal init() {
|
||||
|
||||
self.diffableSnapshot = Internals.FallbackDiffableDataSourceSnapshot()
|
||||
self.diffableSnapshot = .init()
|
||||
self.context = nil
|
||||
}
|
||||
|
||||
@@ -263,11 +263,22 @@ public struct ListSnapshot<D: DynamicObject>: SnapshotResult, RandomAccessCollec
|
||||
self.diffableSnapshot = diffableSnapshot
|
||||
self.context = context
|
||||
}
|
||||
|
||||
internal var nextStateTag: UUID {
|
||||
|
||||
return self.diffableSnapshot.nextStateTag
|
||||
}
|
||||
|
||||
internal func itemIDs(where stateCondition: @escaping (UUID) -> Bool) -> [ItemID] {
|
||||
|
||||
return self.diffableSnapshot.itemIDs(where: stateCondition)
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let id: UUID = .init()
|
||||
private let context: NSManagedObjectContext?
|
||||
|
||||
private var diffableSnapshot: Internals.DiffableDataSourceSnapshot
|
||||
}
|
||||
|
||||
@@ -38,40 +38,135 @@ import SwiftUI
|
||||
|
||||
// MARK: - LiveList
|
||||
|
||||
public final class LiveList<D: DynamicObject> {
|
||||
public final class LiveList<O: DynamicObject>: Hashable {
|
||||
|
||||
// MARK: Public
|
||||
|
||||
public fileprivate(set) var snapshot: ListSnapshot<ObjectType> = .init() {
|
||||
public typealias SectionID = SnapshotType.SectionID
|
||||
public typealias ItemID = SnapshotType.ItemID
|
||||
|
||||
public fileprivate(set) var snapshot: SnapshotType = .init() {
|
||||
|
||||
willSet {
|
||||
|
||||
guard #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 15.0, *) else {
|
||||
|
||||
return
|
||||
if #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 15.0, *) {
|
||||
|
||||
self.willChange()
|
||||
}
|
||||
#if canImport(Combine)
|
||||
|
||||
#if canImport(SwiftUI)
|
||||
withAnimation {
|
||||
self.objectWillChange.send()
|
||||
}
|
||||
|
||||
#else
|
||||
self.objectWillChange.send()
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
public var numberOfItems: Int {
|
||||
|
||||
return self.snapshot.numberOfItems
|
||||
}
|
||||
|
||||
public var numberOfSections: Int {
|
||||
|
||||
return self.snapshot.numberOfSections
|
||||
}
|
||||
|
||||
public var sections: [SectionID] {
|
||||
|
||||
return self.snapshot.sectionIDs
|
||||
}
|
||||
|
||||
public subscript(section sectionID: SectionID) -> [LiveObject<O>] {
|
||||
|
||||
let context = self.context
|
||||
return self.snapshot
|
||||
.itemIdentifiers(inSection: sectionID)
|
||||
.map({ context.liveObject(id: $0) })
|
||||
}
|
||||
|
||||
public subscript(itemID itemID: ItemID) -> LiveObject<O>? {
|
||||
|
||||
guard let validID = self.snapshot.itemIdentifiers.first(where: { $0 == itemID }) else {
|
||||
|
||||
return nil
|
||||
}
|
||||
return self.context.liveObject(id: validID)
|
||||
}
|
||||
|
||||
public subscript<S: Sequence>(section sectionID: SectionID, itemIndices itemIndices: S) -> [LiveObject<O>] where S.Element == Int {
|
||||
|
||||
let context = self.context
|
||||
let itemIDs = self.snapshot.itemIdentifiers(inSection: sectionID)
|
||||
return itemIndices.map { position in
|
||||
|
||||
let itemID = itemIDs[position]
|
||||
return context.liveObject(id: itemID)
|
||||
}
|
||||
}
|
||||
|
||||
public var items: [LiveObject<O>] {
|
||||
|
||||
let context = self.context
|
||||
return self.snapshot.itemIdentifiers
|
||||
.map({ context.liveObject(id: $0) })
|
||||
}
|
||||
|
||||
public func numberOfItems(inSection identifier: SectionID) -> Int {
|
||||
|
||||
return self.snapshot.numberOfItems(inSection: identifier)
|
||||
}
|
||||
|
||||
public func items(inSection identifier: SectionID) -> [LiveObject<O>] {
|
||||
|
||||
let context = self.context
|
||||
return self.snapshot
|
||||
.itemIdentifiers(inSection: identifier)
|
||||
.map({ context.liveObject(id: $0) })
|
||||
}
|
||||
|
||||
public func items(inSection identifier: SectionID, atIndices indices: IndexSet) -> [LiveObject<O>] {
|
||||
|
||||
let context = self.context
|
||||
let itemIDs = self.snapshot.itemIdentifiers(inSection: identifier)
|
||||
return indices.map { position in
|
||||
|
||||
let itemID = itemIDs[position]
|
||||
return context.liveObject(id: itemID)
|
||||
}
|
||||
}
|
||||
|
||||
public func section(containingItem item: LiveObject<O>) -> SectionID? {
|
||||
|
||||
return self.snapshot.sectionIdentifier(containingItem: item.id)
|
||||
}
|
||||
|
||||
public func indexOfItem(_ item: LiveObject<O>) -> Int? {
|
||||
|
||||
return self.snapshot.indexOfItem(item.id)
|
||||
}
|
||||
|
||||
public func indexOfSection(_ identifier: SectionID) -> Int? {
|
||||
|
||||
return self.snapshot.indexOfSection(identifier)
|
||||
}
|
||||
|
||||
|
||||
// MARK: Equatable
|
||||
|
||||
public static func == (_ lhs: LiveList, _ rhs: LiveList) -> Bool {
|
||||
|
||||
return lhs === rhs
|
||||
}
|
||||
|
||||
|
||||
// MARK: Hashable
|
||||
|
||||
public func hash(into hasher: inout Hasher) {
|
||||
|
||||
hasher.combine(ObjectIdentifier(self))
|
||||
}
|
||||
|
||||
|
||||
// MARK: LiveResult
|
||||
|
||||
public typealias ObjectType = D
|
||||
public typealias ObjectType = O
|
||||
|
||||
public typealias SnapshotType = ListSnapshot<D>
|
||||
public typealias SnapshotType = ListSnapshot<O>
|
||||
|
||||
|
||||
// MARK: Internal
|
||||
@@ -137,6 +232,8 @@ public final class LiveList<D: DynamicObject> {
|
||||
private let from: From<ObjectType>
|
||||
private let sectionBy: SectionBy<ObjectType>?
|
||||
|
||||
private lazy var context: NSManagedObjectContext = self.fetchedResultsController.managedObjectContext
|
||||
|
||||
private static func recreateFetchedResultsController(context: NSManagedObjectContext, from: From<ObjectType>, sectionBy: SectionBy<ObjectType>?, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest<NSManagedObject>) -> Void) -> (controller: Internals.CoreStoreFetchedResultsController, delegate: Internals.FetchedDiffableDataSourceSnapshotDelegate) {
|
||||
|
||||
let fetchRequest = Internals.CoreStoreFetchRequest<NSManagedObject>()
|
||||
@@ -197,76 +294,7 @@ public final class LiveList<D: DynamicObject> {
|
||||
self.applyFetchClauses = applyFetchClauses
|
||||
self.fetchedResultsControllerDelegate.handler = self
|
||||
|
||||
guard let coordinator = context.parentStack?.coordinator else {
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
self.observerForWillChangePersistentStore = Internals.NotificationObserver(
|
||||
notificationName: NSNotification.Name.NSPersistentStoreCoordinatorStoresWillChange,
|
||||
object: coordinator,
|
||||
queue: OperationQueue.main,
|
||||
closure: { [weak self] (note) -> Void in
|
||||
|
||||
guard let `self` = self else {
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// self.isPersistentStoreChanging = true
|
||||
//
|
||||
// guard let removedStores = (note.userInfo?[NSRemovedPersistentStoresKey] as? [NSPersistentStore]).flatMap(Set.init),
|
||||
// !Set(self.fetchedResultsController.typedFetchRequest.safeAffectedStores() ?? []).intersection(removedStores).isEmpty else {
|
||||
//
|
||||
// return
|
||||
// }
|
||||
// self.refetch(self.applyFetchClauses)
|
||||
}
|
||||
)
|
||||
|
||||
self.observerForDidChangePersistentStore = Internals.NotificationObserver(
|
||||
notificationName: NSNotification.Name.NSPersistentStoreCoordinatorStoresDidChange,
|
||||
object: coordinator,
|
||||
queue: OperationQueue.main,
|
||||
closure: { [weak self] (note) -> Void in
|
||||
|
||||
guard let `self` = self else {
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// if !self.isPendingRefetch {
|
||||
//
|
||||
// let previousStores = Set(self.fetchedResultsController.typedFetchRequest.safeAffectedStores() ?? [])
|
||||
// let currentStores = previousStores
|
||||
// .subtracting(note.userInfo?[NSRemovedPersistentStoresKey] as? [NSPersistentStore] ?? [])
|
||||
// .union(note.userInfo?[NSAddedPersistentStoresKey] as? [NSPersistentStore] ?? [])
|
||||
//
|
||||
// if previousStores != currentStores {
|
||||
//
|
||||
// self.refetch(self.applyFetchClauses)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// self.isPersistentStoreChanging = false
|
||||
}
|
||||
)
|
||||
|
||||
if let createAsynchronously = createAsynchronously {
|
||||
|
||||
// transactionQueue.async {
|
||||
//
|
||||
// try!internal self.fetchedResultsController.performFetchFromSpecifiedStores()
|
||||
// self.taskGroup.notify(queue: .main) {
|
||||
//
|
||||
// createAsynchronously(self)
|
||||
// }
|
||||
// }
|
||||
}
|
||||
else {
|
||||
|
||||
try! self.fetchedResultsController.performFetchFromSpecifiedStores()
|
||||
}
|
||||
try! self.fetchedResultsController.performFetchFromSpecifiedStores()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -301,6 +329,29 @@ extension LiveList: LiveResult {
|
||||
|
||||
return self.rawObjectWillChange! as! ObservableObjectPublisher
|
||||
}
|
||||
|
||||
public func willChange() {
|
||||
|
||||
#if canImport(Combine)
|
||||
|
||||
#if canImport(SwiftUI)
|
||||
withAnimation {
|
||||
|
||||
self.objectWillChange.send()
|
||||
}
|
||||
|
||||
#else
|
||||
self.objectWillChange.send()
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
public func didChange() {
|
||||
|
||||
// TODO:
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
285
Sources/LiveObject.swift
Normal file
285
Sources/LiveObject.swift
Normal file
@@ -0,0 +1,285 @@
|
||||
//
|
||||
// LiveObject.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 CoreData
|
||||
|
||||
#if canImport(Combine)
|
||||
import Combine
|
||||
|
||||
#endif
|
||||
|
||||
#if canImport(SwiftUI)
|
||||
import SwiftUI
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
// MARK: - LiveObject
|
||||
|
||||
@dynamicMemberLookup
|
||||
public final class LiveObject<O: DynamicObject>: Identifiable, Hashable {
|
||||
|
||||
// MARK: Public
|
||||
|
||||
public typealias SectionID = String
|
||||
public typealias ItemID = O.ObjectID
|
||||
|
||||
public var snapshot: SnapshotType {
|
||||
|
||||
return self.lazySnapshot
|
||||
}
|
||||
|
||||
public private(set) lazy var object: O = self.context.fetchExisting(self.id)!
|
||||
|
||||
|
||||
// MARK: Identifiable
|
||||
|
||||
public let id: O.ObjectID
|
||||
|
||||
|
||||
// MARK: Equatable
|
||||
|
||||
public static func == (_ lhs: LiveObject, _ rhs: LiveObject) -> Bool {
|
||||
|
||||
return lhs.id == rhs.id
|
||||
&& lhs.context == rhs.context
|
||||
}
|
||||
|
||||
|
||||
// MARK: Hashable
|
||||
|
||||
public func hash(into hasher: inout Hasher) {
|
||||
|
||||
hasher.combine(self.id)
|
||||
hasher.combine(self.context)
|
||||
}
|
||||
|
||||
|
||||
// MARK: LiveResult
|
||||
|
||||
public typealias ObjectType = O
|
||||
|
||||
public typealias SnapshotType = ObjectSnapshot<O>
|
||||
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
internal convenience init(id: ID, context: NSManagedObjectContext) {
|
||||
|
||||
self.init(id: id, context: context, initializer: ObjectSnapshot<O>.init(id:context:))
|
||||
}
|
||||
|
||||
|
||||
// MARK: FilePrivate
|
||||
|
||||
fileprivate let rawObjectWillChange: Any?
|
||||
|
||||
fileprivate init(id: O.ObjectID, context: NSManagedObjectContext, initializer: @escaping (NSManagedObjectID, NSManagedObjectContext) -> ObjectSnapshot<O>) {
|
||||
|
||||
self.id = id
|
||||
self.context = context
|
||||
if #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 15.0, *) {
|
||||
|
||||
#if canImport(Combine)
|
||||
self.rawObjectWillChange = ObservableObjectPublisher()
|
||||
|
||||
#else
|
||||
self.rawObjectWillChange = nil
|
||||
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
|
||||
self.rawObjectWillChange = nil
|
||||
}
|
||||
self.observer = NotificationCenter.default.addObserver(
|
||||
forName: .NSManagedObjectContextObjectsDidChange,
|
||||
object: context,
|
||||
queue: .main,
|
||||
using: { [weak self] (notification) in
|
||||
|
||||
guard let self = self, let userInfo = notification.userInfo else {
|
||||
|
||||
return
|
||||
}
|
||||
let updatedObjects = (userInfo[NSUpdatedObjectsKey] as! NSSet? ?? [])
|
||||
let mergedObjects = (userInfo[NSRefreshedObjectsKey] as! NSSet? ?? [])
|
||||
guard mergedObjects.contains(where: { ($0 as! NSManagedObject).objectID == id })
|
||||
|| updatedObjects.contains(where: { ($0 as! NSManagedObject).objectID == id }) else {
|
||||
|
||||
return
|
||||
}
|
||||
self.$lazySnapshot.reset({ initializer(id, context) })
|
||||
if #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 15.0, *) {
|
||||
|
||||
self.willChange()
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
self.$lazySnapshot.initialize({ initializer(id, context) })
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let context: NSManagedObjectContext
|
||||
private var observer: NSObjectProtocol?
|
||||
|
||||
@Internals.LazyNonmutating(uninitialized: ())
|
||||
private var lazySnapshot: ObjectSnapshot<O>
|
||||
}
|
||||
|
||||
|
||||
#if canImport(Combine)
|
||||
import Combine
|
||||
|
||||
// MARK: - LiveObject: LiveResult
|
||||
|
||||
@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 15.0, *)
|
||||
extension LiveObject: LiveResult {
|
||||
|
||||
// MARK: ObservableObject
|
||||
|
||||
public var objectWillChange: ObservableObjectPublisher {
|
||||
|
||||
return self.rawObjectWillChange! as! ObservableObjectPublisher
|
||||
}
|
||||
|
||||
public func willChange() {
|
||||
|
||||
#if canImport(Combine)
|
||||
|
||||
#if canImport(SwiftUI)
|
||||
withAnimation {
|
||||
|
||||
self.objectWillChange.send()
|
||||
}
|
||||
|
||||
#else
|
||||
self.objectWillChange.send()
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
public func didChange() {
|
||||
|
||||
// TODO:
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
// MARK: - LiveObject where O: NSManagedObject
|
||||
|
||||
@available(*, unavailable, message: "KeyPaths accessed from @dynamicMemberLookup types can't generate KVC keys yet (https://bugs.swift.org/browse/SR-11351)")
|
||||
extension LiveObject where O: NSManagedObject {
|
||||
|
||||
// MARK: Public
|
||||
|
||||
/**
|
||||
Returns the value for the property identified by a given key.
|
||||
*/
|
||||
public subscript<V: AllowedObjectiveCKeyPathValue>(dynamicMember member: KeyPath<O, V>) -> V {
|
||||
|
||||
fatalError()
|
||||
// return self.snapshot[dynamicMember: member]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - LiveObject where O: CoreStoreObject
|
||||
|
||||
extension LiveObject where O: CoreStoreObject {
|
||||
|
||||
// MARK: Public
|
||||
|
||||
/**
|
||||
Returns the value for the property identified by a given key.
|
||||
*/
|
||||
public subscript<V>(dynamicMember member: KeyPath<O, ValueContainer<O>.Required<V>>) -> V {
|
||||
|
||||
return self.snapshot[dynamicMember: member]
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the value for the property identified by a given key.
|
||||
*/
|
||||
public subscript<V>(dynamicMember member: KeyPath<O, ValueContainer<O>.Optional<V>>) -> V? {
|
||||
|
||||
return self.snapshot[dynamicMember: member]
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the value for the property identified by a given key.
|
||||
*/
|
||||
public subscript<V>(dynamicMember member: KeyPath<O, TransformableContainer<O>.Required<V>>) -> V {
|
||||
|
||||
return self.snapshot[dynamicMember: member]
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the value for the property identified by a given key.
|
||||
*/
|
||||
public subscript<V>(dynamicMember member: KeyPath<O, TransformableContainer<O>.Optional<V>>) -> V? {
|
||||
|
||||
return self.snapshot[dynamicMember: member]
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the value for the property identified by a given key.
|
||||
*/
|
||||
public subscript<D>(dynamicMember member: KeyPath<O, RelationshipContainer<O>.ToOne<D>>) -> D? {
|
||||
|
||||
return self.snapshot[dynamicMember: member]
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the value for the property identified by a given key.
|
||||
*/
|
||||
public subscript<D>(dynamicMember member: KeyPath<O, RelationshipContainer<O>.ToManyOrdered<D>>) -> [D] {
|
||||
|
||||
return self.snapshot[dynamicMember: member]
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the value for the property identified by a given key.
|
||||
*/
|
||||
public subscript<D>(dynamicMember member: KeyPath<O, RelationshipContainer<O>.ToManyUnordered<D>>) -> Set<D> {
|
||||
|
||||
return self.snapshot[dynamicMember: member]
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the value for the property identified by a given key.
|
||||
*/
|
||||
public subscript<T>(dynamicMember member: KeyPath<O, T>) -> T {
|
||||
|
||||
return self.object[keyPath: member]
|
||||
}
|
||||
}
|
||||
@@ -41,6 +41,9 @@ public protocol LiveResult: ObservableObject {
|
||||
|
||||
associatedtype ObjectType
|
||||
associatedtype SnapshotType: SnapshotResult where SnapshotType.ObjectType == Self.ObjectType
|
||||
|
||||
func willChange()
|
||||
func didChange()
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -38,15 +38,7 @@ extension NSManagedObject {
|
||||
|
||||
return nil
|
||||
}
|
||||
if context.isTransactionContext {
|
||||
|
||||
return context.parentTransaction?.isRunningInAllowedQueue()
|
||||
}
|
||||
if context.isDataStackContext {
|
||||
|
||||
return Thread.isMainThread
|
||||
}
|
||||
return nil
|
||||
return context.isRunningInAllowedQueue()
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
@@ -56,14 +48,6 @@ extension NSManagedObject {
|
||||
|
||||
return nil
|
||||
}
|
||||
if context.isTransactionContext {
|
||||
|
||||
return true
|
||||
}
|
||||
if context.isDataStackContext {
|
||||
|
||||
return false
|
||||
}
|
||||
return nil
|
||||
return context.isEditableInContext()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,6 +85,35 @@ extension NSManagedObjectContext {
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
internal func liveObject<D: DynamicObject>(id: NSManagedObjectID) -> LiveObject<D> {
|
||||
|
||||
let cache = self.liveObjectsCache(D.self)
|
||||
return Internals.with {
|
||||
|
||||
if let liveObject = cache.object(forKey: id) {
|
||||
|
||||
return liveObject
|
||||
}
|
||||
let liveObject = LiveObject<D>(id: id, context: self)
|
||||
cache.setObject(liveObject, forKey: id)
|
||||
return liveObject
|
||||
}
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
private func liveObjectsCache<D: DynamicObject>(_ objectType: D.Type) -> NSMapTable<NSManagedObjectID, LiveObject<D>> {
|
||||
|
||||
let key = Internals.typeName(objectType)
|
||||
if let cache = self.userInfo[key] {
|
||||
|
||||
return cache as! NSMapTable<NSManagedObjectID, LiveObject<D>>
|
||||
}
|
||||
let cache = NSMapTable<NSManagedObjectID, LiveObject<D>>.strongToWeakObjects()
|
||||
self.userInfo[key] = cache
|
||||
return cache
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
61
Sources/NSManagedObjectContext+Logging.swift
Normal file
61
Sources/NSManagedObjectContext+Logging.swift
Normal file
@@ -0,0 +1,61 @@
|
||||
//
|
||||
// NSManagedObjectContext+Logging.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2018 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CoreData
|
||||
|
||||
|
||||
// MARK: - NSManagedObjectContext
|
||||
|
||||
extension NSManagedObjectContext {
|
||||
|
||||
@nonobjc
|
||||
internal func isRunningInAllowedQueue() -> Bool? {
|
||||
|
||||
if self.isTransactionContext {
|
||||
|
||||
return self.parentTransaction?.isRunningInAllowedQueue()
|
||||
}
|
||||
if self.isDataStackContext {
|
||||
|
||||
return Thread.isMainThread
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
internal func isEditableInContext() -> Bool? {
|
||||
|
||||
if self.isTransactionContext {
|
||||
|
||||
return true
|
||||
}
|
||||
if self.isDataStackContext {
|
||||
|
||||
return false
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -24,58 +24,78 @@
|
||||
//
|
||||
|
||||
import CoreData
|
||||
import Foundation
|
||||
|
||||
#if canImport(UIKit)
|
||||
import UIKit
|
||||
|
||||
#elseif canImport(AppKit)
|
||||
import AppKit
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
// MARK: - ObjectSnapshot
|
||||
|
||||
/**
|
||||
An `ObjectSnapshot` contains "snapshot" values from a `DynamicObject` instance copied at a specific point in time.
|
||||
*/
|
||||
@dynamicMemberLookup
|
||||
public struct ObjectSnapshot<O: DynamicObject> {
|
||||
|
||||
// MARK: FilePrivate
|
||||
|
||||
fileprivate var attributes: [KeyPathString: Any]
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private init() {
|
||||
|
||||
self.attributes = [:]
|
||||
public struct ObjectSnapshot<O: DynamicObject>: SnapshotResult, Identifiable, Hashable {
|
||||
|
||||
// MARK: SnapshotResult
|
||||
|
||||
public typealias ObjectType = O
|
||||
|
||||
|
||||
// MARK: Identifiable
|
||||
|
||||
public let id: O.ObjectID
|
||||
|
||||
|
||||
// MARK: Equatable
|
||||
|
||||
public static func == (_ lhs: Self, _ rhs: Self) -> Bool {
|
||||
|
||||
return lhs.id == rhs.id
|
||||
&& lhs.values == rhs.values
|
||||
}
|
||||
|
||||
|
||||
// MARK: Hashable
|
||||
|
||||
public func hash(into hasher: inout Hasher) {
|
||||
|
||||
hasher.combine(self.id)
|
||||
hasher.combine(self.values)
|
||||
}
|
||||
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
internal init(id: ID, context: NSManagedObjectContext) {
|
||||
|
||||
self.id = id
|
||||
self.context = context
|
||||
self.values = O.cs_snapshotDictionary(id: id, context: context) as NSDictionary
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let context: NSManagedObjectContext
|
||||
private let values: NSDictionary
|
||||
}
|
||||
|
||||
|
||||
// MARK: - ObjectSnapshot where O: NSManagedObject
|
||||
|
||||
@available(*, unavailable, message: "KeyPaths accessed from @dynamicMemberLookup types can't generate KVC keys yet (https://bugs.swift.org/browse/SR-11351)")
|
||||
extension ObjectSnapshot where O: NSManagedObject {
|
||||
|
||||
/**
|
||||
Initializes an `ObjectSnapshot` instance by copying all attribute values from the given `NSManagedObject`.
|
||||
*/
|
||||
public init(from object: O) {
|
||||
|
||||
self.attributes = object.dictionaryWithValues(
|
||||
forKeys: Array(object.entity.attributesByName.keys)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Returns the value for the property identified by a given key.
|
||||
*/
|
||||
public subscript<V: AllowedObjectiveCKeyPathValue>(dynamicMember member: KeyPath<O, V>) -> V {
|
||||
|
||||
get {
|
||||
|
||||
let key = String(keyPath: member)
|
||||
return self.attributes[key]! as! V
|
||||
}
|
||||
set {
|
||||
|
||||
let key = String(keyPath: member)
|
||||
self.attributes[key] = newValue
|
||||
}
|
||||
|
||||
let key = String(keyPath: member)
|
||||
return self.values[key] as! V
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,61 +103,67 @@ extension ObjectSnapshot where O: NSManagedObject {
|
||||
// MARK: - ObjectSnapshot where O: CoreStoreObject
|
||||
|
||||
extension ObjectSnapshot where O: CoreStoreObject {
|
||||
|
||||
/**
|
||||
Initializes an `ObjectSnapshot` instance by copying all attribute values from the given `CoreStoreObject`.
|
||||
*/
|
||||
public init(from object: O) {
|
||||
|
||||
var attributes: [KeyPathString: Any] = [:]
|
||||
Self.initializeAttributes(
|
||||
mirror: Mirror(reflecting: object),
|
||||
object: object,
|
||||
into: &attributes
|
||||
)
|
||||
self.attributes = attributes
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Returns the value for the property identified by a given key.
|
||||
*/
|
||||
public subscript<K: AttributeKeyPathStringConvertible>(dynamicMember member: KeyPath<O, K>) -> K.ReturnValueType {
|
||||
|
||||
get {
|
||||
|
||||
let key = String(keyPath: member)
|
||||
return self.attributes[key]! as! K.ReturnValueType
|
||||
}
|
||||
set {
|
||||
|
||||
let key = String(keyPath: member)
|
||||
self.attributes[key] = newValue
|
||||
}
|
||||
public subscript<V>(dynamicMember member: KeyPath<O, ValueContainer<O>.Required<V>>) -> V {
|
||||
|
||||
let key = String(keyPath: member)
|
||||
return self.values[key] as! V
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private static func initializeAttributes(mirror: Mirror, object: CoreStoreObject, into attributes: inout [KeyPathString: Any]) {
|
||||
|
||||
if let superClassMirror = mirror.superclassMirror {
|
||||
|
||||
self.initializeAttributes(
|
||||
mirror: superClassMirror,
|
||||
object: object,
|
||||
into: &attributes
|
||||
)
|
||||
}
|
||||
for child in mirror.children {
|
||||
|
||||
switch child.value {
|
||||
|
||||
case let property as AttributeProtocol:
|
||||
attributes[property.keyPath] = property.valueForSnapshot
|
||||
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the value for the property identified by a given key.
|
||||
*/
|
||||
public subscript<V>(dynamicMember member: KeyPath<O, ValueContainer<O>.Optional<V>>) -> V? {
|
||||
|
||||
let key = String(keyPath: member)
|
||||
return self.values[key] as! V?
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the value for the property identified by a given key.
|
||||
*/
|
||||
public subscript<V>(dynamicMember member: KeyPath<O, TransformableContainer<O>.Required<V>>) -> V {
|
||||
|
||||
let key = String(keyPath: member)
|
||||
return self.values[key] as! V
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the value for the property identified by a given key.
|
||||
*/
|
||||
public subscript<V>(dynamicMember member: KeyPath<O, TransformableContainer<O>.Optional<V>>) -> V? {
|
||||
|
||||
let key = String(keyPath: member)
|
||||
return self.values[key] as! V?
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the value for the property identified by a given key.
|
||||
*/
|
||||
public subscript<D>(dynamicMember member: KeyPath<O, RelationshipContainer<O>.ToOne<D>>) -> D? {
|
||||
|
||||
let key = String(keyPath: member)
|
||||
return self.values[key] as! D?
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the value for the property identified by a given key.
|
||||
*/
|
||||
public subscript<D>(dynamicMember member: KeyPath<O, RelationshipContainer<O>.ToManyOrdered<D>>) -> [D] {
|
||||
|
||||
let key = String(keyPath: member)
|
||||
return self.values[key] as! [D]
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the value for the property identified by a given key.
|
||||
*/
|
||||
public subscript<D>(dynamicMember member: KeyPath<O, RelationshipContainer<O>.ToManyUnordered<D>>) -> Set<D> {
|
||||
|
||||
let key = String(keyPath: member)
|
||||
return self.values[key] as! Set<D>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -308,6 +308,11 @@ public enum RelationshipContainer<O: CoreStoreObject> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal var valueForSnapshot: Any {
|
||||
|
||||
return self.value as Any
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
@@ -595,6 +600,11 @@ public enum RelationshipContainer<O: CoreStoreObject> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal var valueForSnapshot: Any {
|
||||
|
||||
return self.value as Any
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
@@ -887,6 +897,11 @@ public enum RelationshipContainer<O: CoreStoreObject> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal var valueForSnapshot: Any {
|
||||
|
||||
return self.value as Any
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
@@ -42,4 +42,5 @@ internal protocol RelationshipProtocol: AnyObject {
|
||||
var renamingIdentifier: () -> String? { get }
|
||||
var minCount: Int { get }
|
||||
var maxCount: Int { get }
|
||||
var valueForSnapshot: Any { get }
|
||||
}
|
||||
|
||||
@@ -269,7 +269,8 @@ public enum TransformableContainer<O: CoreStoreObject> {
|
||||
}
|
||||
|
||||
internal var valueForSnapshot: Any {
|
||||
return self.value
|
||||
|
||||
return self.value as Any
|
||||
}
|
||||
|
||||
|
||||
@@ -486,6 +487,7 @@ public enum TransformableContainer<O: CoreStoreObject> {
|
||||
}
|
||||
|
||||
internal var valueForSnapshot: Any {
|
||||
|
||||
return self.value as Any
|
||||
}
|
||||
|
||||
|
||||
@@ -264,7 +264,8 @@ public enum ValueContainer<O: CoreStoreObject> {
|
||||
}
|
||||
|
||||
internal var valueForSnapshot: Any {
|
||||
return self.value
|
||||
|
||||
return self.value as Any
|
||||
}
|
||||
|
||||
|
||||
@@ -481,6 +482,7 @@ public enum ValueContainer<O: CoreStoreObject> {
|
||||
}
|
||||
|
||||
internal var valueForSnapshot: Any {
|
||||
|
||||
return self.value as Any
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user