diff --git a/CoreStore.xcodeproj/project.pbxproj b/CoreStore.xcodeproj/project.pbxproj index 96cc302..bee2174 100644 --- a/CoreStore.xcodeproj/project.pbxproj +++ b/CoreStore.xcodeproj/project.pbxproj @@ -70,10 +70,10 @@ 82BA18D81C4BBD7100A0916E /* Internals.WeakObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84F2D1AFF849C0064E85B /* Internals.WeakObject.swift */; }; 82BA18DC1C4BBD9C00A0916E /* Model.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = B5D372821A39CD6900F583D9 /* Model.xcdatamodeld */; }; 82BA18DD1C4BBE1400A0916E /* NSFetchedResultsController+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5202CF91C04688100DED140 /* NSFetchedResultsController+Convenience.swift */; }; - B501322A2344ECB500FC238B /* LiveList.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50132292344ECB500FC238B /* LiveList.swift */; }; - B501322B2346A9AE00FC238B /* LiveList.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50132292344ECB500FC238B /* LiveList.swift */; }; - B501322D2346A9B000FC238B /* LiveList.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50132292344ECB500FC238B /* LiveList.swift */; }; - B501322E2346A9B100FC238B /* LiveList.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50132292344ECB500FC238B /* LiveList.swift */; }; + B501322A2344ECB500FC238B /* ListPublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50132292344ECB500FC238B /* ListPublisher.swift */; }; + B501322B2346A9AE00FC238B /* ListPublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50132292344ECB500FC238B /* ListPublisher.swift */; }; + B501322D2346A9B000FC238B /* ListPublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50132292344ECB500FC238B /* ListPublisher.swift */; }; + B501322E2346A9B100FC238B /* ListPublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50132292344ECB500FC238B /* ListPublisher.swift */; }; B50132302346B76E00FC238B /* Internals.FetchedDiffableDataSourceSnapshotDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B501322F2346B76E00FC238B /* Internals.FetchedDiffableDataSourceSnapshotDelegate.swift */; }; B50132312346B76E00FC238B /* Internals.FetchedDiffableDataSourceSnapshotDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B501322F2346B76E00FC238B /* Internals.FetchedDiffableDataSourceSnapshotDelegate.swift */; }; B50132322346B76E00FC238B /* Internals.FetchedDiffableDataSourceSnapshotDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B501322F2346B76E00FC238B /* Internals.FetchedDiffableDataSourceSnapshotDelegate.swift */; }; @@ -600,10 +600,10 @@ B5BF7FBD234C99190070E741 /* Internals.DiffableDataUIDispatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FBB234C99190070E741 /* Internals.DiffableDataUIDispatcher.swift */; }; B5BF7FBE234C99190070E741 /* Internals.DiffableDataUIDispatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FBB234C99190070E741 /* Internals.DiffableDataUIDispatcher.swift */; }; B5BF7FBF234C99190070E741 /* Internals.DiffableDataUIDispatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FBB234C99190070E741 /* Internals.DiffableDataUIDispatcher.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 */; }; + B5BF7FC1234D7B2E0070E741 /* ObjectPublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FC0234D7B2E0070E741 /* ObjectPublisher.swift */; }; + B5BF7FC2234D7B2E0070E741 /* ObjectPublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FC0234D7B2E0070E741 /* ObjectPublisher.swift */; }; + B5BF7FC3234D7B2E0070E741 /* ObjectPublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FC0234D7B2E0070E741 /* ObjectPublisher.swift */; }; + B5BF7FC4234D7B2E0070E741 /* ObjectPublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FC0234D7B2E0070E741 /* ObjectPublisher.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 */; }; @@ -668,9 +668,9 @@ B5D8CA772346EAEE0055D7D1 /* DataStack+DataSources.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D8CA752346E7590055D7D1 /* DataStack+DataSources.swift */; }; B5D8CA782346EAEF0055D7D1 /* DataStack+DataSources.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D8CA752346E7590055D7D1 /* DataStack+DataSources.swift */; }; B5D8CA792346EAEF0055D7D1 /* DataStack+DataSources.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D8CA752346E7590055D7D1 /* DataStack+DataSources.swift */; }; - B5D8CA7C2346EC5F0055D7D1 /* LiveListTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D8CA7A2346EC550055D7D1 /* LiveListTests.swift */; }; - B5D8CA7D2346EC610055D7D1 /* LiveListTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D8CA7A2346EC550055D7D1 /* LiveListTests.swift */; }; - B5D8CA7E2346EC610055D7D1 /* LiveListTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D8CA7A2346EC550055D7D1 /* LiveListTests.swift */; }; + B5D8CA7C2346EC5F0055D7D1 /* ListPublisherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D8CA7A2346EC550055D7D1 /* ListPublisherTests.swift */; }; + B5D8CA7D2346EC610055D7D1 /* ListPublisherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D8CA7A2346EC550055D7D1 /* ListPublisherTests.swift */; }; + B5D8CA7E2346EC610055D7D1 /* ListPublisherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D8CA7A2346EC550055D7D1 /* ListPublisherTests.swift */; }; B5DAFB482203D9F8003FCCD0 /* Where.Expression.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5DAFB472203D9F8003FCCD0 /* Where.Expression.swift */; }; B5DAFB4A2203E01D003FCCD0 /* KeyPathGenericBindings.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5DAFB492203E01D003FCCD0 /* KeyPathGenericBindings.swift */; }; B5DBE2CD1C9914A900B5CEFA /* CSCoreStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5DBE2CC1C9914A900B5CEFA /* CSCoreStore.swift */; }; @@ -722,10 +722,6 @@ B5E2222C1CA51B6E00BA2E95 /* CSUnsafeDataTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E222291CA51B6E00BA2E95 /* CSUnsafeDataTransaction.swift */; }; B5E2222D1CA51B6E00BA2E95 /* CSUnsafeDataTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E222291CA51B6E00BA2E95 /* CSUnsafeDataTransaction.swift */; }; B5E2222E1CA51B6E00BA2E95 /* CSUnsafeDataTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E222291CA51B6E00BA2E95 /* CSUnsafeDataTransaction.swift */; }; - B5E294DD2349F8E7003E5956 /* SnapshotResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E294DC2349F8E7003E5956 /* SnapshotResult.swift */; }; - B5E294DE2349F8E7003E5956 /* SnapshotResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E294DC2349F8E7003E5956 /* SnapshotResult.swift */; }; - B5E294DF2349F8E7003E5956 /* SnapshotResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E294DC2349F8E7003E5956 /* SnapshotResult.swift */; }; - B5E294E02349F8E7003E5956 /* SnapshotResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E294DC2349F8E7003E5956 /* SnapshotResult.swift */; }; B5E41EC01EA9BB37006240F0 /* DynamicSchema+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E41EBF1EA9BB37006240F0 /* DynamicSchema+Convenience.swift */; }; 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 */; }; @@ -818,10 +814,6 @@ B5ECDC331CA81CDC00C7F112 /* CSCoreStore+Transaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5ECDC2E1CA81CDC00C7F112 /* CSCoreStore+Transaction.swift */; }; B5F1DA8D1B9AA97D007C5CBB /* ImportableObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F1DA8C1B9AA97D007C5CBB /* ImportableObject.swift */; }; B5F1DA901B9AA991007C5CBB /* ImportableUniqueObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F1DA8F1B9AA991007C5CBB /* ImportableUniqueObject.swift */; }; - B5F335582348D75D00FD649F /* LiveResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F335572348D75D00FD649F /* LiveResult.swift */; }; - B5F335592348D76300FD649F /* LiveResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F335572348D75D00FD649F /* LiveResult.swift */; }; - B5F3355A2348D76500FD649F /* LiveResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F335572348D75D00FD649F /* LiveResult.swift */; }; - B5F3355B2348D76500FD649F /* LiveResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F335572348D75D00FD649F /* LiveResult.swift */; }; B5F8496C234898240029D57B /* ListSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F8496B234898240029D57B /* ListSnapshot.swift */; }; B5F8496D234898240029D57B /* ListSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F8496B234898240029D57B /* ListSnapshot.swift */; }; B5F8496E234898240029D57B /* ListSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F8496B234898240029D57B /* ListSnapshot.swift */; }; @@ -884,7 +876,7 @@ 82BA18DE1C4BBE2600A0916E /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS9.1.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; 82BA18E01C4BBE2C00A0916E /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS9.1.sdk/System/Library/Frameworks/CoreData.framework; sourceTree = DEVELOPER_DIR; }; B500810F2290CDF800F4CEA5 /* bitrise.yml */ = {isa = PBXFileReference; lastKnownFileType = text; path = bitrise.yml; sourceTree = SOURCE_ROOT; }; - B50132292344ECB500FC238B /* LiveList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveList.swift; sourceTree = ""; }; + B50132292344ECB500FC238B /* ListPublisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListPublisher.swift; sourceTree = ""; }; B501322F2346B76E00FC238B /* Internals.FetchedDiffableDataSourceSnapshotDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Internals.FetchedDiffableDataSourceSnapshotDelegate.swift; sourceTree = ""; }; B501323623477F9300FC238B /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/System/Library/Frameworks/SwiftUI.framework; sourceTree = DEVELOPER_DIR; }; B501323823477FAC00FC238B /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; @@ -1021,7 +1013,7 @@ B5BF7FB1234C97910070E741 /* DiffableDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiffableDataSource.swift; sourceTree = ""; }; B5BF7FB6234C97CE0070E741 /* DiffableDataSource.TableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiffableDataSource.TableView.swift; sourceTree = ""; }; B5BF7FBB234C99190070E741 /* Internals.DiffableDataUIDispatcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Internals.DiffableDataUIDispatcher.swift; sourceTree = ""; }; - B5BF7FC0234D7B2E0070E741 /* LiveObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveObject.swift; sourceTree = ""; }; + B5BF7FC0234D7B2E0070E741 /* ObjectPublisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObjectPublisher.swift; sourceTree = ""; }; B5BF7FC5234D7E460070E741 /* ObjectSnapshot.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObjectSnapshot.swift; sourceTree = ""; }; B5BF7FCA234D80910070E741 /* Internals.LazyNonmutating.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Internals.LazyNonmutating.swift; sourceTree = ""; }; B5C976E21C6C9F6A00B1AF90 /* UnsafeDataTransaction+Observing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UnsafeDataTransaction+Observing.swift"; sourceTree = ""; }; @@ -1042,7 +1034,7 @@ B5D39A0119FD00C9000E91BB /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; B5D7A5B51CA3BF8F005C752B /* CSInto.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSInto.swift; sourceTree = ""; }; B5D8CA752346E7590055D7D1 /* DataStack+DataSources.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DataStack+DataSources.swift"; sourceTree = ""; }; - B5D8CA7A2346EC550055D7D1 /* LiveListTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveListTests.swift; sourceTree = ""; }; + B5D8CA7A2346EC550055D7D1 /* ListPublisherTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListPublisherTests.swift; sourceTree = ""; }; B5D9C8F61B160ED200E64F0E /* CoreStore.podspec */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; path = CoreStore.podspec; sourceTree = SOURCE_ROOT; }; B5DAFB472203D9F8003FCCD0 /* Where.Expression.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Where.Expression.swift; sourceTree = ""; }; B5DAFB492203E01D003FCCD0 /* KeyPathGenericBindings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyPathGenericBindings.swift; sourceTree = ""; }; @@ -1062,7 +1054,6 @@ B5E1B5A71CAA49E2007FD580 /* CSDataStack+Migrating.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CSDataStack+Migrating.swift"; sourceTree = ""; }; B5E222221CA4E12600BA2E95 /* CSSynchronousDataTransaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSSynchronousDataTransaction.swift; sourceTree = ""; }; B5E222291CA51B6E00BA2E95 /* CSUnsafeDataTransaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSUnsafeDataTransaction.swift; sourceTree = ""; }; - B5E294DC2349F8E7003E5956 /* SnapshotResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SnapshotResult.swift; sourceTree = ""; }; B5E41EBF1EA9BB37006240F0 /* DynamicSchema+Convenience.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DynamicSchema+Convenience.swift"; sourceTree = ""; }; B5E834B81B76311F001D3D50 /* BaseDataTransaction+Importing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BaseDataTransaction+Importing.swift"; sourceTree = ""; }; B5E84ED81AFF82360064E85B /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = SOURCE_ROOT; }; @@ -1115,7 +1106,6 @@ B5ECDC2E1CA81CDC00C7F112 /* CSCoreStore+Transaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CSCoreStore+Transaction.swift"; sourceTree = ""; }; B5F1DA8C1B9AA97D007C5CBB /* ImportableObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImportableObject.swift; sourceTree = ""; }; B5F1DA8F1B9AA991007C5CBB /* ImportableUniqueObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImportableUniqueObject.swift; sourceTree = ""; }; - B5F335572348D75D00FD649F /* LiveResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveResult.swift; sourceTree = ""; }; B5F8496B234898240029D57B /* ListSnapshot.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListSnapshot.swift; sourceTree = ""; }; B5F849702348A6690029D57B /* EnvironmentValues+DataSources.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "EnvironmentValues+DataSources.swift"; sourceTree = ""; }; B5FAD6A81B50A4B300714891 /* Progress+Convenience.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Progress+Convenience.swift"; sourceTree = ""; }; @@ -1264,7 +1254,7 @@ B5220E0B1D0D0D19009BC71E /* ImportTests.swift */, B525576B1CFAF18F00E51965 /* IntoTests.swift */, B5220E0F1D0DA6AB009BC71E /* ListObserverTests.swift */, - B5D8CA7A2346EC550055D7D1 /* LiveListTests.swift */, + B5D8CA7A2346EC550055D7D1 /* ListPublisherTests.swift */, B5DC47C51C93D22900FA3BF3 /* MigrationChainTests.swift */, B5220E071D0C5F8D009BC71E /* ObjectObserverTests.swift */, B52557771D02826E00E51965 /* OrderByTests.swift */, @@ -1597,15 +1587,13 @@ isa = PBXGroup; children = ( B5F849702348A6690029D57B /* EnvironmentValues+DataSources.swift */, - B50132292344ECB500FC238B /* LiveList.swift */, - B5BF7FC0234D7B2E0070E741 /* LiveObject.swift */, + B50132292344ECB500FC238B /* ListPublisher.swift */, B5F8496B234898240029D57B /* ListSnapshot.swift */, + B5BF7FC0234D7B2E0070E741 /* ObjectPublisher.swift */, B5BF7FC5234D7E460070E741 /* ObjectSnapshot.swift */, B55BB4D3235012AE00C33E34 /* ObjectRepresentation.swift */, B50EE14123473C92009B8C47 /* CoreStoreObject+DataSources.swift */, B5D8CA752346E7590055D7D1 /* DataStack+DataSources.swift */, - B5F335572348D75D00FD649F /* LiveResult.swift */, - B5E294DC2349F8E7003E5956 /* SnapshotResult.swift */, B5BF7FB1234C97910070E741 /* DiffableDataSource.swift */, B5BF7FB6234C97CE0070E741 /* DiffableDataSource.TableView.swift */, B5635D132356C39500B80E6B /* DiffableDataSource.CollectionView.swift */, @@ -2119,7 +2107,6 @@ B5D339E21E948C3600C880DE /* Value.swift in Sources */, B5A261211B64BFDB006EB6D3 /* MigrationType.swift in Sources */, B53FBA0B1CAB5E6500F0D40A /* CSCoreStore+Migrating.swift in Sources */, - B5E294DD2349F8E7003E5956 /* SnapshotResult.swift in Sources */, B5E84F141AFF847B0064E85B /* DataStack+Querying.swift in Sources */, B5D7A5B61CA3BF8F005C752B /* CSInto.swift in Sources */, B56007141B3F6C2800A9A8F9 /* SectionBy.swift in Sources */, @@ -2133,7 +2120,7 @@ B51B5C2D22D43E38009FA3BA /* KeyPath+KeyPaths.swift in Sources */, B50564D32350CC3100482308 /* PropertyProtocol.swift in Sources */, B5D8CA762346E7590055D7D1 /* DataStack+DataSources.swift in Sources */, - B5BF7FC1234D7B2E0070E741 /* LiveObject.swift in Sources */, + B5BF7FC1234D7B2E0070E741 /* ObjectPublisher.swift in Sources */, B5E1B5A81CAA49E2007FD580 /* CSDataStack+Migrating.swift in Sources */, B50132302346B76E00FC238B /* Internals.FetchedDiffableDataSourceSnapshotDelegate.swift in Sources */, B5D339F11E94AF5800C880DE /* CoreStoreStrings.swift in Sources */, @@ -2148,7 +2135,6 @@ B50E175C2351848E004F033C /* Internals.DiffableDataUIDispatcher.DiffResult.swift in Sources */, B5ECDC291CA81CC700C7F112 /* CSDataStack+Transaction.swift in Sources */, B56923F01EB827F6007C4DC9 /* XcodeSchemaMappingProvider.swift in Sources */, - B5F335582348D75D00FD649F /* LiveResult.swift in Sources */, B5E84F121AFF847B0064E85B /* OrderBy.swift in Sources */, B546F9581C99B17400D5AC55 /* CSCoreStore+Setup.swift in Sources */, B5635D142356C39500B80E6B /* DiffableDataSource.CollectionView.swift in Sources */, @@ -2269,7 +2255,7 @@ B5ECDC0B1CA8161B00C7F112 /* CSGroupBy.swift in Sources */, B5E84F151AFF847B0064E85B /* CoreStore+Querying.swift in Sources */, B5E84F241AFF84860064E85B /* ListObserver.swift in Sources */, - B501322A2344ECB500FC238B /* LiveList.swift in Sources */, + B501322A2344ECB500FC238B /* ListPublisher.swift in Sources */, B5F8496C234898240029D57B /* ListSnapshot.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -2283,7 +2269,7 @@ B5220E101D0DA6AB009BC71E /* ListObserverTests.swift in Sources */, B5519A401CA1B17B002BEF78 /* ErrorTests.swift in Sources */, B525577C1D0291FE00E51965 /* GroupByTests.swift in Sources */, - B5D8CA7C2346EC5F0055D7D1 /* LiveListTests.swift in Sources */, + B5D8CA7C2346EC5F0055D7D1 /* ListPublisherTests.swift in Sources */, B52557741D02791400E51965 /* WhereTests.swift in Sources */, B5DC47C61C93D22900FA3BF3 /* MigrationChainTests.swift in Sources */, B525576C1CFAF18F00E51965 /* IntoTests.swift in Sources */, @@ -2329,7 +2315,7 @@ 82BA18CE1C4BBD7100A0916E /* Internals.FetchedResultsControllerDelegate.swift in Sources */, B56923FB1EB82956007C4DC9 /* CSXcodeDataModelSchema.swift in Sources */, B55514EB1EED8BF900BAB888 /* From+Querying.swift in Sources */, - B5BF7FC2234D7B2E0070E741 /* LiveObject.swift in Sources */, + B5BF7FC2234D7B2E0070E741 /* ObjectPublisher.swift in Sources */, B596BBBC1DD5C39F001DCDD9 /* QueryableSource.swift in Sources */, B5ECDC011CA80CBA00C7F112 /* CSWhere.swift in Sources */, B55BB4D5235012AE00C33E34 /* ObjectRepresentation.swift in Sources */, @@ -2352,7 +2338,6 @@ 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 */, B50564D42350CC3100482308 /* PropertyProtocol.swift in Sources */, B53FBA141CAB63CB00F0D40A /* Progress+ObjectiveC.swift in Sources */, @@ -2455,7 +2440,6 @@ 82BA18AF1C4BBD3100A0916E /* CoreStore+Transaction.swift in Sources */, 82BA18CB1C4BBD6400A0916E /* NSManagedObject+Convenience.swift in Sources */, 82BA18B51C4BBD3F00A0916E /* BaseDataTransaction+Querying.swift in Sources */, - B5F335592348D76300FD649F /* LiveResult.swift in Sources */, B501FDDF1CA8D05000BE22EF /* CSSectionBy.swift in Sources */, B5BF7FAE234C41E90070E741 /* Internals.DiffableDataSourceSnapshot.swift in Sources */, B538BA781D15B3E30003A766 /* CoreStoreBridge.m in Sources */, @@ -2477,7 +2461,7 @@ B5E8A72121C1015300EF006A /* CoreStoreObject+Observing.swift in Sources */, B50E175D2351848E004F033C /* Internals.DiffableDataUIDispatcher.DiffResult.swift in Sources */, B5474D162227C08700B21FEC /* Internals.CoreStoreFetchRequest.swift in Sources */, - B501322B2346A9AE00FC238B /* LiveList.swift in Sources */, + B501322B2346A9AE00FC238B /* ListPublisher.swift in Sources */, B56924001EB82976007C4DC9 /* CSUnsafeDataModelSchema.swift in Sources */, B5215CAF1FA4812500139E3A /* SectionMonitorBuilder.swift in Sources */, 82BA18D61C4BBD7100A0916E /* NSManagedObjectContext+Transaction.swift in Sources */, @@ -2507,7 +2491,7 @@ B5220E111D0DA6AB009BC71E /* ListObserverTests.swift in Sources */, B5519A411CA1B17B002BEF78 /* ErrorTests.swift in Sources */, B525577D1D0291FE00E51965 /* GroupByTests.swift in Sources */, - B5D8CA7D2346EC610055D7D1 /* LiveListTests.swift in Sources */, + B5D8CA7D2346EC610055D7D1 /* ListPublisherTests.swift in Sources */, B52557751D02791400E51965 /* WhereTests.swift in Sources */, B5DC47C71C93D22900FA3BF3 /* MigrationChainTests.swift in Sources */, B5DBE2E01C9939E100B5CEFA /* BridgingTests.m in Sources */, @@ -2553,7 +2537,7 @@ B52DD1951BE1F92500949AFE /* CoreStoreError.swift in Sources */, B56923FD1EB82956007C4DC9 /* CSXcodeDataModelSchema.swift in Sources */, B55514ED1EED8BF900BAB888 /* From+Querying.swift in Sources */, - B5BF7FC4234D7B2E0070E741 /* LiveObject.swift in Sources */, + B5BF7FC4234D7B2E0070E741 /* ObjectPublisher.swift in Sources */, B596BBBE1DD5C39F001DCDD9 /* QueryableSource.swift in Sources */, B546F9601C9A12B800D5AC55 /* CSSQliteStore.swift in Sources */, B55BB4D7235012AE00C33E34 /* ObjectRepresentation.swift in Sources */, @@ -2576,7 +2560,6 @@ 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 */, B50564D62350CC3100482308 /* PropertyProtocol.swift in Sources */, B5831B781F34AC7A00A9F647 /* RelationshipProtocol.swift in Sources */, @@ -2679,7 +2662,6 @@ B5220E191D130761009BC71E /* ListMonitor.swift in Sources */, B5220E181D130711009BC71E /* ObjectObserver.swift in Sources */, B5220E251D13088E009BC71E /* ListObserver.swift in Sources */, - B5F3355B2348D76500FD649F /* LiveResult.swift in Sources */, B538BA7A1D15B3E30003A766 /* CoreStoreBridge.m in Sources */, B5BF7FB0234C41E90070E741 /* Internals.DiffableDataSourceSnapshot.swift in Sources */, B51260821E97A18000402229 /* CoreStoreObject+Convenience.swift in Sources */, @@ -2701,7 +2683,7 @@ B5E8A72321C1015300EF006A /* CoreStoreObject+Observing.swift in Sources */, B50E175F2351848E004F033C /* Internals.DiffableDataUIDispatcher.DiffResult.swift in Sources */, B5474D182227C08700B21FEC /* Internals.CoreStoreFetchRequest.swift in Sources */, - B501322E2346A9B100FC238B /* LiveList.swift in Sources */, + B501322E2346A9B100FC238B /* ListPublisher.swift in Sources */, B56924021EB82976007C4DC9 /* CSUnsafeDataModelSchema.swift in Sources */, B5215CB11FA4812500139E3A /* SectionMonitorBuilder.swift in Sources */, B5220E171D1306DF009BC71E /* UnsafeDataTransaction+Observing.swift in Sources */, @@ -2731,7 +2713,7 @@ B5220E271D1308D1009BC71E /* ObjectObserverTests.swift in Sources */, B525577E1D0291FE00E51965 /* GroupByTests.swift in Sources */, B52557761D02791400E51965 /* WhereTests.swift in Sources */, - B5D8CA7E2346EC610055D7D1 /* LiveListTests.swift in Sources */, + B5D8CA7E2346EC610055D7D1 /* ListPublisherTests.swift in Sources */, B5DC47C81C93D22900FA3BF3 /* MigrationChainTests.swift in Sources */, B5DBE2E11C9939E100B5CEFA /* BridgingTests.m in Sources */, B5220E0E1D0D0D19009BC71E /* ImportTests.swift in Sources */, @@ -2777,7 +2759,7 @@ B5ECDC021CA80CBA00C7F112 /* CSWhere.swift in Sources */, B56923FC1EB82956007C4DC9 /* CSXcodeDataModelSchema.swift in Sources */, B55514EC1EED8BF900BAB888 /* From+Querying.swift in Sources */, - B5BF7FC3234D7B2E0070E741 /* LiveObject.swift in Sources */, + B5BF7FC3234D7B2E0070E741 /* ObjectPublisher.swift in Sources */, B596BBBD1DD5C39F001DCDD9 /* QueryableSource.swift in Sources */, B5ECDC081CA8138100C7F112 /* CSOrderBy.swift in Sources */, B55BB4D6235012AE00C33E34 /* ObjectRepresentation.swift in Sources */, @@ -2800,7 +2782,6 @@ 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 */, B50564D52350CC3100482308 /* PropertyProtocol.swift in Sources */, B5E1B5AB1CAA49E2007FD580 /* CSDataStack+Migrating.swift in Sources */, @@ -2903,7 +2884,6 @@ B5DE522D230BD7D600A22534 /* Internals.swift in Sources */, B56321851BD65216006C9394 /* CoreStore+Logging.swift in Sources */, B56321921BD65216006C9394 /* BaseDataTransaction+Querying.swift in Sources */, - B5F3355A2348D76500FD649F /* LiveResult.swift in Sources */, B501FDE01CA8D05000BE22EF /* CSSectionBy.swift in Sources */, B5BF7FAF234C41E90070E741 /* Internals.DiffableDataSourceSnapshot.swift in Sources */, B538BA791D15B3E30003A766 /* CoreStoreBridge.m in Sources */, @@ -2925,7 +2905,7 @@ B5E8A72221C1015300EF006A /* CoreStoreObject+Observing.swift in Sources */, B50E175E2351848E004F033C /* Internals.DiffableDataUIDispatcher.DiffResult.swift in Sources */, B5474D172227C08700B21FEC /* Internals.CoreStoreFetchRequest.swift in Sources */, - B501322D2346A9B000FC238B /* LiveList.swift in Sources */, + B501322D2346A9B000FC238B /* ListPublisher.swift in Sources */, B56924011EB82976007C4DC9 /* CSUnsafeDataModelSchema.swift in Sources */, B5215CB01FA4812500139E3A /* SectionMonitorBuilder.swift in Sources */, B56321B41BD6521C006C9394 /* NSManagedObjectContext+Transaction.swift in Sources */, diff --git a/CoreStoreDemo/CoreStoreDemo/Base.lproj/Main.storyboard b/CoreStoreDemo/CoreStoreDemo/Base.lproj/Main.storyboard index 3805cbc..d00ba9a 100644 --- a/CoreStoreDemo/CoreStoreDemo/Base.lproj/Main.storyboard +++ b/CoreStoreDemo/CoreStoreDemo/Base.lproj/Main.storyboard @@ -717,7 +717,7 @@ - + @@ -768,7 +768,7 @@ - + @@ -898,7 +898,7 @@ - + @@ -963,7 +963,7 @@ - + diff --git a/CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/CollectionViewDemoViewController.swift b/CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/CollectionViewDemoViewController.swift index 7a4175c..362ab99 100644 --- a/CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/CollectionViewDemoViewController.swift +++ b/CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/CollectionViewDemoViewController.swift @@ -75,14 +75,14 @@ final class CollectionViewDemoViewController: UICollectionViewController { } } ) - ColorsDemo.palettes.addObserver(self) { [weak self] (liveList) in + ColorsDemo.palettes.addObserver(self) { [weak self] (listPublisher) in guard let self = self else { return } self.filterBarButton?.title = ColorsDemo.filter.rawValue - self.dataSource?.apply(liveList.snapshot, animatingDifferences: true) + self.dataSource?.apply(listPublisher.snapshot, animatingDifferences: true) } self.dataSource?.apply(ColorsDemo.palettes.snapshot, animatingDifferences: false) } @@ -93,7 +93,7 @@ final class CollectionViewDemoViewController: UICollectionViewController { switch (segue.identifier, segue.destination, sender) { - case ("ObjectObserverDemoViewController"?, let destinationViewController as ObjectObserverDemoViewController, let palette as LiveObject): + case ("ObjectObserverDemoViewController"?, let destinationViewController as ObjectObserverDemoViewController, let palette as ObjectPublisher): destinationViewController.setPalette(palette) default: diff --git a/CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/ColorsDemo.swift b/CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/ColorsDemo.swift index d012c15..931aa0b 100644 --- a/CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/ColorsDemo.swift +++ b/CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/ColorsDemo.swift @@ -75,9 +75,9 @@ struct ColorsDemo { return dataStack }() - static let palettes: LiveList = { + static let palettes: ListPublisher = { - return ColorsDemo.stack.liveList( + return ColorsDemo.stack.listPublisher( From() .sectionBy(\.colorName) .orderBy(.ascending(\.hue)) diff --git a/CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/ListObserverDemoViewController.swift b/CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/ListObserverDemoViewController.swift index 72daae2..14f614f 100644 --- a/CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/ListObserverDemoViewController.swift +++ b/CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/ListObserverDemoViewController.swift @@ -87,14 +87,14 @@ final class ListObserverDemoViewController: UITableViewController { return cell } ) - ColorsDemo.palettes.addObserver(self) { [weak self] (liveList) in + ColorsDemo.palettes.addObserver(self) { [weak self] (listPublisher) in guard let self = self else { return } self.filterBarButton?.title = ColorsDemo.filter.rawValue - self.dataSource?.apply(liveList.snapshot, animatingDifferences: true) + self.dataSource?.apply(listPublisher.snapshot, animatingDifferences: true) } self.dataSource?.apply(ColorsDemo.palettes.snapshot, animatingDifferences: false) } @@ -105,7 +105,7 @@ final class ListObserverDemoViewController: UITableViewController { switch (segue.identifier, segue.destination, sender) { - case ("ObjectObserverDemoViewController"?, let destinationViewController as ObjectObserverDemoViewController, let palette as LiveObject): + case ("ObjectObserverDemoViewController"?, let destinationViewController as ObjectObserverDemoViewController, let palette as ObjectPublisher): destinationViewController.setPalette(palette) default: diff --git a/CoreStoreDemo/CoreStoreDemo/SwiftUI Demo/SwiftUIContainerViewController.swift b/CoreStoreDemo/CoreStoreDemo/SwiftUI Demo/SwiftUIContainerViewController.swift index ea0fbad..c6bd6d1 100644 --- a/CoreStoreDemo/CoreStoreDemo/SwiftUI Demo/SwiftUIContainerViewController.swift +++ b/CoreStoreDemo/CoreStoreDemo/SwiftUI Demo/SwiftUIContainerViewController.swift @@ -25,7 +25,7 @@ final class SwiftUIContainerViewController: UIViewController { let hostingController = UIHostingController( rootView: SwiftUIView( - palettes: ColorsDemo.stack.liveList( + palettes: ColorsDemo.stack.listPublisher( From() .sectionBy(\.colorName) .orderBy(.ascending(\.hue)) diff --git a/CoreStoreDemo/CoreStoreDemo/SwiftUI Demo/SwiftUIView.swift b/CoreStoreDemo/CoreStoreDemo/SwiftUI Demo/SwiftUIView.swift index 5c2ab8c..8b727cd 100644 --- a/CoreStoreDemo/CoreStoreDemo/SwiftUI Demo/SwiftUIView.swift +++ b/CoreStoreDemo/CoreStoreDemo/SwiftUI Demo/SwiftUIView.swift @@ -18,7 +18,7 @@ struct SwiftUIView: View { var dataStack: DataStack @ObservedObject - var palettes: LiveList + var palettes: ListPublisher var body: some View { NavigationView { @@ -92,7 +92,7 @@ struct SwiftUIView: View { content: { Alert( title: Text("SwiftUI Binding Demo"), - message: Text("This demo shows how to bind to LiveList and to CoreStoreObject when using SwiftUI"), + message: Text("This demo shows how to bind to ListPublisher and to CoreStoreObject when using SwiftUI"), dismissButton: .cancel(Text("OK")) ) } @@ -112,7 +112,7 @@ struct SwiftUIView: View { struct ColorCell: View { @ObservedObject - var palette: LiveObject + var palette: ObjectPublisher var body: some View { HStack { @@ -131,13 +131,13 @@ struct DetailView: View { var dataStack: DataStack @ObservedObject - var palette: LiveObject + var palette: ObjectPublisher @State var hue: Float = 0 @State var saturation: Float = 0 @State var brightness: Float = 0 - init(palette: LiveObject) { + init(palette: ObjectPublisher) { self.palette = palette self.hue = Float(palette.hue ?? 0) @@ -166,7 +166,7 @@ struct SwiftUIView_Previews: PreviewProvider { static var previews: some View { SwiftUIView( - palettes: ColorsDemo.stack.liveList( + palettes: ColorsDemo.stack.listPublisher( From() .sectionBy(\.colorName) .orderBy(.ascending(\.hue)) diff --git a/CoreStoreTests/LiveListTests.swift b/CoreStoreTests/ListPublisherTests.swift similarity index 98% rename from CoreStoreTests/LiveListTests.swift rename to CoreStoreTests/ListPublisherTests.swift index 0d8184c..c17e179 100644 --- a/CoreStoreTests/LiveListTests.swift +++ b/CoreStoreTests/ListPublisherTests.swift @@ -1,5 +1,5 @@ // -// LiveListTests.swift +// ListPublisherTests.swift // CoreStore iOS // // Copyright © 2018 John Rommel Estropia @@ -31,18 +31,18 @@ import XCTest import CoreStore -// MARK: - LiveListTests +// MARK: - ListPublisherTests @available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) -class LiveListTests: BaseTestDataTestCase { +class ListPublisherTests: BaseTestDataTestCase { @objc - dynamic func test_ThatLiveLists_CanReceiveInsertNotifications() { + dynamic func test_ThatListPublishers_CanReceiveInsertNotifications() { self.prepareStack { (stack) in // let observer = TestListObserver() - let liveList = stack.liveList( + let listPublisher = stack.listPublisher( From(), SectionBy(#keyPath(TestEntity1.testBoolean)), OrderBy(.ascending(#keyPath(TestEntity1.testBoolean)), .ascending(#keyPath(TestEntity1.testEntityID))) @@ -160,7 +160,7 @@ class LiveListTests: BaseTestDataTestCase { ) self.waitAndCheckExpectations() - withExtendedLifetime(liveList, {}) + withExtendedLifetime(listPublisher, {}) } } diff --git a/Sources/CoreStoreObject+DataSources.swift b/Sources/CoreStoreObject+DataSources.swift index 3147b2c..52155f4 100644 --- a/Sources/CoreStoreObject+DataSources.swift +++ b/Sources/CoreStoreObject+DataSources.swift @@ -11,7 +11,7 @@ #if canImport(Combine) import Combine -// MARK: - LiveList: ObservableObject +// MARK: - ListPublisher: ObservableObject @available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) extension CoreStoreObject: ObservableObject { diff --git a/Sources/DataStack+DataSources.swift b/Sources/DataStack+DataSources.swift index 827bfc7..1e899c1 100644 --- a/Sources/DataStack+DataSources.swift +++ b/Sources/DataStack+DataSources.swift @@ -34,24 +34,24 @@ import CoreData extension DataStack { /** - Creates a `LiveObject` for the specified `DynamicObject`. Multiple objects may then register themselves to be notified when changes are made to the `DynamicObject`. + Creates a `ObjectPublisher` for the specified `DynamicObject`. Multiple objects may then register themselves to be notified when changes are made to the `DynamicObject`. - parameter object: the `DynamicObject` to observe changes from - - returns: a `LiveObject` that broadcasts changes to `object` + - returns: a `ObjectPublisher` that broadcasts changes to `object` */ - public func liveObject(_ object: O) -> LiveObject { + public func objectPublisher(_ object: O) -> ObjectPublisher { - return LiveObject(objectID: object.cs_id(), context: self.unsafeContext()) + return ObjectPublisher(objectID: object.cs_id(), context: self.unsafeContext()) } - public func liveList(_ from: From, _ fetchClauses: FetchClause...) -> LiveList { + public func listPublisher(_ from: From, _ fetchClauses: FetchClause...) -> ListPublisher { - return self.liveList(from, fetchClauses) + return self.listPublisher(from, fetchClauses) } - public func liveList(_ from: From, _ fetchClauses: [FetchClause]) -> LiveList { + public func listPublisher(_ from: From, _ fetchClauses: [FetchClause]) -> ListPublisher { - return LiveList( + return ListPublisher( dataStack: self, from: from, sectionBy: nil, @@ -61,31 +61,31 @@ extension DataStack { Internals.assert( fetchRequest.sortDescriptors?.isEmpty == false, - "An \(Internals.typeName(LiveList.self)) requires a sort information. Specify from a \(Internals.typeName(OrderBy.self)) clause or any custom \(Internals.typeName(FetchClause.self)) that provides a sort descriptor." + "An \(Internals.typeName(ListPublisher.self)) requires a sort information. Specify from a \(Internals.typeName(OrderBy.self)) clause or any custom \(Internals.typeName(FetchClause.self)) that provides a sort descriptor." ) } ) } - public func liveList(_ clauseChain: B) -> LiveList { + public func listPublisher(_ clauseChain: B) -> ListPublisher { - return self.liveList( + return self.listPublisher( clauseChain.from, clauseChain.fetchClauses ) } - public func liveList(createAsynchronously: @escaping (LiveList) -> Void, _ from: From, _ fetchClauses: FetchClause...) { + public func listPublisher(createAsynchronously: @escaping (ListPublisher) -> Void, _ from: From, _ fetchClauses: FetchClause...) { - self.liveList( + self.listPublisher( createAsynchronously: createAsynchronously, from, fetchClauses ) } - public func liveList(createAsynchronously: @escaping (LiveList) -> Void, _ from: From, _ fetchClauses: [FetchClause]) { + public func listPublisher(createAsynchronously: @escaping (ListPublisher) -> Void, _ from: From, _ fetchClauses: [FetchClause]) { - _ = LiveList( + _ = ListPublisher( dataStack: self, from: from, sectionBy: nil, @@ -95,25 +95,25 @@ extension DataStack { Internals.assert( fetchRequest.sortDescriptors?.isEmpty == false, - "An \(Internals.typeName(LiveList.self)) requires a sort information. Specify from a \(Internals.typeName(OrderBy.self)) clause or any custom \(Internals.typeName(FetchClause.self)) that provides a sort descriptor." + "An \(Internals.typeName(ListPublisher.self)) requires a sort information. Specify from a \(Internals.typeName(OrderBy.self)) clause or any custom \(Internals.typeName(FetchClause.self)) that provides a sort descriptor." ) }, createAsynchronously: createAsynchronously ) } - public func liveList(_ from: From, _ sectionBy: SectionBy, _ fetchClauses: FetchClause...) -> LiveList { + public func listPublisher(_ from: From, _ sectionBy: SectionBy, _ fetchClauses: FetchClause...) -> ListPublisher { - return self.liveList( + return self.listPublisher( from, sectionBy, fetchClauses ) } - public func liveList(_ from: From, _ sectionBy: SectionBy, _ fetchClauses: [FetchClause]) -> LiveList { + public func listPublisher(_ from: From, _ sectionBy: SectionBy, _ fetchClauses: [FetchClause]) -> ListPublisher { - return LiveList( + return ListPublisher( dataStack: self, from: from, sectionBy: sectionBy, @@ -123,24 +123,24 @@ extension DataStack { Internals.assert( fetchRequest.sortDescriptors?.isEmpty == false, - "An \(Internals.typeName(LiveList.self)) requires a sort information. Specify from a \(Internals.typeName(OrderBy.self)) clause or any custom \(Internals.typeName(FetchClause.self)) that provides a sort descriptor." + "An \(Internals.typeName(ListPublisher.self)) requires a sort information. Specify from a \(Internals.typeName(OrderBy.self)) clause or any custom \(Internals.typeName(FetchClause.self)) that provides a sort descriptor." ) } ) } - public func liveList(_ clauseChain: B) -> LiveList { + public func listPublisher(_ clauseChain: B) -> ListPublisher { - return self.liveList( + return self.listPublisher( clauseChain.from, clauseChain.sectionBy, clauseChain.fetchClauses ) } - public func liveList(createAsynchronously: @escaping (LiveList) -> Void, _ from: From, _ sectionBy: SectionBy, _ fetchClauses: FetchClause...) { + public func listPublisher(createAsynchronously: @escaping (ListPublisher) -> Void, _ from: From, _ sectionBy: SectionBy, _ fetchClauses: FetchClause...) { - self.liveList( + self.listPublisher( createAsynchronously: createAsynchronously, from, sectionBy, @@ -148,9 +148,9 @@ extension DataStack { ) } - public func liveList(createAsynchronously: @escaping (LiveList) -> Void, _ from: From, _ sectionBy: SectionBy, _ fetchClauses: [FetchClause]) { + public func listPublisher(createAsynchronously: @escaping (ListPublisher) -> Void, _ from: From, _ sectionBy: SectionBy, _ fetchClauses: [FetchClause]) { - _ = LiveList( + _ = ListPublisher( dataStack: self, from: from, sectionBy: sectionBy, @@ -160,16 +160,16 @@ extension DataStack { Internals.assert( fetchRequest.sortDescriptors?.isEmpty == false, - "An \(Internals.typeName(LiveList.self)) requires a sort information. Specify from a \(Internals.typeName(OrderBy.self)) clause or any custom \(Internals.typeName(FetchClause.self)) that provides a sort descriptor." + "An \(Internals.typeName(ListPublisher.self)) requires a sort information. Specify from a \(Internals.typeName(OrderBy.self)) clause or any custom \(Internals.typeName(FetchClause.self)) that provides a sort descriptor." ) }, createAsynchronously: createAsynchronously ) } - public func liveList(createAsynchronously: @escaping (LiveList) -> Void, _ clauseChain: B) { + public func listPublisher(createAsynchronously: @escaping (ListPublisher) -> Void, _ clauseChain: B) { - self.liveList( + self.listPublisher( createAsynchronously: createAsynchronously, clauseChain.from, clauseChain.sectionBy, diff --git a/Sources/ListMonitor.swift b/Sources/ListMonitor.swift index 74e0d63..85b2ab6 100644 --- a/Sources/ListMonitor.swift +++ b/Sources/ListMonitor.swift @@ -67,14 +67,14 @@ import CoreData In the example above, both `person1` and `person2` will contain the object at section=2, index=3. */ @available(macOS 10.12, *) -public final class ListMonitor: Hashable { +public final class ListMonitor: Hashable { // MARK: Public (Accessors) /** The type for the objects contained bye the `ListMonitor` */ - public typealias ObjectType = D + public typealias ObjectType = O /** Returns the object at the given index within the first section. This subscript indexer is typically used for `ListMonitor`s created with `monitorList(_:)`. @@ -82,7 +82,7 @@ public final class ListMonitor: Hashable { - parameter index: the index of the object. Using an index above the valid range will raise an exception. - returns: the `DynamicObject` at the specified index */ - public subscript(index: Int) -> ObjectType { + public subscript(index: Int) -> O { Internals.assert( !self.isPendingRefetch || Thread.isMainThread, @@ -90,7 +90,7 @@ public final class ListMonitor: Hashable { ) if self.isSectioned { - return ObjectType.cs_fromRaw(object: (self.fetchedResultsController.fetchedObjects as NSArray?)![index] as! NSManagedObject) + return O.cs_fromRaw(object: (self.fetchedResultsController.fetchedObjects as NSArray?)![index] as! NSManagedObject) } return self[0, index] } @@ -101,14 +101,14 @@ public final class ListMonitor: Hashable { - parameter index: the index for the object. Using an index above the valid range will return `nil`. - returns: the `DynamicObject` at the specified index, or `nil` if out of bounds */ - public subscript(safeIndex index: Int) -> ObjectType? { + public subscript(safeIndex index: Int) -> O? { if self.isSectioned { let fetchedObjects = (self.fetchedResultsController.fetchedObjects as NSArray?)! if index < fetchedObjects.count && index >= 0 { - return ObjectType.cs_fromRaw(object: fetchedObjects[index] as! NSManagedObject) + return O.cs_fromRaw(object: fetchedObjects[index] as! NSManagedObject) } return nil } @@ -122,7 +122,7 @@ public final class ListMonitor: Hashable { - parameter itemIndex: the index for the object within the section. Using an `itemIndex` with an invalid range will raise an exception. - returns: the `DynamicObject` at the specified section and item index */ - public subscript(sectionIndex: Int, itemIndex: Int) -> ObjectType { + public subscript(sectionIndex: Int, itemIndex: Int) -> O { return self[IndexPath(indexes: [sectionIndex, itemIndex])] } @@ -134,7 +134,7 @@ public final class ListMonitor: Hashable { - parameter itemIndex: the index for the object within the section. Using an `itemIndex` with an invalid range will return `nil`. - returns: the `DynamicObject` at the specified section and item index, or `nil` if out of bounds */ - public subscript(safeSectionIndex sectionIndex: Int, safeItemIndex itemIndex: Int) -> ObjectType? { + public subscript(safeSectionIndex sectionIndex: Int, safeItemIndex itemIndex: Int) -> O? { guard let section = self.sectionInfo(safelyAt: sectionIndex) else { @@ -153,13 +153,13 @@ public final class ListMonitor: Hashable { - parameter indexPath: the `IndexPath` for the object. Using an `indexPath` with an invalid range will raise an exception. - returns: the `DynamicObject` at the specified index path */ - public subscript(indexPath: IndexPath) -> ObjectType { + public subscript(indexPath: IndexPath) -> O { Internals.assert( !self.isPendingRefetch || Thread.isMainThread, "Attempted to access a \(Internals.typeName(self)) outside the main thread while a refetch is in progress." ) - return ObjectType.cs_fromRaw(object: self.fetchedResultsController.object(at: indexPath)) + return O.cs_fromRaw(object: self.fetchedResultsController.object(at: indexPath)) } /** @@ -168,7 +168,7 @@ public final class ListMonitor: Hashable { - parameter indexPath: the `IndexPath` for the object. Using an `indexPath` with an invalid range will return `nil`. - returns: the `DynamicObject` at the specified index path, or `nil` if out of bounds */ - public subscript(safeIndexPath indexPath: IndexPath) -> ObjectType? { + public subscript(safeIndexPath indexPath: IndexPath) -> O? { return self[ safeSectionIndex: indexPath[0], @@ -345,7 +345,7 @@ public final class ListMonitor: Hashable { - parameter object: the `DynamicObject` to search the index of - returns: the index of the `DynamicObject` if it exists in the `ListMonitor`'s fetched objects, or `nil` if not found. */ - public func index(of object: ObjectType) -> Int? { + public func index(of object: O) -> Int? { Internals.assert( !self.isPendingRefetch || Thread.isMainThread, @@ -364,7 +364,7 @@ public final class ListMonitor: Hashable { - parameter object: the `DynamicObject` to search the index of - returns: the `IndexPath` of the `DynamicObject` if it exists in the `ListMonitor`'s fetched objects, or `nil` if not found. */ - public func indexPath(of object: ObjectType) -> IndexPath? { + public func indexPath(of object: O) -> IndexPath? { Internals.assert( !self.isPendingRefetch || Thread.isMainThread, @@ -387,7 +387,7 @@ public final class ListMonitor: Hashable { - parameter observer: a `ListObserver` to send change notifications to */ - public func addObserver(_ observer: U) where U.ListEntityType == ObjectType { + public func addObserver(_ observer: U) where U.ListEntityType == O { self.unregisterObserver(observer) self.registerObserver( @@ -422,7 +422,7 @@ public final class ListMonitor: Hashable { - parameter observer: a `ListObjectObserver` to send change notifications to */ - public func addObserver(_ observer: U) where U.ListEntityType == ObjectType { + public func addObserver(_ observer: U) where U.ListEntityType == O { self.unregisterObserver(observer) self.registerObserver( @@ -476,7 +476,7 @@ public final class ListMonitor: Hashable { - parameter observer: a `ListSectionObserver` to send change notifications to */ - public func addObserver(_ observer: U) where U.ListEntityType == ObjectType { + public func addObserver(_ observer: U) where U.ListEntityType == O { self.unregisterObserver(observer) self.registerObserver( @@ -537,7 +537,7 @@ public final class ListMonitor: Hashable { - parameter observer: a `ListObserver` to unregister notifications to */ - public func removeObserver(_ observer: U) where U.ListEntityType == ObjectType { + public func removeObserver(_ observer: U) where U.ListEntityType == O { self.unregisterObserver(observer) } @@ -597,7 +597,7 @@ public final class ListMonitor: Hashable { // MARK: Equatable - public static func == (lhs: ListMonitor, rhs: ListMonitor) -> Bool { + public static func == (lhs: ListMonitor, rhs: ListMonitor) -> Bool { return lhs.fetchedResultsController === rhs.fetchedResultsController } @@ -607,7 +607,7 @@ public final class ListMonitor: Hashable { return lhs.fetchedResultsController === rhs.fetchedResultsController } - public static func ~= (lhs: ListMonitor, rhs: ListMonitor) -> Bool { + public static func ~= (lhs: ListMonitor, rhs: ListMonitor) -> Bool { return lhs.fetchedResultsController === rhs.fetchedResultsController } @@ -628,7 +628,7 @@ public final class ListMonitor: Hashable { // MARK: Internal - internal convenience init(dataStack: DataStack, from: From, sectionBy: SectionBy?, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest) -> Void) { + internal convenience init(dataStack: DataStack, from: From, sectionBy: SectionBy?, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest) -> Void) { self.init( context: dataStack.mainContext, @@ -640,7 +640,7 @@ public final class ListMonitor: Hashable { ) } - internal convenience init(dataStack: DataStack, from: From, sectionBy: SectionBy?, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest) -> Void, createAsynchronously: @escaping (ListMonitor) -> Void) { + internal convenience init(dataStack: DataStack, from: From, sectionBy: SectionBy?, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest) -> Void, createAsynchronously: @escaping (ListMonitor) -> Void) { self.init( context: dataStack.mainContext, @@ -652,7 +652,7 @@ public final class ListMonitor: Hashable { ) } - internal convenience init(unsafeTransaction: UnsafeDataTransaction, from: From, sectionBy: SectionBy?, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest) -> Void) { + internal convenience init(unsafeTransaction: UnsafeDataTransaction, from: From, sectionBy: SectionBy?, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest) -> Void) { self.init( context: unsafeTransaction.context, @@ -664,7 +664,7 @@ public final class ListMonitor: Hashable { ) } - internal convenience init(unsafeTransaction: UnsafeDataTransaction, from: From, sectionBy: SectionBy?, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest) -> Void, createAsynchronously: @escaping (ListMonitor) -> Void) { + internal convenience init(unsafeTransaction: UnsafeDataTransaction, from: From, sectionBy: SectionBy?, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest) -> Void, createAsynchronously: @escaping (ListMonitor) -> Void) { self.init( context: unsafeTransaction.context, @@ -676,7 +676,7 @@ public final class ListMonitor: Hashable { ) } - internal func registerChangeNotification(_ notificationKey: UnsafeRawPointer, name: Notification.Name, toObserver observer: AnyObject, callback: @escaping (_ monitor: ListMonitor) -> Void) { + internal func registerChangeNotification(_ notificationKey: UnsafeRawPointer, name: Notification.Name, toObserver observer: AnyObject, callback: @escaping (_ monitor: ListMonitor) -> Void) { Internals.setAssociatedRetainedObject( Internals.NotificationObserver( @@ -696,7 +696,7 @@ public final class ListMonitor: Hashable { ) } - internal func registerObjectNotification(_ notificationKey: UnsafeRawPointer, name: Notification.Name, toObserver observer: AnyObject, callback: @escaping (_ monitor: ListMonitor, _ object: ObjectType, _ indexPath: IndexPath?, _ newIndexPath: IndexPath?) -> Void) { + internal func registerObjectNotification(_ notificationKey: UnsafeRawPointer, name: Notification.Name, toObserver observer: AnyObject, callback: @escaping (_ monitor: ListMonitor, _ object: O, _ indexPath: IndexPath?, _ newIndexPath: IndexPath?) -> Void) { Internals.setAssociatedRetainedObject( Internals.NotificationObserver( @@ -712,7 +712,7 @@ public final class ListMonitor: Hashable { } callback( self, - ObjectType.cs_fromRaw(object: rawObject), + O.cs_fromRaw(object: rawObject), userInfo[String(describing: IndexPath.self)] as? IndexPath, userInfo["\(String(describing: IndexPath.self)).New"] as? IndexPath ) @@ -723,7 +723,7 @@ public final class ListMonitor: Hashable { ) } - internal func registerSectionNotification(_ notificationKey: UnsafeRawPointer, name: Notification.Name, toObserver observer: AnyObject, callback: @escaping (_ monitor: ListMonitor, _ sectionInfo: NSFetchedResultsSectionInfo, _ sectionIndex: Int) -> Void) { + internal func registerSectionNotification(_ notificationKey: UnsafeRawPointer, name: Notification.Name, toObserver observer: AnyObject, callback: @escaping (_ monitor: ListMonitor, _ sectionInfo: NSFetchedResultsSectionInfo, _ sectionIndex: Int) -> Void) { Internals.setAssociatedRetainedObject( Internals.NotificationObserver( @@ -746,7 +746,7 @@ public final class ListMonitor: Hashable { ) } - internal func registerObserver(_ observer: U, willChange: @escaping (_ observer: U, _ monitor: ListMonitor) -> Void, didChange: @escaping (_ observer: U, _ monitor: ListMonitor) -> Void, willRefetch: @escaping (_ observer: U, _ monitor: ListMonitor) -> Void, didRefetch: @escaping (_ observer: U, _ monitor: ListMonitor) -> Void) { + internal func registerObserver(_ observer: U, willChange: @escaping (_ observer: U, _ monitor: ListMonitor) -> Void, didChange: @escaping (_ observer: U, _ monitor: ListMonitor) -> Void, willRefetch: @escaping (_ observer: U, _ monitor: ListMonitor) -> Void, didRefetch: @escaping (_ observer: U, _ monitor: ListMonitor) -> Void) { Internals.assert( Thread.isMainThread, @@ -806,7 +806,7 @@ public final class ListMonitor: Hashable { ) } - internal func registerObserver(_ observer: U, didInsertObject: @escaping (_ observer: U, _ monitor: ListMonitor, _ object: ObjectType, _ toIndexPath: IndexPath) -> Void, didDeleteObject: @escaping (_ observer: U, _ monitor: ListMonitor, _ object: ObjectType, _ fromIndexPath: IndexPath) -> Void, didUpdateObject: @escaping (_ observer: U, _ monitor: ListMonitor, _ object: ObjectType, _ atIndexPath: IndexPath) -> Void, didMoveObject: @escaping (_ observer: U, _ monitor: ListMonitor, _ object: ObjectType, _ fromIndexPath: IndexPath, _ toIndexPath: IndexPath) -> Void) { + internal func registerObserver(_ observer: U, didInsertObject: @escaping (_ observer: U, _ monitor: ListMonitor, _ object: O, _ toIndexPath: IndexPath) -> Void, didDeleteObject: @escaping (_ observer: U, _ monitor: ListMonitor, _ object: O, _ fromIndexPath: IndexPath) -> Void, didUpdateObject: @escaping (_ observer: U, _ monitor: ListMonitor, _ object: O, _ atIndexPath: IndexPath) -> Void, didMoveObject: @escaping (_ observer: U, _ monitor: ListMonitor, _ object: O, _ fromIndexPath: IndexPath, _ toIndexPath: IndexPath) -> Void) { Internals.assert( Thread.isMainThread, @@ -867,7 +867,7 @@ public final class ListMonitor: Hashable { ) } - internal func registerObserver(_ observer: U, didInsertSection: @escaping (_ observer: U, _ monitor: ListMonitor, _ sectionInfo: NSFetchedResultsSectionInfo, _ toIndex: Int) -> Void, didDeleteSection: @escaping (_ observer: U, _ monitor: ListMonitor, _ sectionInfo: NSFetchedResultsSectionInfo, _ fromIndex: Int) -> Void) { + internal func registerObserver(_ observer: U, didInsertSection: @escaping (_ observer: U, _ monitor: ListMonitor, _ sectionInfo: NSFetchedResultsSectionInfo, _ toIndex: Int) -> Void, didDeleteSection: @escaping (_ observer: U, _ monitor: ListMonitor, _ sectionInfo: NSFetchedResultsSectionInfo, _ fromIndex: Int) -> Void) { Internals.assert( Thread.isMainThread, @@ -1052,7 +1052,7 @@ public final class ListMonitor: Hashable { } } - private static func recreateFetchedResultsController(context: NSManagedObjectContext, from: From, sectionBy: SectionBy?, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest) -> Void) -> (controller: Internals.CoreStoreFetchedResultsController, delegate: Internals.FetchedResultsControllerDelegate) { + private static func recreateFetchedResultsController(context: NSManagedObjectContext, from: From, sectionBy: SectionBy?, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest) -> Void) -> (controller: Internals.CoreStoreFetchedResultsController, delegate: Internals.FetchedResultsControllerDelegate) { let fetchRequest = Internals.CoreStoreFetchRequest() fetchRequest.fetchLimit = 0 @@ -1075,10 +1075,10 @@ public final class ListMonitor: Hashable { return (fetchedResultsController, fetchedResultsControllerDelegate) } - private let from: From - private let sectionBy: SectionBy? + private let from: From + private let sectionBy: SectionBy? - private init(context: NSManagedObjectContext, transactionQueue: DispatchQueue, from: From, sectionBy: SectionBy?, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest) -> Void, createAsynchronously: ((ListMonitor) -> Void)?) { + private init(context: NSManagedObjectContext, transactionQueue: DispatchQueue, from: From, sectionBy: SectionBy?, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest) -> Void, createAsynchronously: ((ListMonitor) -> Void)?) { self.isSectioned = (sectionBy != nil) self.from = from @@ -1173,77 +1173,26 @@ public final class ListMonitor: Hashable { try! self.fetchedResultsController.performFetchFromSpecifiedStores() } } - - - // MARK: Deprecated - - @available(*, deprecated, renamed: "hasObjects(in:)") - public func hasObjectsInSection(_ section: Int) -> Bool { - - return self.hasObjects(in: section) - } - - @available(*, deprecated, renamed: "numberOfObjects(in:)") - public func numberOfObjectsInSection(_ section: Int) -> Int { - - return self.numberOfObjects(in: section) - } - - @available(*, deprecated, renamed: "numberOfObjects(safelyIn:)") - public func numberOfObjectsInSection(safeSectionIndex section: Int) -> Int? { - - return self.numberOfObjects(safelyIn: section) - } - - @available(*, deprecated, renamed: "sectionInfo(at:)") - public func sectionInfoAtIndex(_ section: Int) -> NSFetchedResultsSectionInfo { - - return self.sectionInfo(at: section) - } - - @available(*, deprecated, renamed: "sectionInfo(safelyAt:)") - public func sectionInfoAtIndex(safeSectionIndex section: Int) -> NSFetchedResultsSectionInfo? { - - return self.sectionInfo(safelyAt: section) - } - - @available(*, deprecated, renamed: "targetSection(forSectionIndexTitle:at:)") - public func targetSectionForSectionIndex(title: String, index: Int) -> Int { - - return self.targetSection(forSectionIndexTitle: title, at: index) - } - - @available(*, deprecated, renamed: "index(of:)") - public func indexOf(_ object: ObjectType) -> Int? { - - return self.index(of: object) - } - - @available(*, deprecated, renamed: "indexPath(of:)") - public func indexPathOf(_ object: ObjectType) -> IndexPath? { - - return self.indexPath(of: object) - } } -// MARK: - ListMonitor where ListMonitor.ObjectType: NSManagedObject +// MARK: - ListMonitor where O: NSManagedObject @available(macOS 10.12, *) -extension ListMonitor where ListMonitor.ObjectType: NSManagedObject { +extension ListMonitor where O: NSManagedObject { /** Returns all objects in all sections - returns: all objects in all sections */ - public func objectsInAllSections() -> [ObjectType] { + public func objectsInAllSections() -> [O] { Internals.assert( !self.isPendingRefetch || Thread.isMainThread, "Attempted to access a \(Internals.typeName(self)) outside the main thread while a refetch is in progress." ) - return (self.fetchedResultsController.dynamicCast() as NSFetchedResultsController).fetchedObjects ?? [] + return (self.fetchedResultsController.dynamicCast() as NSFetchedResultsController).fetchedObjects ?? [] } /** @@ -1252,9 +1201,9 @@ extension ListMonitor where ListMonitor.ObjectType: NSManagedObject { - parameter section: the section index. Using an index outside the valid range will raise an exception. - returns: all objects in the specified section */ - public func objects(in section: Int) -> [ObjectType] { + public func objects(in section: Int) -> [O] { - return (self.sectionInfo(at: section).objects as! [ObjectType]?) ?? [] + return (self.sectionInfo(at: section).objects as! [O]?) ?? [] } /** @@ -1263,46 +1212,46 @@ extension ListMonitor where ListMonitor.ObjectType: NSManagedObject { - parameter section: the section index. Using an index outside the valid range will return `nil`. - returns: all objects in the specified section */ - public func objects(safelyIn section: Int) -> [ObjectType]? { + public func objects(safelyIn section: Int) -> [O]? { - return self.sectionInfo(safelyAt: section)?.objects as! [ObjectType]? + return self.sectionInfo(safelyAt: section)?.objects as! [O]? } // MARK: Deprecated @available(*, deprecated, renamed: "objects(in:)") - public func objectsInSection(_ section: Int) -> [ObjectType] { + public func objectsInSection(_ section: Int) -> [O] { return self.objects(in: section) } @available(*, deprecated, renamed: "objects(safelyIn:)") - public func objectsInSection(safeSectionIndex section: Int) -> [ObjectType]? { + public func objectsInSection(safeSectionIndex section: Int) -> [O]? { return self.objects(safelyIn: section) } } -// MARK: - ListMonitor where ListMonitor.ObjectType: CoreStoreObject +// MARK: - ListMonitor where O: CoreStoreObject @available(macOS 10.12, *) -extension ListMonitor where ListMonitor.ObjectType: CoreStoreObject { +extension ListMonitor where O: CoreStoreObject { /** Returns all objects in all sections - returns: all objects in all sections */ - public func objectsInAllSections() -> [ObjectType] { + public func objectsInAllSections() -> [O] { Internals.assert( !self.isPendingRefetch || Thread.isMainThread, "Attempted to access a \(Internals.typeName(self)) outside the main thread while a refetch is in progress." ) return (self.fetchedResultsController.fetchedObjects ?? []) - .map(ObjectType.cs_fromRaw) + .map(O.cs_fromRaw) } /** @@ -1311,10 +1260,10 @@ extension ListMonitor where ListMonitor.ObjectType: CoreStoreObject { - parameter section: the section index. Using an index outside the valid range will raise an exception. - returns: all objects in the specified section */ - public func objects(in section: Int) -> [ObjectType] { + public func objects(in section: Int) -> [O] { return (self.sectionInfo(at: section).objects ?? []) - .map({ ObjectType.cs_fromRaw(object: $0 as! NSManagedObject) }) + .map({ O.cs_fromRaw(object: $0 as! NSManagedObject) }) } /** @@ -1323,26 +1272,17 @@ extension ListMonitor where ListMonitor.ObjectType: CoreStoreObject { - parameter section: the section index. Using an index outside the valid range will return `nil`. - returns: all objects in the specified section */ - public func objects(safelyIn section: Int) -> [ObjectType]? { + public func objects(safelyIn section: Int) -> [O]? { return (self.sectionInfo(safelyAt: section)?.objects)? - .map({ ObjectType.cs_fromRaw(object: $0 as! NSManagedObject) }) + .map({ O.cs_fromRaw(object: $0 as! NSManagedObject) }) } - // MARK: Deprecated + // MARK: - Deprecated - @available(*, deprecated, renamed: "objects(in:)") - public func objectsInSection(_ section: Int) -> [ObjectType] { - - return self.objects(in: section) - } - - @available(*, deprecated, renamed: "objects(safelyIn:)") - public func objectsInSection(safeSectionIndex section: Int) -> [ObjectType]? { - - return self.objects(safelyIn: section) - } + @available(*, deprecated, renamed: "O") + public typealias D = O } diff --git a/Sources/LiveList.swift b/Sources/ListPublisher.swift similarity index 51% rename from Sources/LiveList.swift rename to Sources/ListPublisher.swift index 7b937aa..b992aa4 100644 --- a/Sources/LiveList.swift +++ b/Sources/ListPublisher.swift @@ -1,5 +1,5 @@ // -// LiveList.swift +// ListPublisher.swift // CoreStore // // Copyright © 2018 John Rommel Estropia @@ -36,176 +36,110 @@ import SwiftUI #endif -// MARK: - LiveList +// MARK: - ListPublisher /** - `LiveList` tracks a diffable list of `DynamicObject` instances. Unlike `ListMonitor`s, `LiveList` are more lightweight and access objects lazily. `LiveList`s are also designed to work well with `DiffableDataSource.TableView`s and `DiffableDataSource.CollectionView`s. Objects that need to be notified of `LiveList` changes may register themselves to its `addObserver(_:_:)` method: + `ListPublisher` tracks a diffable list of `DynamicObject` instances. Unlike `ListMonitor`s, `ListPublisher` are more lightweight and access objects lazily. Objects that need to be notified of `ListPublisher` changes may register themselves to its `addObserver(_:_:)` method: ``` - let liveList = Shared.defaultStack.liveList( + let listPublisher = Shared.defaultStack.listPublisher( From() .where(\.title == "Engineer") .orderBy(.ascending(\.lastName)) ) - liveList.addObserver(self) { (liveList) in - + listPublisher.addObserver(self) { (listPublisher) in // Handle changes } ``` - The `LiveList` instance needs to be held on (retained) for as long as the list needs to be observed. - Observers registered via `addObserver(_:_:)` are not retained. `LiveList` only keeps a `weak` reference to all observers, thus keeping itself free from retain-cycles. + The `ListPublisher` instance needs to be held on (retained) for as long as the list needs to be observed. + Observers registered via `addObserver(_:_:)` are not retained. `ListPublisher` only keeps a `weak` reference to all observers, thus keeping itself free from retain-cycles. - `LiveList`s may optionally be created with sections: + `ListPublisher`s may optionally be created with sections: ``` - let liveList = Shared.defaultStack.liveList( + let listPublisher = Shared.defaultStack.listPublisher( From() .sectionBy(\.age") { "Age \($0)" } .where(\.title == "Engineer") .orderBy(.ascending(\.lastName)) ) - liveList.addObserver(self) { (liveList) in - - // Handle changes - } ``` + All access to the `ListPublisher` items should be done via its `snapshot` value, which is a `struct` of type `ListSnapshot`. `ListSnapshot`s are also designed to work well with `DiffableDataSource.TableView`s and `DiffableDataSource.CollectionView`s. For detailed examples, refer to the documentation for `DiffableDataSource.TableView` and `DiffableDataSource.CollectionView`. */ @available(macOS 10.12, *) -public final class LiveList: Hashable { - - // MARK: Public +public final class ListPublisher: Hashable { - public typealias SectionID = SnapshotType.SectionID - public typealias ItemID = SnapshotType.ItemID + // MARK: Public (Accessors) - public subscript(section sectionID: SectionID) -> [LiveObject] { + /** + The `DynamicObject` type associated with this list + */ + public typealias ObjectType = O - let context = self.context - return self.snapshot - .itemIdentifiers(inSection: sectionID) - .map({ context.liveObject(objectID: $0) }) - } + /** + The type for the section IDs + */ + public typealias SectionID = ListSnapshot.SectionID - public subscript(itemID itemID: ItemID) -> LiveObject? { + /** + The type for the item IDs + */ + public typealias ItemID = ListSnapshot.ItemID - guard let validID = self.snapshot.itemIdentifiers.first(where: { $0 == itemID }) else { - - return nil - } - return self.context.liveObject(objectID: validID) - } - - public subscript(indexPath indexPath: IndexPath) -> LiveObject? { - - let snapshot = self.snapshot - let sectionIdentifiers = snapshot.sectionIdentifiers - guard sectionIdentifiers.indices.contains(indexPath.section) else { - - return nil - } - let sectionID = sectionIdentifiers[indexPath.section] - let itemIdentifiers = snapshot.itemIdentifiers(inSection: sectionID) - guard itemIdentifiers.indices.contains(indexPath.item) else { - - return nil - } - let itemID = itemIdentifiers[indexPath.item] - return self.context.liveObject(objectID: itemID) - } - - public subscript(section sectionID: SectionID, itemIndices itemIndices: S) -> [LiveObject] 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(objectID: itemID) - } - } - - public fileprivate(set) var snapshot: SnapshotType = .init() { + /** + A snapshot of the latest state of this list + */ + public fileprivate(set) var snapshot: ListSnapshot = .init() { willSet { self.willChange() } didSet { - - self.notifyObservers() + self.didChange() + self.notifyObservers() } } - public var numberOfItems: Int { - return self.snapshot.numberOfItems - } + // MARK: Public (Observers) - public var numberOfSections: Int { + /** + Registers an object as an observer to be notified when changes to the `ListPublisher`'s snapshot occur. - return self.snapshot.numberOfSections - } + To prevent retain-cycles, `ListPublisher` only keeps `weak` references to its observers. - public var sectionIdentifiers: [SectionID] { + For thread safety, this method needs to be called from the main thread. An assertion failure will occur (on debug builds only) if called from any thread other than the main thread. - return self.snapshot.sectionIdentifiers - } + Calling `addObserver(_:_:)` multiple times on the same observer is safe. - public var items: [LiveObject] { + - parameter observer: an object to become owner of the specified `callback` + - parameter callback: the closure to execute when changes occur + */ + public func addObserver(_ observer: T, _ callback: @escaping (ListPublisher) -> Void) { - let context = self.context - return self.snapshot.itemIdentifiers - .map({ context.liveObject(objectID: $0) }) - } - - public func numberOfItems(inSection identifier: SectionID) -> Int { - - return self.snapshot.numberOfItems(inSection: identifier) - } - - public func items(inSection identifier: SectionID) -> [LiveObject] { - - let context = self.context - return self.snapshot - .itemIdentifiers(inSection: identifier) - .map({ context.liveObject(objectID: $0) }) - } - - public func items(inSection identifier: SectionID, atIndices indices: IndexSet) -> [LiveObject] { - - let context = self.context - let itemIDs = self.snapshot.itemIdentifiers(inSection: identifier) - return indices.map { position in - - let itemID = itemIDs[position] - return context.liveObject(objectID: itemID) - } - } - - public func section(containingItem item: LiveObject) -> SectionID? { - - return self.snapshot.sectionIdentifier(containingItem: item.objectID()) - } - - public func indexOfItem(_ item: LiveObject) -> Int? { - - return self.snapshot.indexOfItem(item.objectID()) - } - - public func indexOfSection(_ identifier: SectionID) -> Int? { - - return self.snapshot.indexOfSection(identifier) - } - - public func addObserver(_ observer: T, _ callback: @escaping (LiveList) -> Void) { - + Internals.assert( + Thread.isMainThread, + "Attempted to add an observer of type \(Internals.typeName(observer)) outside the main thread." + ) self.observers.setObject( Internals.Closure(callback), forKey: observer ) } - + + /** + Unregisters an object from receiving notifications for changes to the `ListPublisher`'s snapshot. + + For thread safety, this method needs to be called from the main thread. An assertion failure will occur (on debug builds only) if called from any thread other than the main thread. + + - parameter observer: the object whose notifications will be unregistered + */ public func removeObserver(_ observer: T) { - + + Internals.assert( + Thread.isMainThread, + "Attempted to remove an observer of type \(Internals.typeName(observer)) outside the main thread." + ) self.observers.removeObject(forKey: observer) } @@ -213,39 +147,57 @@ public final class LiveList: Hashable { // MARK: Public (Refetching) /** - Asks the `ListMonitor` to refetch its objects using the specified series of `FetchClause`s. Note that this method does not execute the fetch immediately; the actual fetching will happen after the `NSFetchedResultsController`'s last `controllerDidChangeContent(_:)` notification completes. - - `refetch(...)` broadcasts `listMonitorWillRefetch(...)` to its observers immediately, and then `listMonitorDidRefetch(...)` after the new fetch request completes. - - - parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - - Important: Starting CoreStore 4.0, all `FetchClause`s required by the `ListMonitor` should be provided in the arguments list of `refetch(...)`. + Asks the `ListPublisher` to refetch its objects using the specified `FetchChainableBuilderType`. Unlike `ListMonitor`s, a `ListPublisher`'s `refetch(...)` executes immediately. + ``` + try listPublisher.refetch( + From() + .where(\.age > 18) + .orderBy(.ascending(\.age)) + ) + ``` + - parameter clauseChain: a `FetchChainableBuilderType` built from a chain of clauses */ - public func refetch(_ fetchClauses: FetchClause...) { + public func refetch(_ clauseChain: B) throws where B.ObjectType == O { - self.refetch(fetchClauses) + try self.refetch( + from: clauseChain.from, + sectionBy: nil, + applyFetchClauses: { (fetchRequest) in + + clauseChain.fetchClauses.forEach { $0.applyToFetchRequest(fetchRequest) } + } + ) } /** - Asks the `ListMonitor` to refetch its objects using the specified series of `FetchClause`s. Note that this method does not execute the fetch immediately; the actual fetching will happen after the `NSFetchedResultsController`'s last `controllerDidChangeContent(_:)` notification completes. - - `refetch(...)` broadcasts `listMonitorWillRefetch(...)` to its observers immediately, and then `listMonitorDidRefetch(...)` after the new fetch request completes. - - - parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - - Important: Starting CoreStore 4.0, all `FetchClause`s required by the `ListMonitor` should be provided in the arguments list of `refetch(...)`. + Asks the `ListPublisher` to refetch its objects using the specified `SectionMonitorBuilderType`. Unlike `ListMonitor`s, a `ListPublisher`'s `refetch(...)` executes immediately. + ``` + try listPublisher.refetch( + From() + .sectionBy(\.age, { "\($0!) years old" }) + .where(\.age > 18) + .orderBy(.ascending(\.age)) + ) + ``` + - parameter clauseChain: a `SectionMonitorBuilderType` built from a chain of clauses */ - public func refetch(_ fetchClauses: [FetchClause]) { + public func refetch(_ clauseChain: B) throws where B.ObjectType == O { - self.refetch { (fetchRequest) in + try self.refetch( + from: clauseChain.from, + sectionBy: clauseChain.sectionBy, + applyFetchClauses: { (fetchRequest) in - fetchClauses.forEach { $0.applyToFetchRequest(fetchRequest) } - } + clauseChain.fetchClauses.forEach { $0.applyToFetchRequest(fetchRequest) } + } + ) } // MARK: Public (3rd Party Utilities) /** - Allow external libraries to store custom data in the `ListMonitor`. App code should rarely have a need for this. + Allow external libraries to store custom data in the `ListPublisher`. App code should rarely have a need for this. ``` enum Static { static var myDataKey: Void? @@ -259,7 +211,7 @@ public final class LiveList: Hashable { // MARK: Equatable - public static func == (_ lhs: LiveList, _ rhs: LiveList) -> Bool { + public static func == (_ lhs: ListPublisher, _ rhs: ListPublisher) -> Bool { return lhs === rhs } @@ -271,13 +223,6 @@ public final class LiveList: Hashable { hasher.combine(ObjectIdentifier(self)) } - - - // MARK: LiveResult - - public typealias ObjectType = O - - public typealias SnapshotType = ListSnapshot // MARK: Internal @@ -293,7 +238,7 @@ public final class LiveList: Hashable { ) } - internal convenience init(dataStack: DataStack, from: From, sectionBy: SectionBy?, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest) -> Void, createAsynchronously: @escaping (LiveList) -> Void) { + internal convenience init(dataStack: DataStack, from: From, sectionBy: SectionBy?, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest) -> Void, createAsynchronously: @escaping (ListPublisher) -> Void) { self.init( context: dataStack.mainContext, @@ -315,7 +260,7 @@ public final class LiveList: Hashable { ) } - internal convenience init(unsafeTransaction: UnsafeDataTransaction, from: From, sectionBy: SectionBy?, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest) -> Void, createAsynchronously: @escaping (LiveList) -> Void) { + internal convenience init(unsafeTransaction: UnsafeDataTransaction, from: From, sectionBy: SectionBy?, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest) -> Void, createAsynchronously: @escaping (ListPublisher) -> Void) { self.init( context: unsafeTransaction.context, @@ -326,36 +271,26 @@ public final class LiveList: Hashable { ) } - internal func refetch(_ applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest) -> Void) { + internal func refetch(from: From, sectionBy: SectionBy?, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest) -> Void) throws { - self.applyFetchClauses = applyFetchClauses + let (newFetchedResultsController, newFetchedResultsControllerDelegate) = Self.recreateFetchedResultsController( + context: self.fetchedResultsController.managedObjectContext, + from: from, + sectionBy: sectionBy, + applyFetchClauses: applyFetchClauses + ) - DispatchQueue.main.async { [weak self] () -> Void in + try newFetchedResultsController.performFetchFromSpecifiedStores() - guard let `self` = self else { + newFetchedResultsControllerDelegate.handler = self - return - } - - let (newFetchedResultsController, newFetchedResultsControllerDelegate) = Self.recreateFetchedResultsController( - context: self.fetchedResultsController.managedObjectContext, - from: self.from, - sectionBy: self.sectionBy, - applyFetchClauses: self.applyFetchClauses - ) - newFetchedResultsControllerDelegate.handler = self - - do { - - try newFetchedResultsController.performFetchFromSpecifiedStores() - } - catch { - - // DataStack may have been deallocated - return - } - (self.fetchedResultsController, self.fetchedResultsControllerDelegate) = (newFetchedResultsController, newFetchedResultsControllerDelegate) - } + self.query = ( + from: from, + sectionBy: sectionBy, + sectionIndexTransformer: sectionBy?.sectionIndexTransformer ?? { $0 }, + applyFetchClauses: applyFetchClauses + ) + (self.fetchedResultsController, self.fetchedResultsControllerDelegate) = (newFetchedResultsController, newFetchedResultsControllerDelegate) } deinit { @@ -372,16 +307,19 @@ public final class LiveList: Hashable { // MARK: Private + private var query: ( + from: From, + sectionBy: SectionBy?, + sectionIndexTransformer: (_ sectionName: KeyPathString?) -> String?, + applyFetchClauses: (_ fetchRequest: Internals.CoreStoreFetchRequest) -> Void + ) + private var fetchedResultsController: Internals.CoreStoreFetchedResultsController private var fetchedResultsControllerDelegate: Internals.FetchedDiffableDataSourceSnapshotDelegate - private let sectionIndexTransformer: (_ sectionName: KeyPathString?) -> String? - private var applyFetchClauses: (_ fetchRequest: Internals.CoreStoreFetchRequest) -> Void private var observerForWillChangePersistentStore: Internals.NotificationObserver! private var observerForDidChangePersistentStore: Internals.NotificationObserver! - private let from: From - private let sectionBy: SectionBy? - private lazy var observers: NSMapTable, Void>> = .weakToStrongObjects() + private lazy var observers: NSMapTable, Void>> = .weakToStrongObjects() private lazy var context: NSManagedObjectContext = self.fetchedResultsController.managedObjectContext @@ -407,10 +345,14 @@ public final class LiveList: Hashable { return (fetchedResultsController, fetchedResultsControllerDelegate) } - private init(context: NSManagedObjectContext, from: From, sectionBy: SectionBy?, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest) -> Void, createAsynchronously: ((LiveList) -> Void)?) { + private init(context: NSManagedObjectContext, from: From, sectionBy: SectionBy?, applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest) -> Void, createAsynchronously: ((ListPublisher) -> Void)?) { - self.from = from - self.sectionBy = sectionBy + self.query = ( + from: from, + sectionBy: sectionBy, + sectionIndexTransformer: sectionBy?.sectionIndexTransformer ?? { $0 }, + applyFetchClauses: applyFetchClauses + ) (self.fetchedResultsController, self.fetchedResultsControllerDelegate) = Self.recreateFetchedResultsController( context: context, from: from, @@ -433,15 +375,6 @@ public final class LiveList: Hashable { self.rawObjectWillChange = nil } - if let sectionIndexTransformer = sectionBy?.sectionIndexTransformer { - - self.sectionIndexTransformer = sectionIndexTransformer - } - else { - - self.sectionIndexTransformer = { $0 } - } - self.applyFetchClauses = applyFetchClauses self.fetchedResultsControllerDelegate.handler = self try! self.fetchedResultsController.performFetchFromSpecifiedStores() @@ -455,15 +388,15 @@ public final class LiveList: Hashable { } for closure in enumerator { - (closure as! Internals.Closure, Void>).invoke(with: self) + (closure as! Internals.Closure, Void>).invoke(with: self) } } } -// MARK: - LiveList: FetchedDiffableDataSourceSnapshotHandler +// MARK: - ListPublisher: FetchedDiffableDataSourceSnapshotHandler -extension LiveList: FetchedDiffableDataSourceSnapshotHandler { +extension ListPublisher: FetchedDiffableDataSourceSnapshotHandler { // MARK: FetchedDiffableDataSourceSnapshotHandler @@ -486,7 +419,7 @@ extension LiveList: FetchedDiffableDataSourceSnapshotHandler { internal func controller(_ controller: NSFetchedResultsController, sectionIndexTitleForSectionName sectionName: String?) -> String? { - return self.sectionIndexTransformer(sectionName) + return self.query.sectionIndexTransformer(sectionName) } } @@ -495,13 +428,13 @@ extension LiveList: FetchedDiffableDataSourceSnapshotHandler { import Combine @available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) -extension LiveList: ObservableObject {} +extension ListPublisher: ObservableObject {} #endif -// MARK: - LiveList: LiveResult +// MARK: - ListPublisher -extension LiveList: LiveResult { +extension ListPublisher { // MARK: ObservableObject @@ -515,7 +448,7 @@ extension LiveList: LiveResult { #endif - public func willChange() { + fileprivate func willChange() { guard #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) else { @@ -536,8 +469,8 @@ extension LiveList: LiveResult { #endif } - public func didChange() { + fileprivate func didChange() { - // TODO: + // nothing } } diff --git a/Sources/ListSnapshot.swift b/Sources/ListSnapshot.swift index 5f84726..fd2723e 100644 --- a/Sources/ListSnapshot.swift +++ b/Sources/ListSnapshot.swift @@ -36,63 +36,174 @@ import AppKit // MARK: - ListSnapshot -public struct ListSnapshot: SnapshotResult, RandomAccessCollection, Hashable { +/** + A `ListSnapshot` holds a stable list of `DynamicObject` identifiers. This is typically created by a `ListPublisher` and are designed to work well with `DiffableDataSource.TableView`s and `DiffableDataSource.CollectionView`s. For detailed examples, see the documentation on `DiffableDataSource.TableView` and `DiffableDataSource.CollectionView`. - // MARK: Public + While the `ListSnapshot` stores only object identifiers, all accessors to its items return `ObjectPublisher`s, which are lazily created. For more details, see the documentation on `ListObject`. + Since `ListSnapshot` is a value type, you can freely modify its items. + */ +public struct ListSnapshot: RandomAccessCollection, Hashable { + + // MARK: Public (Accessors) + + /** + The `DynamicObject` type associated with this list + */ + public typealias ObjectType = O + + /** + The type for the section IDs + */ public typealias SectionID = String + + /** + The type for the item IDs + */ public typealias ItemID = O.ObjectID - - public init(byCloning snapshot: ListSnapshot, for dataStack: DataStack) { -// if #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) { -// -// self.init( -// diffableSnapshot: snapshot.diffableSnapshot as! NSDiffableDataSourceSnapshot, -// context: dataStack.mainContext -// ) -// } -// else { + /** + Returns the object at the given index. - self.init( - diffableSnapshot: snapshot.diffableSnapshot as! Internals.DiffableDataSourceSnapshot, - context: dataStack.mainContext - ) -// } + - parameter index: the index of the object. Using an index above the valid range will raise an exception. + - returns: the `ObjectPublisher` interfacing the object at the specified index + */ + public subscript(index: Index) -> ObjectPublisher { + + let context = self.context! + let itemID = self.diffableSnapshot.itemIdentifiers[index] + return context.objectPublisher(objectID: itemID) } - public subscript(indices indices: S) -> [LiveObject] where S.Element == Index { + /** + Returns the object at the given index, or `nil` if out of bounds. + + - parameter index: the index for the object. Using an index above the valid range will return `nil`. + - returns: the `ObjectPublisher` interfacing the object at the specified index, or `nil` if out of bounds + */ + public subscript(safeIndex index: Index) -> ObjectPublisher? { let context = self.context! let itemIDs = self.diffableSnapshot.itemIdentifiers - return indices.map { position in + guard itemIDs.indices.contains(index) else { - let itemID = itemIDs[position] - return LiveObject(objectID: itemID, context: context) + return nil } + let itemID = itemIDs[index] + return context.objectPublisher(objectID: itemID) } - public subscript(section sectionID: SectionID) -> [LiveObject] { + /** + Returns the object at the given `sectionIndex` and `itemIndex`. + + - parameter sectionIndex: the section index for the object. Using a `sectionIndex` with an invalid range will raise an exception. + - parameter itemIndex: the index for the object within the section. Using an `itemIndex` with an invalid range will raise an exception. + - returns: the `ObjectPublisher` interfacing the object at the specified section and item index + */ + public subscript(sectionIndex: Int, itemIndex: Int) -> ObjectPublisher { let context = self.context! - let itemIDs = self.diffableSnapshot.itemIdentifiers(inSection: sectionID) - return itemIDs.map { - - return LiveObject(objectID: $0, context: context) - } + let snapshot = self.diffableSnapshot + let sectionID = snapshot.sectionIdentifiers[sectionIndex] + let itemID = snapshot.itemIdentifiers(inSection: sectionID)[itemIndex] + return context.objectPublisher(objectID: itemID) } - public subscript(section sectionID: SectionID, itemIndices itemIndices: S) -> [LiveObject] where S.Element == Int { + /** + Returns the object at the given section and item index, or `nil` if out of bounds. + + - parameter sectionIndex: the section index for the object. Using a `sectionIndex` with an invalid range will return `nil`. + - parameter itemIndex: the index for the object within the section. Using an `itemIndex` with an invalid range will return `nil`. + - returns: the `ObjectPublisher` interfacing the object at the specified section and item index, or `nil` if out of bounds + */ + public subscript(safeSectionIndex sectionIndex: Int, safeItemIndex itemIndex: Int) -> ObjectPublisher? { let context = self.context! - let itemIDs = self.diffableSnapshot.itemIdentifiers(inSection: sectionID) - return itemIndices.map { position in + let snapshot = self.diffableSnapshot + let sectionIDs = snapshot.sectionIdentifiers + guard sectionIDs.indices.contains(sectionIndex) else { - let itemID = itemIDs[position] - return LiveObject(objectID: itemID, context: context) + return nil } + let sectionID = sectionIDs[sectionIndex] + let itemIDs = snapshot.itemIdentifiers(inSection: sectionID) + guard itemIDs.indices.contains(itemIndex) else { + + return nil + } + let itemID = itemIDs[itemIndex] + return context.objectPublisher(objectID: itemID) } + /** + Returns the object at the given `IndexPath`. + + - parameter indexPath: the `IndexPath` for the object. Using an `indexPath` with an invalid range will raise an exception. + - returns: the `ObjectPublisher` interfacing the object at the specified index path + */ + public subscript(indexPath: IndexPath) -> ObjectPublisher { + + return self[indexPath[0], indexPath[1]] + } + + /** + Returns the object at the given `IndexPath`, or `nil` if out of bounds. + + - parameter indexPath: the `IndexPath` for the object. Using an `indexPath` with an invalid range will return `nil`. + - returns: the `ObjectPublisher` interfacing the object at the specified index path, or `nil` if out of bounds + */ + public subscript(safeIndexPath indexPath: IndexPath) -> ObjectPublisher? { + + return self[ + safeSectionIndex: indexPath[0], + safeItemIndex: indexPath[1] + ] + } + + /** + Checks if the `ListSnapshot` has at least one section + + - returns: `true` if at least one section exists, `false` otherwise + */ + public func hasSections() -> Bool { + + return self.diffableSnapshot.numberOfSections > 0 + } + + /** + Checks if the `ListSnapshot` has at least one object in any section. + + - returns: `true` if at least one object in any section exists, `false` otherwise + */ + public func hasObjects() -> Bool { + + return self.diffableSnapshot.numberOfItems > 0 + } + + /** + Checks if the `ListSnapshot` has at least one object the specified section. + + - parameter section: the section index. Using an index outside the valid range will return `false`. + - returns: `true` if at least one object in the specified section exists, `false` otherwise + */ + public func hasObjects(in sectionIndex: Int) -> Bool { + + let snapshot = self.diffableSnapshot + let sectionIDs = snapshot.sectionIdentifiers + guard sectionIDs.indices.contains(sectionIndex) else { + + return false + } + let sectionID = sectionIDs[sectionIndex] + return snapshot.numberOfItems(inSection: sectionID) > 0 + } + + + + + + + public var numberOfItems: Int { return self.diffableSnapshot.numberOfItems @@ -218,11 +329,39 @@ public struct ListSnapshot: SnapshotResult, RandomAccessCollec self.diffableSnapshot.reloadSections(identifiers) } - - - // MARK: SnapshotResult - - public typealias ObjectType = O + + public func items(atIndices indices: S) -> [ObjectPublisher] where S.Element == Index { + + let context = self.context! + let itemIDs = self.diffableSnapshot.itemIdentifiers + return indices.map { position in + + let itemID = itemIDs[position] + return ObjectPublisher(objectID: itemID, context: context) + } + } + + public subscript(section sectionID: SectionID) -> [ObjectPublisher] { + + let context = self.context! + let itemIDs = self.diffableSnapshot.itemIdentifiers(inSection: sectionID) + return itemIDs.map { + + return ObjectPublisher(objectID: $0, context: context) + } + } + + public subscript(section sectionID: SectionID, itemIndices itemIndices: S) -> [ObjectPublisher] where S.Element == Int { + + let context = self.context! + let itemIDs = self.diffableSnapshot.itemIdentifiers(inSection: sectionID) + return itemIndices.map { position in + + let itemID = itemIDs[position] + return ObjectPublisher(objectID: itemID, context: context) + } + } + // MARK: RandomAccessCollection @@ -237,17 +376,10 @@ public struct ListSnapshot: SnapshotResult, RandomAccessCollec return self.diffableSnapshot.itemIdentifiers.endIndex } - public subscript(position: Index) -> Element { - - let context = self.context! - let itemID = self.diffableSnapshot.itemIdentifiers[position] - return LiveObject(objectID: itemID, context: context) - } - // MARK: Sequence - public typealias Element = LiveObject + public typealias Element = ObjectPublisher public typealias Index = Int diff --git a/Sources/LiveResult.swift b/Sources/LiveResult.swift deleted file mode 100644 index e2c012f..0000000 --- a/Sources/LiveResult.swift +++ /dev/null @@ -1,48 +0,0 @@ -// -// File.swift -// CoreStore iOS -// -// Copyright © 2018 John Rommel Estropia -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// - -#if canImport(UIKit) || canImport(AppKit) - -#if canImport(UIKit) -import UIKit - -#elseif canImport(AppKit) -import AppKit - -#endif - - -// MARK: - LiveResult - -public protocol LiveResult: AnyObject { - - associatedtype ObjectType - associatedtype SnapshotType: SnapshotResult where SnapshotType.ObjectType == Self.ObjectType - - func willChange() - func didChange() -} - -#endif diff --git a/Sources/NSManagedObjectContext+CoreStore.swift b/Sources/NSManagedObjectContext+CoreStore.swift index 589e99b..0e0be6c 100644 --- a/Sources/NSManagedObjectContext+CoreStore.swift +++ b/Sources/NSManagedObjectContext+CoreStore.swift @@ -87,21 +87,21 @@ extension NSManagedObjectContext { } @nonobjc - internal func liveObject(objectID: NSManagedObjectID) -> LiveObject { + internal func objectPublisher(objectID: NSManagedObjectID) -> ObjectPublisher { - let cache: NSMapTable> = self.userInfo(for: .liveObjectsCache(D.self)) { + let cache: NSMapTable> = self.userInfo(for: .objectPublishersCache(D.self)) { return .strongToWeakObjects() } return Internals.with { - if let liveObject = cache.object(forKey: objectID) { + if let objectPublisher = cache.object(forKey: objectID) { - return liveObject + return objectPublisher } - let liveObject = LiveObject(objectID: objectID, context: self) - cache.setObject(liveObject, forKey: objectID) - return liveObject + let objectPublisher = ObjectPublisher(objectID: objectID, context: self) + cache.setObject(objectPublisher, forKey: objectID) + return objectPublisher } } @@ -185,15 +185,15 @@ extension NSManagedObjectContext { private enum UserInfoKeys { - case liveObjectsCache(DynamicObject.Type) + case objectPublishersCache(DynamicObject.Type) case objectsChangeObserver(AnyObject.Type) var keyString: String { switch self { - case .liveObjectsCache(let objectType): - return "CoreStore.liveObjectsCache(\(Internals.typeName(objectType)))" + case .objectPublishersCache(let objectType): + return "CoreStore.objectPublishersCache(\(Internals.typeName(objectType)))" case .objectsChangeObserver: return "CoreStore.objectsChangeObserver" diff --git a/Sources/ObjectMonitor.swift b/Sources/ObjectMonitor.swift index 9a9fa3a..f3367c0 100644 --- a/Sources/ObjectMonitor.swift +++ b/Sources/ObjectMonitor.swift @@ -325,6 +325,12 @@ public final class ObjectMonitor: Equatable { inObject: observer ) } + + + // MARK: - Deprecated + + @available(*, deprecated, renamed: "O") + public typealias D = O } diff --git a/Sources/LiveObject.swift b/Sources/ObjectPublisher.swift similarity index 70% rename from Sources/LiveObject.swift rename to Sources/ObjectPublisher.swift index 4ca4f2e..c1a6a0d 100644 --- a/Sources/LiveObject.swift +++ b/Sources/ObjectPublisher.swift @@ -1,5 +1,5 @@ // -// LiveObject.swift +// ObjectPublisher.swift // CoreStore // // Copyright © 2018 John Rommel Estropia @@ -36,44 +36,89 @@ import SwiftUI #endif -// MARK: - LiveObject +// MARK: - ObjectPublisher +/** + The `ObjectPublisher` tracks changes to a single `DynamicObject` instance. Objects that need to be notified of `ObjectPublisher` changes may register themselves to its `addObserver(_:_:)` method: + ``` + let objectPublisher = Shared.defaultStack.objectPublisher(object) + objectPublisher.addObserver(self) { (objectPublisher) in + // Handle changes + } + ``` + The created `ObjectPublisher` instance needs to be held on (retained) for as long as the object needs to be observed. + + Observers registered via `addObserver(_:_:)` are not retained. `ObjectPublisher` only keeps a `weak` reference to all observers, thus keeping itself free from retain-cycles. + + The `ObjectPublisher`'s `snapshot` value is a lazy copy operation that extracts all property values at a specific point time. This cached copy is invalidated right before the `ObjectPublisher` broadcasts any changes to its observers. + */ @dynamicMemberLookup -public final class LiveObject: ObjectRepresentation, Hashable { - - // MARK: Public - - public typealias SectionID = String - public typealias ItemID = O.ObjectID +public final class ObjectPublisher: ObjectRepresentation, Hashable { + // MARK: Public (Accessors) + /** + A snapshot of the latest state of this list. Returns `nil` if the object has been deleted. + */ public var snapshot: ObjectSnapshot? { return self.lazySnapshot } - public lazy var object: O? = self.context.fetchExisting(self.id) - - public func addObserver(_ observer: T, _ callback: @escaping (LiveObject) -> Void) { - + /** + The actual `DynamicObject` instance. Becomes `nil` if the object has been deleted. + */ + public private(set) lazy var object: O? = self.context.fetchExisting(self.id) + + + + // MARK: Public (Observers) + + /** + Registers an object as an observer to be notified when changes to the `ObjectPublisher`'s snapshot occur. + + To prevent retain-cycles, `ObjectPublisher` only keeps `weak` references to its observers. + + For thread safety, this method needs to be called from the main thread. An assertion failure will occur (on debug builds only) if called from any thread other than the main thread. + + Calling `addObserver(_:_:)` multiple times on the same observer is safe. + + - parameter observer: an object to become owner of the specified `callback` + - parameter callback: the closure to execute when changes occur + */ + public func addObserver(_ observer: T, _ callback: @escaping (ObjectPublisher) -> Void) { + + Internals.assert( + Thread.isMainThread, + "Attempted to add an observer of type \(Internals.typeName(observer)) outside the main thread." + ) self.observers.setObject( Internals.Closure(callback), forKey: observer ) } - + + /** + Unregisters an object from receiving notifications for changes to the `ObjectPublisher`'s snapshot. + + For thread safety, this method needs to be called from the main thread. An assertion failure will occur (on debug builds only) if called from any thread other than the main thread. + + - parameter observer: the object whose notifications will be unregistered + */ public func removeObserver(_ observer: T) { - + + Internals.assert( + Thread.isMainThread, + "Attempted to remove an observer of type \(Internals.typeName(observer)) outside the main thread." + ) self.observers.removeObject(forKey: observer) } - - deinit { - - self.observers.removeAllObjects() - } // MARK: ObjectRepresentation + /** + The `DynamicObject` type associated with this list + */ public typealias ObjectType = O public func objectID() -> O.ObjectID { @@ -81,7 +126,7 @@ public final class LiveObject: ObjectRepresentation, Hashable return self.id } - public func asLiveObject(in dataStack: DataStack) -> LiveObject { + public func asPublisher(in dataStack: DataStack) -> ObjectPublisher { let context = dataStack.unsafeContext() if self.context == context { @@ -124,7 +169,7 @@ public final class LiveObject: ObjectRepresentation, Hashable // MARK: Equatable - public static func == (_ lhs: LiveObject, _ rhs: LiveObject) -> Bool { + public static func == (_ lhs: ObjectPublisher, _ rhs: ObjectPublisher) -> Bool { return lhs.id == rhs.id && lhs.context == rhs.context @@ -140,11 +185,6 @@ public final class LiveObject: ObjectRepresentation, Hashable } - // MARK: LiveResult - - public typealias SnapshotType = ObjectSnapshot - - // MARK: Internal internal convenience init(objectID: O.ObjectID, context: NSManagedObjectContext) { @@ -156,6 +196,11 @@ public final class LiveObject: ObjectRepresentation, Hashable ) } + deinit { + + self.observers.removeAllObjects() + } + // MARK: FilePrivate @@ -193,15 +238,15 @@ public final class LiveObject: ObjectRepresentation, Hashable self.willChange() self.$lazySnapshot.reset({ nil }) - self.notifyObservers() self.didChange() + self.notifyObservers() } else if updatedIDs.contains(objectID) { self.willChange() self.$lazySnapshot.reset({ initializer(objectID, context) }) - self.notifyObservers() self.didChange() + self.notifyObservers() } } } @@ -215,7 +260,7 @@ public final class LiveObject: ObjectRepresentation, Hashable @Internals.LazyNonmutating(uninitialized: ()) private var lazySnapshot: ObjectSnapshot? - private lazy var observers: NSMapTable, Void>> = .weakToStrongObjects() + private lazy var observers: NSMapTable, Void>> = .weakToStrongObjects() private func notifyObservers() { @@ -225,7 +270,7 @@ public final class LiveObject: ObjectRepresentation, Hashable } for closure in enumerator { - (closure as! Internals.Closure, Void>).invoke(with: self) } } @@ -236,13 +281,13 @@ public final class LiveObject: ObjectRepresentation, Hashable import Combine @available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) -extension LiveObject: ObservableObject {} +extension ObjectPublisher: ObservableObject {} #endif -// MARK: - LiveObject: LiveResult +// MARK: - ObjectPublisher -extension LiveObject: LiveResult { +extension ObjectPublisher { // MARK: ObservableObject @@ -256,7 +301,7 @@ extension LiveObject: LiveResult { #endif - public func willChange() { + fileprivate func willChange() { guard #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) else { @@ -277,17 +322,17 @@ extension LiveObject: LiveResult { #endif } - public func didChange() { + fileprivate func didChange() { - // TODO: + // nothing } } -// MARK: - LiveObject where O: NSManagedObject +// MARK: - ObjectPublisher where O: NSManagedObject @available(*, unavailable, message: "KeyPaths accessed from @dynamicMemberLookup types can't generate KVC keys yet (https://bugs.swift.org/browse/SR-11351)") -extension LiveObject where O: NSManagedObject { +extension ObjectPublisher where O: NSManagedObject { // MARK: Public @@ -302,9 +347,9 @@ extension LiveObject where O: NSManagedObject { } -// MARK: - LiveObject where O: CoreStoreObject +// MARK: - ObjectPublisher where O: CoreStoreObject -extension LiveObject where O: CoreStoreObject { +extension ObjectPublisher where O: CoreStoreObject { // MARK: Public diff --git a/Sources/ObjectRepresentation.swift b/Sources/ObjectRepresentation.swift index 8f88c1f..3eb2f64 100644 --- a/Sources/ObjectRepresentation.swift +++ b/Sources/ObjectRepresentation.swift @@ -46,7 +46,7 @@ public protocol ObjectRepresentation { /** An instance that may be observed for object changes. */ - func asLiveObject(in dataStack: DataStack) -> LiveObject + func asPublisher(in dataStack: DataStack) -> ObjectPublisher /** A read-only instance in the `DataStack`. @@ -82,10 +82,10 @@ extension DynamicObject where Self: ObjectRepresentation { return self.cs_id() } - public func asLiveObject(in dataStack: DataStack) -> LiveObject { + public func asPublisher(in dataStack: DataStack) -> ObjectPublisher { let context = dataStack.unsafeContext() - return LiveObject(objectID: self.cs_id(), context: context) + return ObjectPublisher(objectID: self.cs_id(), context: context) } public func asReadOnly(in dataStack: DataStack) -> Self? { diff --git a/Sources/ObjectSnapshot.swift b/Sources/ObjectSnapshot.swift index 5c2dd3d..b4b95cd 100644 --- a/Sources/ObjectSnapshot.swift +++ b/Sources/ObjectSnapshot.swift @@ -37,9 +37,7 @@ import AppKit // MARK: - ObjectSnapshot @dynamicMemberLookup -public struct ObjectSnapshot: SnapshotResult, ObjectRepresentation, Hashable { - - // MARK: SnapshotResult +public struct ObjectSnapshot: ObjectRepresentation, Hashable { public typealias ObjectType = O @@ -51,10 +49,10 @@ public struct ObjectSnapshot: SnapshotResult, ObjectRepresenta return self.id } - public func asLiveObject(in dataStack: DataStack) -> LiveObject { + public func asPublisher(in dataStack: DataStack) -> ObjectPublisher { let context = dataStack.unsafeContext() - return LiveObject(objectID: self.id, context: context) + return ObjectPublisher(objectID: self.id, context: context) } public func asReadOnly(in dataStack: DataStack) -> O? { diff --git a/Sources/SnapshotResult.swift b/Sources/SnapshotResult.swift deleted file mode 100644 index a58422c..0000000 --- a/Sources/SnapshotResult.swift +++ /dev/null @@ -1,44 +0,0 @@ -// -// SnapshotResult.swift -// CoreStore -// -// Copyright © 2018 John Rommel Estropia -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// - -#if canImport(UIKit) || canImport(AppKit) - -#if canImport(UIKit) -import UIKit - -#elseif canImport(AppKit) -import AppKit - -#endif - - -// MARK: - SnapshotResult - -public protocol SnapshotResult { - - associatedtype ObjectType: DynamicObject -} - -#endif