diff --git a/CoreStore.podspec b/CoreStore.podspec index 646ea17..ced4a8b 100644 --- a/CoreStore.podspec +++ b/CoreStore.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "CoreStore" - s.version = "7.3.1" - s.swift_version = "5.3" + s.version = "8.0.0" + s.swift_version = "5.4" s.license = "MIT" s.homepage = "https://github.com/JohnEstropia/CoreStore" s.documentation_url = "https://JohnEstropia.github.io/CoreStore" @@ -9,10 +9,10 @@ Pod::Spec.new do |s| s.author = { "John Rommel Estropia" => "rommel.estropia@gmail.com" } s.source = { :git => "https://github.com/JohnEstropia/CoreStore.git", :tag => s.version.to_s } - s.ios.deployment_target = "10.0" - s.osx.deployment_target = "10.12" - s.watchos.deployment_target = "3.0" - s.tvos.deployment_target = "10.0" + s.ios.deployment_target = "11.0" + s.osx.deployment_target = "10.13" + s.watchos.deployment_target = "4.0" + s.tvos.deployment_target = "11.0" s.source_files = "Sources", "Sources/**/*.{swift,h,m}" s.public_header_files = "Sources/**/*.h" diff --git a/CoreStore.xcodeproj/project.pbxproj b/CoreStore.xcodeproj/project.pbxproj index 50d70ce..9103e4b 100644 --- a/CoreStore.xcodeproj/project.pbxproj +++ b/CoreStore.xcodeproj/project.pbxproj @@ -633,6 +633,14 @@ B58D0C641EAA0C7E003EDD87 /* NSManagedObject+DynamicModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B58D0C621EAA0C7E003EDD87 /* NSManagedObject+DynamicModel.swift */; }; B58D0C651EAA0C7E003EDD87 /* NSManagedObject+DynamicModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B58D0C621EAA0C7E003EDD87 /* NSManagedObject+DynamicModel.swift */; }; B58D0C661EAA0C7E003EDD87 /* NSManagedObject+DynamicModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B58D0C621EAA0C7E003EDD87 /* NSManagedObject+DynamicModel.swift */; }; + B5944EF625E269F9001D1D81 /* ObjectPublisher+Reactive.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5944EF525E269F9001D1D81 /* ObjectPublisher+Reactive.swift */; }; + B5944EF725E269F9001D1D81 /* ObjectPublisher+Reactive.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5944EF525E269F9001D1D81 /* ObjectPublisher+Reactive.swift */; }; + B5944EF825E269F9001D1D81 /* ObjectPublisher+Reactive.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5944EF525E269F9001D1D81 /* ObjectPublisher+Reactive.swift */; }; + B5944EF925E269F9001D1D81 /* ObjectPublisher+Reactive.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5944EF525E269F9001D1D81 /* ObjectPublisher+Reactive.swift */; }; + B5944EFB25E8E8DA001D1D81 /* ListPublisher.SnapshotPublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5944EFA25E8E8DA001D1D81 /* ListPublisher.SnapshotPublisher.swift */; }; + B5944EFC25E8E8DA001D1D81 /* ListPublisher.SnapshotPublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5944EFA25E8E8DA001D1D81 /* ListPublisher.SnapshotPublisher.swift */; }; + B5944EFD25E8E8DA001D1D81 /* ListPublisher.SnapshotPublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5944EFA25E8E8DA001D1D81 /* ListPublisher.SnapshotPublisher.swift */; }; + B5944EFE25E8E8DA001D1D81 /* ListPublisher.SnapshotPublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5944EFA25E8E8DA001D1D81 /* ListPublisher.SnapshotPublisher.swift */; }; B596BBB21DD5A014001DCDD9 /* ConvenienceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B596BBAD1DD59FDB001DCDD9 /* ConvenienceTests.swift */; }; B596BBB31DD5A014001DCDD9 /* ConvenienceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B596BBAD1DD59FDB001DCDD9 /* ConvenienceTests.swift */; }; B596BBB41DD5A016001DCDD9 /* ConvenienceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B596BBAD1DD59FDB001DCDD9 /* ConvenienceTests.swift */; }; @@ -677,6 +685,14 @@ B5AEFAB61C9962AE00AD137F /* CoreStoreBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5AEFAB41C9962AE00AD137F /* CoreStoreBridge.swift */; }; B5AEFAB71C9962AE00AD137F /* CoreStoreBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5AEFAB41C9962AE00AD137F /* CoreStoreBridge.swift */; }; B5AEFAB81C9962AE00AD137F /* CoreStoreBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5AEFAB41C9962AE00AD137F /* CoreStoreBridge.swift */; }; + B5B866DB25E9012F00335476 /* ListPublisher+Reactive.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5B866DA25E9012F00335476 /* ListPublisher+Reactive.swift */; }; + B5B866DC25E9012F00335476 /* ListPublisher+Reactive.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5B866DA25E9012F00335476 /* ListPublisher+Reactive.swift */; }; + B5B866DD25E9012F00335476 /* ListPublisher+Reactive.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5B866DA25E9012F00335476 /* ListPublisher+Reactive.swift */; }; + B5B866DE25E9012F00335476 /* ListPublisher+Reactive.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5B866DA25E9012F00335476 /* ListPublisher+Reactive.swift */; }; + B5B866E025E9048000335476 /* ObjectPublisher.SnapshotPublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5B866DF25E9048000335476 /* ObjectPublisher.SnapshotPublisher.swift */; }; + B5B866E125E9048000335476 /* ObjectPublisher.SnapshotPublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5B866DF25E9048000335476 /* ObjectPublisher.SnapshotPublisher.swift */; }; + B5B866E225E9048000335476 /* ObjectPublisher.SnapshotPublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5B866DF25E9048000335476 /* ObjectPublisher.SnapshotPublisher.swift */; }; + B5B866E325E9048000335476 /* ObjectPublisher.SnapshotPublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5B866DF25E9048000335476 /* ObjectPublisher.SnapshotPublisher.swift */; }; B5BF7FAD234C41E90070E741 /* Internals.DiffableDataSourceSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FAC234C41E90070E741 /* Internals.DiffableDataSourceSnapshot.swift */; }; B5BF7FAE234C41E90070E741 /* Internals.DiffableDataSourceSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FAC234C41E90070E741 /* Internals.DiffableDataSourceSnapshot.swift */; }; B5BF7FAF234C41E90070E741 /* Internals.DiffableDataSourceSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FAC234C41E90070E741 /* Internals.DiffableDataSourceSnapshot.swift */; }; @@ -725,14 +741,6 @@ B5C795C425DD651F00BDACC1 /* DataStack+Reactive.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C795C225DD651F00BDACC1 /* DataStack+Reactive.swift */; }; B5C795C525DD651F00BDACC1 /* DataStack+Reactive.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C795C225DD651F00BDACC1 /* DataStack+Reactive.swift */; }; B5C795C625DD651F00BDACC1 /* DataStack+Reactive.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C795C225DD651F00BDACC1 /* DataStack+Reactive.swift */; }; - B5C795C825DD692600BDACC1 /* PublisherConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C795C725DD692600BDACC1 /* PublisherConvertible.swift */; }; - B5C795C925DD692600BDACC1 /* PublisherConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C795C725DD692600BDACC1 /* PublisherConvertible.swift */; }; - B5C795CA25DD692600BDACC1 /* PublisherConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C795C725DD692600BDACC1 /* PublisherConvertible.swift */; }; - B5C795CB25DD692600BDACC1 /* PublisherConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C795C725DD692600BDACC1 /* PublisherConvertible.swift */; }; - B5C795CD25DD6C7600BDACC1 /* PublisherProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C795CC25DD6C7600BDACC1 /* PublisherProvider.swift */; }; - B5C795CE25DD6C7600BDACC1 /* PublisherProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C795CC25DD6C7600BDACC1 /* PublisherProvider.swift */; }; - B5C795CF25DD6C7600BDACC1 /* PublisherProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C795CC25DD6C7600BDACC1 /* PublisherProvider.swift */; }; - B5C795D025DD6C7600BDACC1 /* PublisherProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C795CC25DD6C7600BDACC1 /* PublisherProvider.swift */; }; B5C795D225E0DD1B00BDACC1 /* ListSnapshot.SectionInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C795D125E0DD1B00BDACC1 /* ListSnapshot.SectionInfo.swift */; }; B5C795D325E0DD1B00BDACC1 /* ListSnapshot.SectionInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C795D125E0DD1B00BDACC1 /* ListSnapshot.SectionInfo.swift */; }; B5C795D425E0DD1B00BDACC1 /* ListSnapshot.SectionInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C795D125E0DD1B00BDACC1 /* ListSnapshot.SectionInfo.swift */; }; @@ -1128,6 +1136,8 @@ B5831B741F34AC7A00A9F647 /* RelationshipProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelationshipProtocol.swift; sourceTree = ""; }; B5831B791F34ACBA00A9F647 /* Transformable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Transformable.swift; sourceTree = ""; }; B58D0C621EAA0C7E003EDD87 /* NSManagedObject+DynamicModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSManagedObject+DynamicModel.swift"; sourceTree = ""; }; + B5944EF525E269F9001D1D81 /* ObjectPublisher+Reactive.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ObjectPublisher+Reactive.swift"; sourceTree = ""; }; + B5944EFA25E8E8DA001D1D81 /* ListPublisher.SnapshotPublisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListPublisher.SnapshotPublisher.swift; sourceTree = ""; }; B596BBAD1DD59FDB001DCDD9 /* ConvenienceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConvenienceTests.swift; sourceTree = ""; }; B596BBB51DD5BC67001DCDD9 /* FetchableSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FetchableSource.swift; sourceTree = ""; }; B596BBBA1DD5C39F001DCDD9 /* QueryableSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QueryableSource.swift; sourceTree = ""; }; @@ -1145,6 +1155,8 @@ B5AA37FC235C3D1A00FFD4B9 /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS6.0.sdk/System/Library/Frameworks/SwiftUI.framework; sourceTree = DEVELOPER_DIR; }; B5AD60CD1C90141E00F2B2E8 /* Package.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = SOURCE_ROOT; }; B5AEFAB41C9962AE00AD137F /* CoreStoreBridge.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreStoreBridge.swift; sourceTree = ""; }; + B5B866DA25E9012F00335476 /* ListPublisher+Reactive.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ListPublisher+Reactive.swift"; sourceTree = ""; }; + B5B866DF25E9048000335476 /* ObjectPublisher.SnapshotPublisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObjectPublisher.SnapshotPublisher.swift; sourceTree = ""; }; B5BF7FAC234C41E90070E741 /* Internals.DiffableDataSourceSnapshot.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Internals.DiffableDataSourceSnapshot.swift; sourceTree = ""; }; B5BF7FB1234C97910070E741 /* DiffableDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiffableDataSource.swift; sourceTree = ""; }; B5BF7FB6234C97CE0070E741 /* DiffableDataSource.TableViewAdapter-UIKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DiffableDataSource.TableViewAdapter-UIKit.swift"; sourceTree = ""; }; @@ -1157,8 +1169,6 @@ B5C7959825D7D8B300BDACC1 /* ListReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListReader.swift; sourceTree = ""; }; B5C795A025D7EB2200BDACC1 /* ForEach+SwiftUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ForEach+SwiftUI.swift"; sourceTree = ""; }; B5C795C225DD651F00BDACC1 /* DataStack+Reactive.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DataStack+Reactive.swift"; sourceTree = ""; }; - B5C795C725DD692600BDACC1 /* PublisherConvertible.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PublisherConvertible.swift; sourceTree = ""; }; - B5C795CC25DD6C7600BDACC1 /* PublisherProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PublisherProvider.swift; sourceTree = ""; }; B5C795D125E0DD1B00BDACC1 /* ListSnapshot.SectionInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListSnapshot.SectionInfo.swift; sourceTree = ""; }; B5C976E21C6C9F6A00B1AF90 /* UnsafeDataTransaction+Observing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UnsafeDataTransaction+Observing.swift"; sourceTree = ""; }; B5C976E61C6E3A5900B1AF90 /* Internals.CoreStoreFetchedResultsController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Internals.CoreStoreFetchedResultsController.swift; sourceTree = ""; }; @@ -1756,9 +1766,11 @@ B5C795BE25D933C200BDACC1 /* Reactive Programming */ = { isa = PBXGroup; children = ( - B5C795C725DD692600BDACC1 /* PublisherConvertible.swift */, - B5C795CC25DD6C7600BDACC1 /* PublisherProvider.swift */, B5C795C225DD651F00BDACC1 /* DataStack+Reactive.swift */, + B5B866DA25E9012F00335476 /* ListPublisher+Reactive.swift */, + B5944EF525E269F9001D1D81 /* ObjectPublisher+Reactive.swift */, + B5944EFA25E8E8DA001D1D81 /* ListPublisher.SnapshotPublisher.swift */, + B5B866DF25E9048000335476 /* ObjectPublisher.SnapshotPublisher.swift */, ); name = "Reactive Programming"; sourceTree = ""; @@ -1953,6 +1965,7 @@ B50564CC2350699700482308 /* Protocols */, B53B275E1EE3B92E00E9B352 /* CoreStoreManagedObject.swift */, B533C4DA1D7D4BFA001383CB /* DispatchQueue+CoreStore.swift */, + B5DE522A230BD7CC00A22534 /* Internals.swift */, B50C3F0223D1B01C00B29880 /* Internals.AnyFieldCoder.swift */, B5C976E61C6E3A5900B1AF90 /* Internals.CoreStoreFetchedResultsController.swift */, B5474D142227C08700B21FEC /* Internals.CoreStoreFetchRequest.swift */, @@ -1969,7 +1982,6 @@ B5E84F2B1AFF849C0064E85B /* Internals.NotificationObserver.swift */, B5277676234F265F0056BE9F /* Internals.SharedNotificationObserver.swift */, B50E17602351FA66004F033C /* Internals.Closure.swift */, - B5DE522A230BD7CC00A22534 /* Internals.swift */, B5E84F2D1AFF849C0064E85B /* Internals.WeakObject.swift */, B51260881E9B252B00402229 /* NSEntityDescription+DynamicModel.swift */, B56923C31EB823B4007C4DC9 /* NSEntityDescription+Migration.swift */, @@ -2328,7 +2340,6 @@ B55BB4D4235012AE00C33E34 /* ObjectRepresentation.swift in Sources */, B5ECDBFF1CA80CBA00C7F112 /* CSWhere.swift in Sources */, B5ECDC051CA8138100C7F112 /* CSOrderBy.swift in Sources */, - B5C795CD25DD6C7600BDACC1 /* PublisherProvider.swift in Sources */, B5E1B5981CAA0C23007FD580 /* CSObjectObserver.swift in Sources */, B5DAFB4A2203E01D003FCCD0 /* KeyPathGenericBindings.swift in Sources */, B5519A5F1CA21954002BEF78 /* CSAsynchronousDataTransaction.swift in Sources */, @@ -2364,6 +2375,7 @@ B5E1B59D1CAA2568007FD580 /* CSDataStack+Observing.swift in Sources */, B549F6731E56A92800FBAB2D /* CoreDataNativeType.swift in Sources */, B509C7F41E54511B0061C547 /* ImportableAttributeType.swift in Sources */, + B5B866E025E9048000335476 /* ObjectPublisher.SnapshotPublisher.swift in Sources */, B5E84F0E1AFF847B0064E85B /* Tweak.swift in Sources */, B5E1B5931CAA0C15007FD580 /* CSObjectMonitor.swift in Sources */, B5277677234F265F0056BE9F /* Internals.SharedNotificationObserver.swift in Sources */, @@ -2382,6 +2394,7 @@ B53CA9A21EF1EF1600E0F440 /* PartialObject.swift in Sources */, B56007111B3F6BD500A9A8F9 /* Into.swift in Sources */, B5E84F111AFF847B0064E85B /* Select.swift in Sources */, + B5B866DB25E9012F00335476 /* ListPublisher+Reactive.swift in Sources */, B51260931E9B28F100402229 /* Internals.EntityIdentifier.swift in Sources */, B56E4ECA23CD9B4800E1708C /* Field.swift in Sources */, B5DAFB482203D9F8003FCCD0 /* Where.Expression.swift in Sources */, @@ -2391,6 +2404,7 @@ B509D7BA23C846E300F42824 /* Value.Required.swift in Sources */, B53FB9FE1CAB2D2F00F0D40A /* CSMigrationResult.swift in Sources */, B5DBE2D21C991B3E00B5CEFA /* CSDataStack.swift in Sources */, + B5944EF625E269F9001D1D81 /* ObjectPublisher+Reactive.swift in Sources */, B50392F91C478FF3009900CA /* NSManagedObject+Transaction.swift in Sources */, B53FBA181CAB63E200F0D40A /* NSManagedObject+ObjectiveC.swift in Sources */, B5202CFA1C04688100DED140 /* NSFetchedResultsController+Convenience.swift in Sources */, @@ -2408,6 +2422,7 @@ B5A1DAC81F111BFA003CF369 /* KeyPath+Querying.swift in Sources */, B5ECDC111CA816E500C7F112 /* CSTweak.swift in Sources */, B509D7D323C84E1900F42824 /* Transformable.Required.swift in Sources */, + B5944EFB25E8E8DA001D1D81 /* ListPublisher.SnapshotPublisher.swift in Sources */, B56923C41EB823B4007C4DC9 /* NSEntityDescription+Migration.swift in Sources */, B5E84F411AFF8CCD0064E85B /* TypeErasedClauses.swift in Sources */, B5E84F0D1AFF847B0064E85B /* BaseDataTransaction+Querying.swift in Sources */, @@ -2496,7 +2511,6 @@ B5ECDBEC1CA6BF2000C7F112 /* CSFrom.swift in Sources */, B56923EC1EB827F6007C4DC9 /* SchemaMappingProvider.swift in Sources */, B5E834B91B76311F001D3D50 /* BaseDataTransaction+Importing.swift in Sources */, - B5C795C825DD692600BDACC1 /* PublisherConvertible.swift in Sources */, B5E84EE61AFF84610064E85B /* DefaultLogger.swift in Sources */, B53FBA041CAB300C00F0D40A /* CSMigrationType.swift in Sources */, B57E6FAC23D30A5B000FD031 /* FieldRelationshipProtocol.swift in Sources */, @@ -2579,7 +2593,6 @@ B5ECDC011CA80CBA00C7F112 /* CSWhere.swift in Sources */, B55BB4D5235012AE00C33E34 /* ObjectRepresentation.swift in Sources */, B5ECDC071CA8138100C7F112 /* CSOrderBy.swift in Sources */, - B5C795CE25DD6C7600BDACC1 /* PublisherProvider.swift in Sources */, B5E1B59A1CAA0C23007FD580 /* CSObjectObserver.swift in Sources */, B5519A601CA21954002BEF78 /* CSAsynchronousDataTransaction.swift in Sources */, B5277678234F265F0056BE9F /* Internals.SharedNotificationObserver.swift in Sources */, @@ -2614,6 +2627,7 @@ B55BB4DA23503B9600C33E34 /* EnvironmentValues+DataSources.swift in Sources */, B5E1B5951CAA0C15007FD580 /* CSObjectMonitor.swift in Sources */, B509D7CA23C8491C00F42824 /* Relationship.ToManyOrdered.swift in Sources */, + B5B866E125E9048000335476 /* ObjectPublisher.SnapshotPublisher.swift in Sources */, 18166887232B9ED60097C275 /* String+KeyPaths.swift in Sources */, B5ECDC2B1CA81CC700C7F112 /* CSDataStack+Transaction.swift in Sources */, B56923F11EB827F6007C4DC9 /* XcodeSchemaMappingProvider.swift in Sources */, @@ -2632,6 +2646,7 @@ B5F8496D234898240029D57B /* ListSnapshot.swift in Sources */, B53FBA001CAB2D2F00F0D40A /* CSMigrationResult.swift in Sources */, B5DBE2D31C991B3E00B5CEFA /* CSDataStack.swift in Sources */, + B5B866DC25E9012F00335476 /* ListPublisher+Reactive.swift in Sources */, 82BA18B41C4BBD3900A0916E /* BaseDataTransaction+Importing.swift in Sources */, B514EF1223A8DB1D0093DBA4 /* DiffableDataSource.BaseAdapter.swift in Sources */, B53FBA1A1CAB63E200F0D40A /* NSManagedObject+ObjectiveC.swift in Sources */, @@ -2641,6 +2656,7 @@ B53D9E5A23513712000F48FB /* DiffableDataSourceSnapshotProtocol.swift in Sources */, 82BA18BC1C4BBD4A00A0916E /* OrderBy.swift in Sources */, 82BA18B01C4BBD3100A0916E /* NSManagedObject+Transaction.swift in Sources */, + B5944EF725E269F9001D1D81 /* ObjectPublisher+Reactive.swift in Sources */, B50132312346B76E00FC238B /* Internals.FetchedDiffableDataSourceSnapshotDelegate.swift in Sources */, 82BA18D41C4BBD7100A0916E /* NSManagedObjectContext+Querying.swift in Sources */, 82BA18D51C4BBD7100A0916E /* NSManagedObjectContext+Setup.swift in Sources */, @@ -2658,6 +2674,7 @@ B5DE5231230BDA1300A22534 /* CoreStoreDefaults.swift in Sources */, B56E4ED523CDB54A00E1708C /* FieldProtocol.swift in Sources */, B52F74461E9B8724005F3DAC /* XcodeDataModelSchema.swift in Sources */, + B5944EFC25E8E8DA001D1D81 /* ListPublisher.SnapshotPublisher.swift in Sources */, 82BA18C61C4BBD5900A0916E /* DataStack+Migration.swift in Sources */, B59851491C90289D00C99590 /* NSPersistentStoreCoordinator+Setup.swift in Sources */, B596BBB71DD5BC67001DCDD9 /* FetchableSource.swift in Sources */, @@ -2745,7 +2762,6 @@ 82BA18B91C4BBD4A00A0916E /* From.swift in Sources */, B53FBA061CAB300C00F0D40A /* CSMigrationType.swift in Sources */, 82BA18BE1C4BBD4A00A0916E /* Tweak.swift in Sources */, - B5C795C925DD692600BDACC1 /* PublisherConvertible.swift in Sources */, B509D7C523C848DA00F42824 /* Relationship.ToOne.swift in Sources */, B5DBE2CE1C9914A900B5CEFA /* CSCoreStore.swift in Sources */, B57E6FAD23D30A5B000FD031 /* FieldRelationshipProtocol.swift in Sources */, @@ -2828,7 +2844,6 @@ B55514ED1EED8BF900BAB888 /* From+Querying.swift in Sources */, B5BF7FC4234D7B2E0070E741 /* ObjectPublisher.swift in Sources */, B596BBBE1DD5C39F001DCDD9 /* QueryableSource.swift in Sources */, - B5C795D025DD6C7600BDACC1 /* PublisherProvider.swift in Sources */, B546F9601C9A12B800D5AC55 /* CSSQliteStore.swift in Sources */, B55BB4D7235012AE00C33E34 /* ObjectRepresentation.swift in Sources */, B5ECDC0F1CA8161B00C7F112 /* CSGroupBy.swift in Sources */, @@ -2864,6 +2879,7 @@ B50EE14523473C97009B8C47 /* CoreStoreObject+DataSources.swift in Sources */, B509D7CC23C8491C00F42824 /* Relationship.ToManyOrdered.swift in Sources */, B55BB4D823503B9500C33E34 /* EnvironmentValues+DataSources.swift in Sources */, + B5B866E325E9048000335476 /* ObjectPublisher.SnapshotPublisher.swift in Sources */, 18166889232B9ED80097C275 /* String+KeyPaths.swift in Sources */, B52DD1A61BE1F92F00949AFE /* BaseDataTransaction+Importing.swift in Sources */, B56923F31EB827F6007C4DC9 /* XcodeSchemaMappingProvider.swift in Sources */, @@ -2882,6 +2898,7 @@ B53FBA1C1CAB63E200F0D40A /* NSManagedObject+ObjectiveC.swift in Sources */, B5F8496F234898240029D57B /* ListSnapshot.swift in Sources */, B5831F452212700500D8604C /* Where.Expression.swift in Sources */, + B5B866DE25E9012F00335476 /* ListPublisher+Reactive.swift in Sources */, B514EF1423A8DB1E0093DBA4 /* DiffableDataSource.BaseAdapter.swift in Sources */, B51260961E9B28F100402229 /* Internals.EntityIdentifier.swift in Sources */, B5ECDBE31CA6BB2B00C7F112 /* CSBaseDataTransaction+Querying.swift in Sources */, @@ -2891,6 +2908,7 @@ B52DD1C71BE1F94600949AFE /* NSManagedObjectContext+Querying.swift in Sources */, B52DD1C81BE1F94600949AFE /* NSManagedObjectContext+Setup.swift in Sources */, B53D9E5C23513712000F48FB /* DiffableDataSourceSnapshotProtocol.swift in Sources */, + B5944EF925E269F9001D1D81 /* ObjectPublisher+Reactive.swift in Sources */, B52DD1C31BE1F94600949AFE /* Internals.NotificationObserver.swift in Sources */, B52DD1A81BE1F93200949AFE /* DataStack+Querying.swift in Sources */, B50132332346B76E00FC238B /* Internals.FetchedDiffableDataSourceSnapshotDelegate.swift in Sources */, @@ -2908,6 +2926,7 @@ B5D8CA792346EAEF0055D7D1 /* DataStack+DataSources.swift in Sources */, B56E4ED723CDB54A00E1708C /* FieldProtocol.swift in Sources */, B56923C71EB823B4007C4DC9 /* NSEntityDescription+Migration.swift in Sources */, + B5944EFE25E8E8DA001D1D81 /* ListPublisher.SnapshotPublisher.swift in Sources */, B5DE5233230BDA1300A22534 /* CoreStoreDefaults.swift in Sources */, B52DD1A51BE1F92F00949AFE /* ImportableUniqueObject.swift in Sources */, B5E222271CA4E12600BA2E95 /* CSSynchronousDataTransaction.swift in Sources */, @@ -2995,7 +3014,6 @@ B5220E171D1306DF009BC71E /* UnsafeDataTransaction+Observing.swift in Sources */, B56923EF1EB827F6007C4DC9 /* SchemaMappingProvider.swift in Sources */, B53FBA081CAB300C00F0D40A /* CSMigrationType.swift in Sources */, - B5C795CB25DD692600BDACC1 /* PublisherConvertible.swift in Sources */, B509D7C723C848DA00F42824 /* Relationship.ToOne.swift in Sources */, B5519A5C1CA2008C002BEF78 /* CSBaseDataTransaction.swift in Sources */, B57E6FAF23D30A5B000FD031 /* FieldRelationshipProtocol.swift in Sources */, @@ -3079,7 +3097,6 @@ B5ECDC081CA8138100C7F112 /* CSOrderBy.swift in Sources */, B55BB4D6235012AE00C33E34 /* ObjectRepresentation.swift in Sources */, B5E1B59B1CAA0C23007FD580 /* CSObjectObserver.swift in Sources */, - B5C795CF25DD6C7600BDACC1 /* PublisherProvider.swift in Sources */, B5519A611CA21954002BEF78 /* CSAsynchronousDataTransaction.swift in Sources */, B5FE4DAE1C85D44E00FA6A91 /* SQLiteStore.swift in Sources */, B5277679234F265F0056BE9F /* Internals.SharedNotificationObserver.swift in Sources */, @@ -3114,6 +3131,7 @@ B509D7CB23C8491C00F42824 /* Relationship.ToManyOrdered.swift in Sources */, 18166888232B9ED70097C275 /* String+KeyPaths.swift in Sources */, B56321911BD65216006C9394 /* BaseDataTransaction+Importing.swift in Sources */, + B5B866E225E9048000335476 /* ObjectPublisher.SnapshotPublisher.swift in Sources */, B56923F21EB827F6007C4DC9 /* XcodeSchemaMappingProvider.swift in Sources */, B56321811BD65216006C9394 /* DataStack.swift in Sources */, B56321A81BD65219006C9394 /* NSManagedObject+Convenience.swift in Sources */, @@ -3132,6 +3150,7 @@ B53FBA011CAB2D2F00F0D40A /* CSMigrationResult.swift in Sources */, B5DBE2D41C991B3E00B5CEFA /* CSDataStack.swift in Sources */, B514EF1323A8DB1D0093DBA4 /* DiffableDataSource.BaseAdapter.swift in Sources */, + B5B866DD25E9012F00335476 /* ListPublisher+Reactive.swift in Sources */, B50392FA1C47963F009900CA /* NSManagedObject+Transaction.swift in Sources */, B53FBA1B1CAB63E200F0D40A /* NSManagedObject+ObjectiveC.swift in Sources */, B5519A5B1CA2008C002BEF78 /* CSBaseDataTransaction.swift in Sources */, @@ -3141,6 +3160,7 @@ B56321AB1BD6521C006C9394 /* Internals.FetchedResultsControllerDelegate.swift in Sources */, B563219C1BD65216006C9394 /* SectionBy.swift in Sources */, B50132322346B76E00FC238B /* Internals.FetchedDiffableDataSourceSnapshotDelegate.swift in Sources */, + B5944EF825E269F9001D1D81 /* ObjectPublisher+Reactive.swift in Sources */, B56321B21BD6521C006C9394 /* NSManagedObjectContext+Querying.swift in Sources */, B5FE4DA41C8481E100FA6A91 /* StorageInterface.swift in Sources */, B56321B31BD6521C006C9394 /* NSManagedObjectContext+Setup.swift in Sources */, @@ -3158,6 +3178,7 @@ B56E4ED623CDB54A00E1708C /* FieldProtocol.swift in Sources */, B5DE5232230BDA1300A22534 /* CoreStoreDefaults.swift in Sources */, B598514A1C90289E00C99590 /* NSPersistentStoreCoordinator+Setup.swift in Sources */, + B5944EFD25E8E8DA001D1D81 /* ListPublisher.SnapshotPublisher.swift in Sources */, B52F74471E9B8724005F3DAC /* XcodeDataModelSchema.swift in Sources */, B5FEC1901C9166E700532541 /* NSPersistentStore+Setup.swift in Sources */, B56321A11BD65216006C9394 /* ListMonitor.swift in Sources */, @@ -3246,7 +3267,6 @@ B56321861BD65216006C9394 /* CoreStoreLogger.swift in Sources */, B53FBA071CAB300C00F0D40A /* CSMigrationType.swift in Sources */, B56321841BD65216006C9394 /* DefaultLogger.swift in Sources */, - B5C795CA25DD692600BDACC1 /* PublisherConvertible.swift in Sources */, B509D7C623C848DA00F42824 /* Relationship.ToOne.swift in Sources */, B5DBE2CF1C9914A900B5CEFA /* CSCoreStore.swift in Sources */, B57E6FAE23D30A5B000FD031 /* FieldRelationshipProtocol.swift in Sources */, @@ -3339,8 +3359,8 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; INFOPLIST_FILE = Sources/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; - MACOSX_DEPLOYMENT_TARGET = 10.12; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + MACOSX_DEPLOYMENT_TARGET = 10.13; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; OTHER_SWIFT_FLAGS = "-D DEBUG"; @@ -3406,8 +3426,8 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; INFOPLIST_FILE = Sources/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; - MACOSX_DEPLOYMENT_TARGET = 10.12; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + MACOSX_DEPLOYMENT_TARGET = 10.13; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = com.johnestropia.CoreStore; PRODUCT_NAME = CoreStore; @@ -3528,6 +3548,7 @@ SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_SWIFT3_OBJC_INFERENCE = Off; TARGETED_DEVICE_FAMILY = 3; + TVOS_DEPLOYMENT_TARGET = 11.0; }; name = Debug; }; @@ -3554,6 +3575,7 @@ SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_SWIFT3_OBJC_INFERENCE = Off; TARGETED_DEVICE_FAMILY = 3; + TVOS_DEPLOYMENT_TARGET = 11.0; }; name = Release; }; @@ -3714,6 +3736,7 @@ SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_SWIFT3_OBJC_INFERENCE = Off; TARGETED_DEVICE_FAMILY = 4; + WATCHOS_DEPLOYMENT_TARGET = 4.0; }; name = Debug; }; @@ -3742,6 +3765,7 @@ SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_SWIFT3_OBJC_INFERENCE = Off; TARGETED_DEVICE_FAMILY = 4; + WATCHOS_DEPLOYMENT_TARGET = 4.0; }; name = Release; }; diff --git a/Package.swift b/Package.swift index 8fab55c..fd66ca9 100644 --- a/Package.swift +++ b/Package.swift @@ -29,7 +29,7 @@ import PackageDescription let package = Package( name: "CoreStore", platforms: [ - .macOS(.v10_12), .iOS(.v10), .tvOS(.v10), .watchOS(.v3) + .macOS(.v10_13), .iOS(.v11), .tvOS(.v11), .watchOS(.v4) ], products: [ .library(name: "CoreStore", type: .static, targets: ["CoreStore"]) diff --git a/README.md b/README.md index 750be08..81e2482 100644 --- a/README.md +++ b/README.md @@ -19,10 +19,10 @@ Unleashing the real power of Core Data with the elegance and safety of Swift

-* **Swift 5.3:** iOS 10+ / macOS 10.12+ / watchOS 3.0+ / tvOS 10.0+ -* Previously supported Swift versions: [Swift 3.2](https://github.com/JohnEstropia/CoreStore/tree/4.2.3), [Swift 4.2](https://github.com/JohnEstropia/CoreStore/tree/6.2.1), [Swift 5.0](https://github.com/JohnEstropia/CoreStore/tree/6.3.2), [Swift 5.1](https://github.com/JohnEstropia/CoreStore/tree/7.0.4) +* **Swift 5.4:** iOS 11+ / macOS 10.13+ / watchOS 4.0+ / tvOS 11.0+ +* Previously supported Swift versions: [Swift 3.2](https://github.com/JohnEstropia/CoreStore/tree/4.2.3), [Swift 4.2](https://github.com/JohnEstropia/CoreStore/tree/6.2.1), [Swift 5.0](https://github.com/JohnEstropia/CoreStore/tree/6.3.2), [Swift 5.1](https://github.com/JohnEstropia/CoreStore/tree/7.0.4), [Swift 5.3](https://github.com/JohnEstropia/CoreStore/tree/7.3.1) -Upgrading from CoreStore 6.x (swift 5.0) to 7.x (Swift 5.1)? Check out the [🆕 features](#features) and make sure to read the [Change logs](https://github.com/JohnEstropia/CoreStore/releases). +Upgrading from previous CoreStore versions? Check out the [🆕 features](#features) and make sure to read the [Change logs](https://github.com/JohnEstropia/CoreStore/releases). CoreStore is now part of the [Swift Source Compatibility projects](https://swift.org/source-compatibility/#current-list-of-projects). @@ -33,6 +33,7 @@ CoreStore was (and is) heavily shaped by real-world needs of developing data-dep ### Features +- **SwiftUI and Combine API utilities.** - **Backwards-portable DiffableDataSources implementation!** `UITableViews` and `UICollectionViews` now have a new ally: `ListPublisher`s provide diffable snapshots that make reloading animations very easy and very safe. Say goodbye to `UITableViews` and `UICollectionViews` reload errors! - **💎Tight design around Swift’s code elegance and type safety.** CoreStore fully utilizes Swift's community-driven language features. - **🚦Safer concurrency architecture.** CoreStore makes it hard to fall into common concurrency mistakes. The main `NSManagedObjectContext` is strictly read-only, while all updates are done through serial *transactions*. *(See [Saving and processing transactions](#saving-and-processing-transactions))* diff --git a/Sources/DataStack+Reactive.swift b/Sources/DataStack+Reactive.swift index f9cdab8..f1f2c7b 100644 --- a/Sources/DataStack+Reactive.swift +++ b/Sources/DataStack+Reactive.swift @@ -31,10 +31,47 @@ import Combine // MARK: - DataStack @available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) -extension DataStack: PublisherCompatible {} +extension DataStack { + + // MARK: Public + + /** + Combine utilities for the `DataStack` are exposed through this namespace + */ + public var reactive: DataStack.ReactiveNamespace { + + return .init(self) + } + + // MARK: - ReactiveNamespace + + /** + Combine utilities for the `DataStack` are exposed through this namespace. Extend this type if you need to add other Combine Publisher utilities for `DataStack`. + */ + public struct ReactiveNamespace { + + // MARK: Public + + /** + The `DataStack` instance + */ + public let base: DataStack + + + // MARK: Internal + + internal init(_ base: DataStack) { + + self.base = base + } + } +} + + +// MARK: - DataStack.ReactiveNamespace @available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) -extension PublisherProvider where Base == DataStack { +extension DataStack.ReactiveNamespace { // MARK: Public diff --git a/Sources/ListPublisher+Reactive.swift b/Sources/ListPublisher+Reactive.swift new file mode 100644 index 0000000..c17db51 --- /dev/null +++ b/Sources/ListPublisher+Reactive.swift @@ -0,0 +1,109 @@ +// +// ListPublisher+Reactive.swift +// CoreStore +// +// Copyright © 2021 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(Combine) + +import Combine + + +// MARK: - ListPublisher + +@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) +extension ListPublisher { + + // MARK: Public + + /** + Combine utilities for the `ListPublisher` are exposed through this namespace + */ + public var reactive: ListPublisher.ReactiveNamespace { + + return .init(self) + } + + + // MARK: - ReactiveNamespace + + /** + Combine utilities for the `ListPublisher` are exposed through this namespace. Extend this type if you need to add other Combine Publisher utilities for `ListPublisher`. + */ + public struct ReactiveNamespace { + + // MARK: Public + + /** + The `ListPublisher` instance + */ + public let base: ListPublisher + + + // MARK: Internal + + internal init(_ base: ListPublisher) { + + self.base = base + } + } +} + + +// MARK: - ListPublisher.ReactiveNamespace + +@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) +extension ListPublisher.ReactiveNamespace { + + // MARK: Public + + /** + Returns a `Publisher` that emits a `ListSnapshot` whenever changes occur in the `ListPublisher` + ``` + listPublisher.reactive + .snapshot(emitInitialValue: true) + .sink( + receiveCompletion: { result in + // ... + }, + receiveValue: { (listSnapshot) in + dataSource.apply( + listSnapshot, + animatingDifferences: true + ) + } + ) + .store(in: &cancellables) + ``` + - parameter emitInitialValue: If `true`, the current value is immediately emitted to the first subscriber. If `false`, the event fires only starting the next `ListSnapshot` update. + - returns: A `Publisher` that emits a `ListSnapshot` whenever changes occur in the `ListPublisher`. + */ + public func snapshot(emitInitialValue: Bool = true) -> ListPublisher.SnapshotPublisher { + + return .init( + listPublisher: self.base, + emitInitialValue: emitInitialValue + ) + } +} + +#endif diff --git a/Sources/ListPublisher.SnapshotPublisher.swift b/Sources/ListPublisher.SnapshotPublisher.swift new file mode 100644 index 0000000..8a8e04a --- /dev/null +++ b/Sources/ListPublisher.SnapshotPublisher.swift @@ -0,0 +1,150 @@ +// +// ListPublisher.SnapshotPublisher.swift +// CoreStore +// +// Copyright © 2021 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(Combine) +import Combine +import CoreData + + +// MARK: - ListPublisher + +@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) +extension ListPublisher { + + // MARK: - SnapshotPublisher + + /** + A `Publisher` that emits a `ListSnapshot` whenever changes occur in the `ListPublisher`. + */ + public struct SnapshotPublisher: Publisher { + + // MARK: Internal + + internal let listPublisher: ListPublisher + internal let emitInitialValue: Bool + + + // MARK: Publisher + + public typealias Output = ListSnapshot + public typealias Failure = Never + + public func receive(subscriber: S) where S.Input == Output, S.Failure == Failure { + + subscriber.receive( + subscription: ListSnapshotSubscription( + publisher: self.listPublisher, + emitInitialValue: self.emitInitialValue, + subscriber: subscriber + ) + ) + } + + + // MARK: - ListSnapshotSubscriber + + fileprivate final class ListSnapshotSubscriber: Subscriber { + + // MARK: Subscriber + + typealias Failure = Never + + func receive(subscription: Subscription) { + + subscription.request(.unlimited) + } + + func receive(_ input: Output) -> Subscribers.Demand { + + return .unlimited + } + + func receive(completion: Subscribers.Completion) {} + } + + + // MARK: - ListSnapshotSubscription + + fileprivate final class ListSnapshotSubscription: Subscription where S.Input == Output, S.Failure == Never { + + // MARK: FilePrivate + + init( + publisher: ListPublisher, + emitInitialValue: Bool, + subscriber: S + ) { + + self.publisher = publisher + self.emitInitialValue = emitInitialValue + self.subscriber = subscriber + } + + + // MARK: Subscription + + func request(_ demand: Subscribers.Demand) { + + guard demand > 0 else { + + return + } + self.publisher.addObserver( + self, + notifyInitial: self.emitInitialValue, + { [weak self] (publisher) in + + guard + let self = self, + let subscriber = self.subscriber + else { + + return + } + _ = subscriber.receive(publisher.snapshot) + } + ) + } + + + // MARK: Cancellable + + func cancel() { + + self.publisher.removeObserver(self) + self.subscriber = nil + } + + + // MARK: Private + + private let publisher: ListPublisher + private let emitInitialValue: Bool + private var subscriber: S? + } + } +} + +#endif diff --git a/Sources/ObjectPublisher+Reactive.swift b/Sources/ObjectPublisher+Reactive.swift new file mode 100644 index 0000000..f5060c8 --- /dev/null +++ b/Sources/ObjectPublisher+Reactive.swift @@ -0,0 +1,161 @@ +// +// ObjectPublisher+Reactive.swift +// CoreStore +// +// Copyright © 2021 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(Combine) + +import Combine + + +// MARK: - ObjectPublisher + +@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) +extension ObjectPublisher { + + // MARK: Public + + /** + Combine utilities for the `ObjectPublisher` are exposed through this namespace + */ + public var reactive: ObjectPublisher.ReactiveNamespace { + + return .init(self) + } + + + // MARK: - ReactiveNamespace + + /** + Combine utilities for the `ObjectPublisher` are exposed through this namespace. Extend this type if you need to add other Combine Publisher utilities for `ObjectPublisher`. + */ + @dynamicMemberLookup + public struct ReactiveNamespace { + + // MARK: Public + + /** + The `ObjectPublisher` instance + */ + public let base: ObjectPublisher + + + // MARK: Internal + + internal init(_ base: ObjectPublisher) { + + self.base = base + } + } +} + + +// MARK: - ObjectPublisher.ReactiveNamespace + +@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) +extension ObjectPublisher.ReactiveNamespace { + + // MARK: Public + + /** + Returns a `Publisher` that emits an `ObjectSnapshot?` whenever changes occur in the `ObjectPublisher`. The event emits `nil` if the object has been deletd. + ``` + objectPublisher.reactive + .snapshot(emitInitialValue: true) + .sink( + receiveCompletion: { result in + // ... + }, + receiveValue: { (objectSnapshot) in + tableViewCell.setObject(objectSnapshot) + } + ) + .store(in: &tableViewCell.cancellables) + ``` + - parameter emitInitialValue: If `true`, the current value is immediately emitted to the first subscriber. If `false`, the event fires only starting the next `ObjectSnapshot` update. + - returns: A `Publisher` that emits an `ObjectSnapshot?` whenever changes occur in the `ObjectPublisher`. The event emits `nil` if the object has been deletd. + */ + public func snapshot(emitInitialValue: Bool = true) -> ObjectPublisher.SnapshotPublisher { + + return .init( + objectPublisher: self.base, + emitInitialValue: emitInitialValue + ) + } +} + + +// MARK: - ObjectPublisher.ReactiveNamespace where O: NSManagedObject + +@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) +extension ObjectPublisher.ReactiveNamespace where O: NSManagedObject { + + /** + Returns the value for the property identified by a given key. + */ + public subscript(dynamicMember member: KeyPath) -> some Publisher { + + return self + .snapshot(emitInitialValue: true) + .map({ $0?[dynamicMember: member] }) + } +} + + +// MARK: - ObjectPublisher.ReactiveNamespace where O: CoreStoreObject + +@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) +extension ObjectPublisher.ReactiveNamespace where O: CoreStoreObject { + + /** + Returns the value for the property identified by a given key. + */ + public subscript(dynamicMember member: KeyPath.Stored>) -> some Publisher { + + return self + .snapshot(emitInitialValue: true) + .map({ $0?[dynamicMember: member] }) + } + + /** + Returns the value for the property identified by a given key. + */ + public subscript(dynamicMember member: KeyPath.Virtual>) -> some Publisher { + + return self + .snapshot(emitInitialValue: true) + .map({ $0?[dynamicMember: member] }) + } + + /** + Returns the value for the property identified by a given key. + */ + public subscript(dynamicMember member: KeyPath.Coded>) -> some Publisher { + + return self + .snapshot(emitInitialValue: true) + .map({ $0?[dynamicMember: member] }) + } +} + +#endif diff --git a/Sources/ObjectPublisher.SnapshotPublisher.swift b/Sources/ObjectPublisher.SnapshotPublisher.swift new file mode 100644 index 0000000..93d4e73 --- /dev/null +++ b/Sources/ObjectPublisher.SnapshotPublisher.swift @@ -0,0 +1,150 @@ +// +// ObjectPublisher.SnapshotPublisher.swift +// CoreStore +// +// Copyright © 2021 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(Combine) +import Combine +import CoreData + + +// MARK: - ObjectPublisher + +@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) +extension ObjectPublisher { + + // MARK: - SnapshotPublisher + + /** + A `Publisher` that emits an `ObjectSnapshot?` whenever changes occur in the `ObjectPublisher`. The event emits `nil` if the object has been deletd. + */ + public struct SnapshotPublisher: Publisher { + + // MARK: Internal + + internal let objectPublisher: ObjectPublisher + internal let emitInitialValue: Bool + + + // MARK: Publisher + + public typealias Output = ObjectSnapshot? + public typealias Failure = Never + + public func receive(subscriber: S) where S.Input == Output, S.Failure == Failure { + + subscriber.receive( + subscription: ObjectSnapshotSubscription( + publisher: self.objectPublisher, + emitInitialValue: self.emitInitialValue, + subscriber: subscriber + ) + ) + } + + + // MARK: - ObjectSnapshotSubscriber + + fileprivate final class ObjectSnapshotSubscriber: Subscriber { + + // MARK: Subscriber + + typealias Failure = Never + + func receive(subscription: Subscription) { + + subscription.request(.unlimited) + } + + func receive(_ input: Output) -> Subscribers.Demand { + + return .unlimited + } + + func receive(completion: Subscribers.Completion) {} + } + + + // MARK: - ObjectSnapshotSubscription + + fileprivate final class ObjectSnapshotSubscription: Subscription where S.Input == Output, S.Failure == Never { + + // MARK: FilePrivate + + init( + publisher: ObjectPublisher, + emitInitialValue: Bool, + subscriber: S + ) { + + self.publisher = publisher + self.emitInitialValue = emitInitialValue + self.subscriber = subscriber + } + + + // MARK: Subscription + + func request(_ demand: Subscribers.Demand) { + + guard demand > 0 else { + + return + } + self.publisher.addObserver( + self, + notifyInitial: self.emitInitialValue, + { [weak self] (publisher) in + + guard + let self = self, + let subscriber = self.subscriber + else { + + return + } + _ = subscriber.receive(publisher.snapshot) + } + ) + } + + + // MARK: Cancellable + + func cancel() { + + self.publisher.removeObserver(self) + self.subscriber = nil + } + + + // MARK: Private + + private let publisher: ObjectPublisher + private let emitInitialValue: Bool + private var subscriber: S? + } + } +} + +#endif diff --git a/Sources/ObjectSnapshot.swift b/Sources/ObjectSnapshot.swift index ba67b64..d80f8ae 100644 --- a/Sources/ObjectSnapshot.swift +++ b/Sources/ObjectSnapshot.swift @@ -174,21 +174,6 @@ extension ObjectSnapshot where O: NSManagedObject { self.values[key] = newValue } } - - - // MARK: Deprecated - - @available(*, deprecated, message: "Accessing the property directly now works") - public func value(forKeyPath keyPath: KeyPath) -> V! { - - return self[dynamicMember: keyPath] - } - - @available(*, deprecated, message: "Mutating the property directly now works") - public mutating func setValue(_ value: V!, forKeyPath keyPath: KeyPath) { - - self[dynamicMember: keyPath] = value - } } diff --git a/Sources/PublisherConvertible.swift b/Sources/PublisherConvertible.swift deleted file mode 100644 index f6e0dbf..0000000 --- a/Sources/PublisherConvertible.swift +++ /dev/null @@ -1,52 +0,0 @@ -// -// PublisherCompatible.swift -// CoreStore -// -// Copyright © 2021 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(Combine) - -import Combine - - -// MARK: - PublisherCompatible - -@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) -public protocol PublisherCompatible { - - associatedtype ReactiveBase - - var reactive: PublisherProvider { get } -} - -@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) -extension PublisherCompatible { - - // MARK: Public - - public var reactive: PublisherProvider { - - return .init(self) - } -} - -#endif diff --git a/Sources/PublisherProvider.swift b/Sources/PublisherProvider.swift deleted file mode 100644 index c891fa3..0000000 --- a/Sources/PublisherProvider.swift +++ /dev/null @@ -1,46 +0,0 @@ -// -// PublisherProvider.swift -// CoreStore -// -// Copyright © 2021 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(Combine) - -import Combine - - -// MARK: - PublisherProvider - -@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) -public struct PublisherProvider { - - // MARK: Public - - public let base: Base - - public init(_ base: Base) { - - self.base = base - } -} - -#endif