From f7471f56a40932946c8ec95f0e463dc31c50f630 Mon Sep 17 00:00:00 2001 From: John Estropia Date: Tue, 16 Feb 2021 09:12:36 +0900 Subject: [PATCH] Prototyping SwiftUI utilities --- CoreStore.xcodeproj/project.pbxproj | 94 +++++++++- Demo/Demo.xcodeproj/project.pbxproj | 44 +++-- Demo/⭐️Sources/Helpers/InstructionsView.swift | 4 +- .../Helpers/Menu/Menu.MainView.swift | 4 +- .../Advanced.EvolutionDemo.ProgressView.swift | 9 +- .../ColorsDemo.xcdatamodel/contents | 7 +- ...ColorsDemo.MainView.swift => Modern.ColorsDemo.MainView.swift} | 44 ++--- .../⭐️Modern/⭐️ColorsDemo/Modern.ColorsDemo.swift | 1 + .../⭐️Modern.ColorsDemo.SwiftUI.DetailView.swift | 88 +++++----- .../⭐️Modern.ColorsDemo.SwiftUI.ItemView.swift | 14 +- .../⭐️Modern.ColorsDemo.SwiftUI.ListView.swift | 27 +-- .../⭐️Modern.ColorsDemo.UIKit.ListViewController.swift | 12 +- .../⭐️PokedexDemo/Modern.PokedexDemo.MainView.swift | 36 ++-- ...ItemCell.swift => Modern.PokedexDemo.UIKit.ItemCell.swift} | 7 +- ...ListView.swift => Modern.PokedexDemo.UIKit.ListView.swift} | 26 ++- ...wift => Modern.PokedexDemo.UIKit.ListViewController.swift} | 12 +- .../⭐️PokedexDemo/Modern.PokedexDemo.UIKit.swift | 13 ++ Sources/CoreDataNativeType.swift | 2 +- Sources/ForEach+SwiftUI.swift | 56 ++++++ Sources/ForEachSection.swift | 59 +++++++ Sources/ListPublisher.swift | 8 + Sources/ListReader.swift | 78 +++++++++ Sources/LiveList.swift | 162 ++++++++++++++++++ Sources/LiveObject.swift | 110 ++++++++++++ Sources/ObjectPublisher.swift | 5 + Sources/ObjectReader.swift | 93 ++++------ Sources/SectionsReader.swift | 65 +++++++ 27 files changed, 853 insertions(+), 227 deletions(-) rename Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️ColorsDemo/{⭐️Modern.ColorsDemo.MainView.swift => Modern.ColorsDemo.MainView.swift} (79%) rename Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️PokedexDemo/{Modern.PokedexDemo.ItemCell.swift => Modern.PokedexDemo.UIKit.ItemCell.swift} (98%) rename Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️PokedexDemo/{Modern.PokedexDemo.ListView.swift => Modern.PokedexDemo.UIKit.ListView.swift} (68%) rename Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️PokedexDemo/{Modern.PokedexDemo.ListViewController.swift => Modern.PokedexDemo.UIKit.ListViewController.swift} (89%) create mode 100644 Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️PokedexDemo/Modern.PokedexDemo.UIKit.swift create mode 100644 Sources/ForEach+SwiftUI.swift create mode 100644 Sources/ForEachSection.swift create mode 100644 Sources/ListReader.swift create mode 100644 Sources/LiveList.swift create mode 100644 Sources/LiveObject.swift create mode 100644 Sources/SectionsReader.swift diff --git a/CoreStore.xcodeproj/project.pbxproj b/CoreStore.xcodeproj/project.pbxproj index f8d3636..c5668b2 100644 --- a/CoreStore.xcodeproj/project.pbxproj +++ b/CoreStore.xcodeproj/project.pbxproj @@ -734,6 +734,30 @@ B5BF7FCC234D80910070E741 /* Internals.LazyNonmutating.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FCA234D80910070E741 /* Internals.LazyNonmutating.swift */; }; B5BF7FCD234D80910070E741 /* Internals.LazyNonmutating.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FCA234D80910070E741 /* Internals.LazyNonmutating.swift */; }; B5BF7FCE234D80910070E741 /* Internals.LazyNonmutating.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF7FCA234D80910070E741 /* Internals.LazyNonmutating.swift */; }; + B5C7958F25D7D18000BDACC1 /* LiveList.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C7958E25D7D18000BDACC1 /* LiveList.swift */; }; + B5C7959025D7D18000BDACC1 /* LiveList.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C7958E25D7D18000BDACC1 /* LiveList.swift */; }; + B5C7959125D7D18000BDACC1 /* LiveList.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C7958E25D7D18000BDACC1 /* LiveList.swift */; }; + B5C7959225D7D18000BDACC1 /* LiveList.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C7958E25D7D18000BDACC1 /* LiveList.swift */; }; + B5C7959425D7D18700BDACC1 /* LiveObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C7959325D7D18700BDACC1 /* LiveObject.swift */; }; + B5C7959525D7D18700BDACC1 /* LiveObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C7959325D7D18700BDACC1 /* LiveObject.swift */; }; + B5C7959625D7D18700BDACC1 /* LiveObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C7959325D7D18700BDACC1 /* LiveObject.swift */; }; + B5C7959725D7D18700BDACC1 /* LiveObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C7959325D7D18700BDACC1 /* LiveObject.swift */; }; + B5C7959925D7D8B300BDACC1 /* ListReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C7959825D7D8B300BDACC1 /* ListReader.swift */; }; + B5C7959A25D7D8B300BDACC1 /* ListReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C7959825D7D8B300BDACC1 /* ListReader.swift */; }; + B5C7959B25D7D8B300BDACC1 /* ListReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C7959825D7D8B300BDACC1 /* ListReader.swift */; }; + B5C7959C25D7D8B300BDACC1 /* ListReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C7959825D7D8B300BDACC1 /* ListReader.swift */; }; + B5C795A125D7EB2200BDACC1 /* ForEach+SwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C795A025D7EB2200BDACC1 /* ForEach+SwiftUI.swift */; }; + B5C795A225D7EB2200BDACC1 /* ForEach+SwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C795A025D7EB2200BDACC1 /* ForEach+SwiftUI.swift */; }; + B5C795A325D7EB2200BDACC1 /* ForEach+SwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C795A025D7EB2200BDACC1 /* ForEach+SwiftUI.swift */; }; + B5C795A425D7EB2200BDACC1 /* ForEach+SwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C795A025D7EB2200BDACC1 /* ForEach+SwiftUI.swift */; }; + B5C795AB25D7ED8600BDACC1 /* SectionsReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C795AA25D7ED8600BDACC1 /* SectionsReader.swift */; }; + B5C795AC25D7ED8600BDACC1 /* SectionsReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C795AA25D7ED8600BDACC1 /* SectionsReader.swift */; }; + B5C795AD25D7ED8600BDACC1 /* SectionsReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C795AA25D7ED8600BDACC1 /* SectionsReader.swift */; }; + B5C795AE25D7ED8600BDACC1 /* SectionsReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C795AA25D7ED8600BDACC1 /* SectionsReader.swift */; }; + B5C795B025D7EF8600BDACC1 /* ForEachSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C795AF25D7EF8600BDACC1 /* ForEachSection.swift */; }; + B5C795B125D7EF8600BDACC1 /* ForEachSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C795AF25D7EF8600BDACC1 /* ForEachSection.swift */; }; + B5C795B225D7EF8600BDACC1 /* ForEachSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C795AF25D7EF8600BDACC1 /* ForEachSection.swift */; }; + B5C795B325D7EF8600BDACC1 /* ForEachSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C795AF25D7EF8600BDACC1 /* ForEachSection.swift */; }; B5C976E31C6C9F6A00B1AF90 /* UnsafeDataTransaction+Observing.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C976E21C6C9F6A00B1AF90 /* UnsafeDataTransaction+Observing.swift */; }; B5C976E41C6C9F9A00B1AF90 /* UnsafeDataTransaction+Observing.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C976E21C6C9F6A00B1AF90 /* UnsafeDataTransaction+Observing.swift */; }; B5C976E51C6C9F9B00B1AF90 /* UnsafeDataTransaction+Observing.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C976E21C6C9F6A00B1AF90 /* UnsafeDataTransaction+Observing.swift */; }; @@ -1168,6 +1192,12 @@ 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 = ""; }; + B5C7958E25D7D18000BDACC1 /* LiveList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveList.swift; sourceTree = ""; }; + B5C7959325D7D18700BDACC1 /* LiveObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveObject.swift; sourceTree = ""; }; + 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 = ""; }; + B5C795AA25D7ED8600BDACC1 /* SectionsReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionsReader.swift; sourceTree = ""; }; + B5C795AF25D7EF8600BDACC1 /* ForEachSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForEachSection.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 = ""; }; B5CA2B071F7E5ACA004B1936 /* WhereClauseType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WhereClauseType.swift; sourceTree = ""; }; @@ -1543,7 +1573,9 @@ B52FEC722596DB6400368BFB /* SwiftUI */ = { isa = PBXGroup; children = ( - B52FEC732596DBE000368BFB /* ObjectReader.swift */, + B5C7959F25D7EB1200BDACC1 /* Extensions */, + B5C7959E25D7E8B100BDACC1 /* Views */, + B5C7959D25D7E89B00BDACC1 /* PropertyWrappers */, ); name = SwiftUI; sourceTree = ""; @@ -1710,6 +1742,7 @@ B5D1E22B19FA9FBC003B2874 /* CoreStoreError.swift */, B549F6721E56A92800FBAB2D /* CoreDataNativeType.swift */, B5D339F01E94AF5800C880DE /* CoreStoreStrings.swift */, + B5C795BE25D933C200BDACC1 /* Reactive Programming */, B52FEC722596DB6400368BFB /* SwiftUI */, B5E84EDA1AFF84500064E85B /* Setup */, B51B5C2922D43854009FA3BA /* KeyPaths */, @@ -1744,6 +1777,41 @@ name = TestEntities; sourceTree = ""; }; + B5C7959D25D7E89B00BDACC1 /* PropertyWrappers */ = { + isa = PBXGroup; + children = ( + B5C7958E25D7D18000BDACC1 /* LiveList.swift */, + B5C7959325D7D18700BDACC1 /* LiveObject.swift */, + ); + name = PropertyWrappers; + sourceTree = ""; + }; + B5C7959E25D7E8B100BDACC1 /* Views */ = { + isa = PBXGroup; + children = ( + B52FEC732596DBE000368BFB /* ObjectReader.swift */, + B5C7959825D7D8B300BDACC1 /* ListReader.swift */, + B5C795AA25D7ED8600BDACC1 /* SectionsReader.swift */, + B5C795AF25D7EF8600BDACC1 /* ForEachSection.swift */, + ); + name = Views; + sourceTree = ""; + }; + B5C7959F25D7EB1200BDACC1 /* Extensions */ = { + isa = PBXGroup; + children = ( + B5C795A025D7EB2200BDACC1 /* ForEach+SwiftUI.swift */, + ); + name = Extensions; + sourceTree = ""; + }; + B5C795BE25D933C200BDACC1 /* Reactive Programming */ = { + isa = PBXGroup; + children = ( + ); + name = "Reactive Programming"; + sourceTree = ""; + }; B5DBE2CB1C99148100B5CEFA /* ObjectiveC */ = { isa = PBXGroup; children = ( @@ -2314,6 +2382,7 @@ B596BBBB1DD5C39F001DCDD9 /* QueryableSource.swift in Sources */, B55BB4D4235012AE00C33E34 /* ObjectRepresentation.swift in Sources */, B5ECDBFF1CA80CBA00C7F112 /* CSWhere.swift in Sources */, + B5C795AB25D7ED8600BDACC1 /* SectionsReader.swift in Sources */, B5ECDC051CA8138100C7F112 /* CSOrderBy.swift in Sources */, B5E1B5981CAA0C23007FD580 /* CSObjectObserver.swift in Sources */, B5DAFB4A2203E01D003FCCD0 /* KeyPathGenericBindings.swift in Sources */, @@ -2368,6 +2437,7 @@ B546F9731C9C553300D5AC55 /* SetupResult.swift in Sources */, B50C3EFE23D1AB1400B29880 /* FieldCoders.Plist.swift in Sources */, B56E4EDF23CEBCF000E1708C /* FieldOptionalType.swift in Sources */, + B5C7959925D7D8B300BDACC1 /* ListReader.swift in Sources */, B53CA9A21EF1EF1600E0F440 /* PartialObject.swift in Sources */, B56007111B3F6BD500A9A8F9 /* Into.swift in Sources */, B5E84F111AFF847B0064E85B /* Select.swift in Sources */, @@ -2389,6 +2459,7 @@ B5E84F2F1AFF849C0064E85B /* Internals.NotificationObserver.swift in Sources */, B5F1DA8D1B9AA97D007C5CBB /* ImportableObject.swift in Sources */, B56965241B356B820075EE4A /* MigrationResult.swift in Sources */, + B5C7958F25D7D18000BDACC1 /* LiveList.swift in Sources */, B5FE4DAC1C85D44E00FA6A91 /* SQLiteStore.swift in Sources */, B501FDE71CA8D20500BE22EF /* CSListObserver.swift in Sources */, B5E41EC01EA9BB37006240F0 /* DynamicSchema+Convenience.swift in Sources */, @@ -2437,6 +2508,7 @@ B56E4ECF23CD9E4200E1708C /* Field.Stored.swift in Sources */, B52F744A1E9B8740005F3DAC /* CoreStoreSchema.swift in Sources */, B5AEFAB51C9962AE00AD137F /* CoreStoreBridge.swift in Sources */, + B5C795B025D7EF8600BDACC1 /* ForEachSection.swift in Sources */, B5E2222A1CA51B6E00BA2E95 /* CSUnsafeDataTransaction.swift in Sources */, B5E84F391AFF85470064E85B /* NSManagedObjectContext+Querying.swift in Sources */, B56923E81EB827F5007C4DC9 /* InferredSchemaMappingProvider.swift in Sources */, @@ -2462,6 +2534,7 @@ B538BA771D15B3E30003A766 /* CoreStoreBridge.m in Sources */, B51B5C2B22D43931009FA3BA /* String+KeyPaths.swift in Sources */, B512607F1E97A18000402229 /* CoreStoreObject+Convenience.swift in Sources */, + B5C7959425D7D18700BDACC1 /* LiveObject.swift in Sources */, B509D7CE23C8492800F42824 /* Relationship.ToManyUnordered.swift in Sources */, B5E84EF81AFF846E0064E85B /* CoreStore+Transaction.swift in Sources */, B5E84F301AFF849C0064E85B /* NSManagedObjectContext+CoreStore.swift in Sources */, @@ -2481,6 +2554,7 @@ B53D9E5923513712000F48FB /* DiffableDataSourceSnapshotProtocol.swift in Sources */, B56E4ED923CEB8E700E1708C /* FieldStorableType.swift in Sources */, B5474D152227C08700B21FEC /* Internals.CoreStoreFetchRequest.swift in Sources */, + B5C795A125D7EB2200BDACC1 /* ForEach+SwiftUI.swift in Sources */, B56923FF1EB82976007C4DC9 /* CSUnsafeDataModelSchema.swift in Sources */, B5215CAE1FA4812500139E3A /* SectionMonitorBuilder.swift in Sources */, B5ECDBEC1CA6BF2000C7F112 /* CSFrom.swift in Sources */, @@ -2567,6 +2641,7 @@ B596BBBC1DD5C39F001DCDD9 /* QueryableSource.swift in Sources */, B5ECDC011CA80CBA00C7F112 /* CSWhere.swift in Sources */, B55BB4D5235012AE00C33E34 /* ObjectRepresentation.swift in Sources */, + B5C795AC25D7ED8600BDACC1 /* SectionsReader.swift in Sources */, B5ECDC071CA8138100C7F112 /* CSOrderBy.swift in Sources */, B5E1B59A1CAA0C23007FD580 /* CSObjectObserver.swift in Sources */, B5519A601CA21954002BEF78 /* CSAsynchronousDataTransaction.swift in Sources */, @@ -2621,6 +2696,7 @@ B5FE4DA81C84FB4400FA6A91 /* InMemoryStore.swift in Sources */, B50C3EFF23D1AB1400B29880 /* FieldCoders.Plist.swift in Sources */, B56E4EE023CEBCF000E1708C /* FieldOptionalType.swift in Sources */, + B5C7959A25D7D8B300BDACC1 /* ListReader.swift in Sources */, B5F8496D234898240029D57B /* ListSnapshot.swift in Sources */, B53FBA001CAB2D2F00F0D40A /* CSMigrationResult.swift in Sources */, B5DBE2D31C991B3E00B5CEFA /* CSDataStack.swift in Sources */, @@ -2642,6 +2718,7 @@ B501FDE41CA8D1F500BE22EF /* CSListMonitor.swift in Sources */, B5A1DAC91F111BFA003CF369 /* KeyPath+Querying.swift in Sources */, B5FE4DA31C8481E100FA6A91 /* StorageInterface.swift in Sources */, + B5C7959025D7D18000BDACC1 /* LiveList.swift in Sources */, B5ECDC131CA816E500C7F112 /* CSTweak.swift in Sources */, B56923C51EB823B4007C4DC9 /* NSEntityDescription+Migration.swift in Sources */, 82BA18C91C4BBD5900A0916E /* MigrationType.swift in Sources */, @@ -2690,6 +2767,7 @@ 82BA18D81C4BBD7100A0916E /* Internals.WeakObject.swift in Sources */, B56923E91EB827F5007C4DC9 /* InferredSchemaMappingProvider.swift in Sources */, B53B27601EE3B92E00E9B352 /* CoreStoreManagedObject.swift in Sources */, + B5C795B125D7EF8600BDACC1 /* ForEachSection.swift in Sources */, B509D7BD23C8480A00F42824 /* Value.Required.swift in Sources */, B5215CA51FA47DFD00139E3A /* FetchChainBuilder.swift in Sources */, B5D33A021E96012400C880DE /* Relationship.swift in Sources */, @@ -2715,6 +2793,7 @@ B546F96A1C9AF26D00D5AC55 /* CSInMemoryStore.swift in Sources */, B5277673234F1AEB0056BE9F /* NSManagedObjectContext+Logging.swift in Sources */, B509D7D923C84E2600F42824 /* Transformable.Optional.swift in Sources */, + B5C7959525D7D18700BDACC1 /* LiveObject.swift in Sources */, B5831B7B1F34ACBA00A9F647 /* Transformable.swift in Sources */, 82BA18A81C4BBD2900A0916E /* CoreStoreLogger.swift in Sources */, B549F65F1E569C7400FBAB2D /* QueryableAttributeType.swift in Sources */, @@ -2734,6 +2813,7 @@ B56924001EB82976007C4DC9 /* CSUnsafeDataModelSchema.swift in Sources */, B56E4EDA23CEB8E700E1708C /* FieldStorableType.swift in Sources */, B5215CAF1FA4812500139E3A /* SectionMonitorBuilder.swift in Sources */, + B5C795A225D7EB2200BDACC1 /* ForEach+SwiftUI.swift in Sources */, 82BA18D61C4BBD7100A0916E /* NSManagedObjectContext+Transaction.swift in Sources */, B56923ED1EB827F6007C4DC9 /* SchemaMappingProvider.swift in Sources */, 82BA18B91C4BBD4A00A0916E /* From.swift in Sources */, @@ -2820,6 +2900,7 @@ B56923FD1EB82956007C4DC9 /* CSXcodeDataModelSchema.swift in Sources */, B55514ED1EED8BF900BAB888 /* From+Querying.swift in Sources */, B5BF7FC4234D7B2E0070E741 /* ObjectPublisher.swift in Sources */, + B5C795AE25D7ED8600BDACC1 /* SectionsReader.swift in Sources */, B596BBBE1DD5C39F001DCDD9 /* QueryableSource.swift in Sources */, B546F9601C9A12B800D5AC55 /* CSSQliteStore.swift in Sources */, B55BB4D7235012AE00C33E34 /* ObjectRepresentation.swift in Sources */, @@ -2874,6 +2955,7 @@ B53CA9A51EF1EF1600E0F440 /* PartialObject.swift in Sources */, B50C3F0123D1AB1400B29880 /* FieldCoders.Plist.swift in Sources */, B56E4EE223CEBCF000E1708C /* FieldOptionalType.swift in Sources */, + B5C7959C25D7D8B300BDACC1 /* ListReader.swift in Sources */, B52DD1AD1BE1F93900949AFE /* Where.swift in Sources */, B53FBA1C1CAB63E200F0D40A /* NSManagedObject+ObjectiveC.swift in Sources */, B5F8496F234898240029D57B /* ListSnapshot.swift in Sources */, @@ -2895,6 +2977,7 @@ B52DD19D1BE1F92C00949AFE /* BaseDataTransaction.swift in Sources */, B5220E131D1305ED009BC71E /* SectionBy.swift in Sources */, B559CD4D1CAA8C6D00E4D58B /* CSStorageInterface.swift in Sources */, + B5C7959225D7D18000BDACC1 /* LiveList.swift in Sources */, B5E41EC31EA9BB37006240F0 /* DynamicSchema+Convenience.swift in Sources */, B5ECDBE91CA6BEA300C7F112 /* CSClauseTypes.swift in Sources */, B5A1DACB1F111BFA003CF369 /* KeyPath+Querying.swift in Sources */, @@ -2943,6 +3026,7 @@ B56923EB1EB827F5007C4DC9 /* InferredSchemaMappingProvider.swift in Sources */, B50E175023517C03004F033C /* Internals.DiffableDataUIDispatcher.StagedChangeset.swift in Sources */, B53B27621EE3B92E00E9B352 /* CoreStoreManagedObject.swift in Sources */, + B5C795B325D7EF8600BDACC1 /* ForEachSection.swift in Sources */, B509D7C123C8480B00F42824 /* Value.Required.swift in Sources */, B5215CA71FA47DFD00139E3A /* FetchChainBuilder.swift in Sources */, B5D33A041E96012400C880DE /* Relationship.swift in Sources */, @@ -2968,6 +3052,7 @@ B5ECDC331CA81CDC00C7F112 /* CSCoreStore+Transaction.swift in Sources */, B52DD1BB1BE1F94000949AFE /* MigrationType.swift in Sources */, B509D7DB23C84E2600F42824 /* Transformable.Optional.swift in Sources */, + B5C7959725D7D18700BDACC1 /* LiveObject.swift in Sources */, B5831B7D1F34ACBA00A9F647 /* Transformable.swift in Sources */, B5277675234F1AEB0056BE9F /* NSManagedObjectContext+Logging.swift in Sources */, B52DD1C91BE1F94600949AFE /* NSManagedObjectContext+Transaction.swift in Sources */, @@ -2987,6 +3072,7 @@ B501322E2346A9B100FC238B /* ListPublisher.swift in Sources */, B56E4EDC23CEB8E700E1708C /* FieldStorableType.swift in Sources */, B56924021EB82976007C4DC9 /* CSUnsafeDataModelSchema.swift in Sources */, + B5C795A425D7EB2200BDACC1 /* ForEach+SwiftUI.swift in Sources */, B5215CB11FA4812500139E3A /* SectionMonitorBuilder.swift in Sources */, B5220E171D1306DF009BC71E /* UnsafeDataTransaction+Observing.swift in Sources */, B56923EF1EB827F6007C4DC9 /* SchemaMappingProvider.swift in Sources */, @@ -3073,6 +3159,7 @@ B596BBBD1DD5C39F001DCDD9 /* QueryableSource.swift in Sources */, B5ECDC081CA8138100C7F112 /* CSOrderBy.swift in Sources */, B55BB4D6235012AE00C33E34 /* ObjectRepresentation.swift in Sources */, + B5C795AD25D7ED8600BDACC1 /* SectionsReader.swift in Sources */, B5E1B59B1CAA0C23007FD580 /* CSObjectObserver.swift in Sources */, B5519A611CA21954002BEF78 /* CSAsynchronousDataTransaction.swift in Sources */, B5FE4DAE1C85D44E00FA6A91 /* SQLiteStore.swift in Sources */, @@ -3127,6 +3214,7 @@ B5831F442212700500D8604C /* Where.Expression.swift in Sources */, B50C3F0023D1AB1400B29880 /* FieldCoders.Plist.swift in Sources */, B56E4EE123CEBCF000E1708C /* FieldOptionalType.swift in Sources */, + B5C7959B25D7D8B300BDACC1 /* ListReader.swift in Sources */, B5F8496E234898240029D57B /* ListSnapshot.swift in Sources */, B51260951E9B28F100402229 /* Internals.EntityIdentifier.swift in Sources */, B53FBA011CAB2D2F00F0D40A /* CSMigrationResult.swift in Sources */, @@ -3148,6 +3236,7 @@ B501FDE51CA8D1F500BE22EF /* CSListMonitor.swift in Sources */, B5E41EC21EA9BB37006240F0 /* DynamicSchema+Convenience.swift in Sources */, B5ECDC141CA816E500C7F112 /* CSTweak.swift in Sources */, + B5C7959125D7D18000BDACC1 /* LiveList.swift in Sources */, B5A1DACA1F111BFA003CF369 /* KeyPath+Querying.swift in Sources */, B56321AE1BD6521C006C9394 /* Internals.NotificationObserver.swift in Sources */, B56321931BD65216006C9394 /* DataStack+Querying.swift in Sources */, @@ -3196,6 +3285,7 @@ B563219F1BD65216006C9394 /* ObjectMonitor.swift in Sources */, B56321B61BD6521C006C9394 /* Internals.WeakObject.swift in Sources */, B56923EA1EB827F5007C4DC9 /* InferredSchemaMappingProvider.swift in Sources */, + B5C795B225D7EF8600BDACC1 /* ForEachSection.swift in Sources */, B509D7BF23C8480B00F42824 /* Value.Required.swift in Sources */, B53B27611EE3B92E00E9B352 /* CoreStoreManagedObject.swift in Sources */, B5215CA61FA47DFD00139E3A /* FetchChainBuilder.swift in Sources */, @@ -3221,6 +3311,7 @@ B546F96B1C9AF26D00D5AC55 /* CSInMemoryStore.swift in Sources */, B5277674234F1AEB0056BE9F /* NSManagedObjectContext+Logging.swift in Sources */, B509D7DA23C84E2600F42824 /* Transformable.Optional.swift in Sources */, + B5C7959625D7D18700BDACC1 /* LiveObject.swift in Sources */, B5831B7C1F34ACBA00A9F647 /* Transformable.swift in Sources */, B563218B1BD65216006C9394 /* UnsafeDataTransaction.swift in Sources */, B549F6601E569C7400FBAB2D /* QueryableAttributeType.swift in Sources */, @@ -3240,6 +3331,7 @@ B56924011EB82976007C4DC9 /* CSUnsafeDataModelSchema.swift in Sources */, B56E4EDB23CEB8E700E1708C /* FieldStorableType.swift in Sources */, B5215CB01FA4812500139E3A /* SectionMonitorBuilder.swift in Sources */, + B5C795A325D7EB2200BDACC1 /* ForEach+SwiftUI.swift in Sources */, B56321B41BD6521C006C9394 /* NSManagedObjectContext+Transaction.swift in Sources */, B56923EE1EB827F6007C4DC9 /* SchemaMappingProvider.swift in Sources */, B56321861BD65216006C9394 /* CoreStoreLogger.swift in Sources */, diff --git a/Demo/Demo.xcodeproj/project.pbxproj b/Demo/Demo.xcodeproj/project.pbxproj index e8143a1..636161f 100644 --- a/Demo/Demo.xcodeproj/project.pbxproj +++ b/Demo/Demo.xcodeproj/project.pbxproj @@ -15,9 +15,9 @@ B54D2F7C251196B6004BEC7D /* Advanced.EvolutionDemo.V2.FromV1MigrationPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = B54D2F7B251196B6004BEC7D /* Advanced.EvolutionDemo.V2.FromV1MigrationPolicy.swift */; }; B54D2F7E25119DCE004BEC7D /* Advanced.EvolutionDemo.V1.FromV2.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = B54D2F7D25119DCE004BEC7D /* Advanced.EvolutionDemo.V1.FromV2.xcmappingmodel */; }; B54D2F852511B70B004BEC7D /* Advanced.EvolutionDemo.CreaturesDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = B54D2F842511B70B004BEC7D /* Advanced.EvolutionDemo.CreaturesDataSource.swift */; }; - B566C8E824F9D406001134A1 /* Modern.PokedexDemo.ListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B566C8E724F9D406001134A1 /* Modern.PokedexDemo.ListView.swift */; }; - B566C8EA24F9D412001134A1 /* Modern.PokedexDemo.ListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B566C8E924F9D412001134A1 /* Modern.PokedexDemo.ListViewController.swift */; }; - B566C8EC24F9D694001134A1 /* Modern.PokedexDemo.ItemCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B566C8EB24F9D694001134A1 /* Modern.PokedexDemo.ItemCell.swift */; }; + B566C8E824F9D406001134A1 /* Modern.PokedexDemo.UIKit.ListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B566C8E724F9D406001134A1 /* Modern.PokedexDemo.UIKit.ListView.swift */; }; + B566C8EA24F9D412001134A1 /* Modern.PokedexDemo.UIKit.ListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B566C8E924F9D412001134A1 /* Modern.PokedexDemo.UIKit.ListViewController.swift */; }; + B566C8EC24F9D694001134A1 /* Modern.PokedexDemo.UIKit.ItemCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B566C8EB24F9D694001134A1 /* Modern.PokedexDemo.UIKit.ItemCell.swift */; }; B566C8EE24FA1EA3001134A1 /* Modern.PokedexDemo.Details.swift in Sources */ = {isa = PBXBuildFile; fileRef = B566C8ED24FA1EA3001134A1 /* Modern.PokedexDemo.Details.swift */; }; B5931B4225124756007DA58E /* Advanced.EvolutionDemo.V1.FromV2.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5931B4125124756007DA58E /* Advanced.EvolutionDemo.V1.FromV2.swift */; }; B5931B442512480A007DA58E /* Advanced.EvolutionDemo.V2.FromV1.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5931B432512480A007DA58E /* Advanced.EvolutionDemo.V2.FromV1.swift */; }; @@ -68,7 +68,7 @@ B5A391A024E8F00A00E7E8BD /* Modern.ColorsDemo.UIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A3919F24E8F00A00E7E8BD /* Modern.ColorsDemo.UIKit.swift */; }; B5A391A224E8F01F00E7E8BD /* ⭐️Modern.ColorsDemo.UIKit.ListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A391A124E8F01F00E7E8BD /* ⭐️Modern.ColorsDemo.UIKit.ListViewController.swift */; }; B5A391A424E8F04300E7E8BD /* Modern.ColorsDemo.UIKit.ListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A391A324E8F04300E7E8BD /* Modern.ColorsDemo.UIKit.ListView.swift */; }; - B5A391A624E8F4EA00E7E8BD /* ⭐️Modern.ColorsDemo.MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A391A524E8F4EA00E7E8BD /* ⭐️Modern.ColorsDemo.MainView.swift */; }; + B5A391A624E8F4EA00E7E8BD /* Modern.ColorsDemo.MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A391A524E8F4EA00E7E8BD /* Modern.ColorsDemo.MainView.swift */; }; B5A391AA24E9104300E7E8BD /* Modern.ColorsDemo.UIKit.ItemCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A391A924E9104300E7E8BD /* Modern.ColorsDemo.UIKit.ItemCell.swift */; }; B5A391AC24E9143B00E7E8BD /* Modern.ColorsDemo.UIKit.DetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A391AB24E9143B00E7E8BD /* Modern.ColorsDemo.UIKit.DetailView.swift */; }; B5A391AE24E9150F00E7E8BD /* ⭐️Modern.ColorsDemo.UIKit.DetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A391AD24E9150F00E7E8BD /* ⭐️Modern.ColorsDemo.UIKit.DetailViewController.swift */; }; @@ -97,6 +97,7 @@ B5A5440B25049492000DC5E3 /* Advanced.EvolutionDemo.V3.Creature.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A5440A25049492000DC5E3 /* Advanced.EvolutionDemo.V3.Creature.swift */; }; B5A5440D2504949C000DC5E3 /* Advanced.EvolutionDemo.V4.Creature.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A5440C2504949C000DC5E3 /* Advanced.EvolutionDemo.V4.Creature.swift */; }; B5C18F3325138700001BEFB3 /* Advanced.EvolutionDemo.ProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C18F3225138700001BEFB3 /* Advanced.EvolutionDemo.ProgressView.swift */; }; + B5C795B625D8EE2B00BDACC1 /* Modern.PokedexDemo.UIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C795B525D8EE2B00BDACC1 /* Modern.PokedexDemo.UIKit.swift */; }; B5D6F1F8250E07FD00DF5D2F /* Advanced.EvolutionDemo.V1.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D6F1F7250E07FD00DF5D2F /* Advanced.EvolutionDemo.V1.swift */; }; B5D6F1FC250E0B1700DF5D2F /* Advanced.EvolutionDemo.V2.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D6F1FB250E0B1700DF5D2F /* Advanced.EvolutionDemo.V2.swift */; }; B5D6F1FE250E0B3F00DF5D2F /* Advanced.EvolutionDemo.GeologicalPeriod.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D6F1FD250E0B3F00DF5D2F /* Advanced.EvolutionDemo.GeologicalPeriod.swift */; }; @@ -133,9 +134,9 @@ B54D2F7B251196B6004BEC7D /* Advanced.EvolutionDemo.V2.FromV1MigrationPolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Advanced.EvolutionDemo.V2.FromV1MigrationPolicy.swift; sourceTree = ""; }; B54D2F7D25119DCE004BEC7D /* Advanced.EvolutionDemo.V1.FromV2.xcmappingmodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcmappingmodel; path = Advanced.EvolutionDemo.V1.FromV2.xcmappingmodel; sourceTree = ""; }; B54D2F842511B70B004BEC7D /* Advanced.EvolutionDemo.CreaturesDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Advanced.EvolutionDemo.CreaturesDataSource.swift; sourceTree = ""; }; - B566C8E724F9D406001134A1 /* Modern.PokedexDemo.ListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.PokedexDemo.ListView.swift; sourceTree = ""; }; - B566C8E924F9D412001134A1 /* Modern.PokedexDemo.ListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.PokedexDemo.ListViewController.swift; sourceTree = ""; }; - B566C8EB24F9D694001134A1 /* Modern.PokedexDemo.ItemCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.PokedexDemo.ItemCell.swift; sourceTree = ""; }; + B566C8E724F9D406001134A1 /* Modern.PokedexDemo.UIKit.ListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.PokedexDemo.UIKit.ListView.swift; sourceTree = ""; }; + B566C8E924F9D412001134A1 /* Modern.PokedexDemo.UIKit.ListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.PokedexDemo.UIKit.ListViewController.swift; sourceTree = ""; }; + B566C8EB24F9D694001134A1 /* Modern.PokedexDemo.UIKit.ItemCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.PokedexDemo.UIKit.ItemCell.swift; sourceTree = ""; }; B566C8ED24FA1EA3001134A1 /* Modern.PokedexDemo.Details.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.PokedexDemo.Details.swift; sourceTree = ""; }; B5931B4125124756007DA58E /* Advanced.EvolutionDemo.V1.FromV2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Advanced.EvolutionDemo.V1.FromV2.swift; sourceTree = ""; }; B5931B432512480A007DA58E /* Advanced.EvolutionDemo.V2.FromV1.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Advanced.EvolutionDemo.V2.FromV1.swift; sourceTree = ""; }; @@ -185,7 +186,7 @@ B5A3919F24E8F00A00E7E8BD /* Modern.ColorsDemo.UIKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.ColorsDemo.UIKit.swift; sourceTree = ""; }; B5A391A124E8F01F00E7E8BD /* ⭐️Modern.ColorsDemo.UIKit.ListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "⭐️Modern.ColorsDemo.UIKit.ListViewController.swift"; sourceTree = ""; }; B5A391A324E8F04300E7E8BD /* Modern.ColorsDemo.UIKit.ListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.ColorsDemo.UIKit.ListView.swift; sourceTree = ""; }; - B5A391A524E8F4EA00E7E8BD /* ⭐️Modern.ColorsDemo.MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "⭐️Modern.ColorsDemo.MainView.swift"; sourceTree = ""; }; + B5A391A524E8F4EA00E7E8BD /* Modern.ColorsDemo.MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.ColorsDemo.MainView.swift; sourceTree = ""; }; B5A391A924E9104300E7E8BD /* Modern.ColorsDemo.UIKit.ItemCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.ColorsDemo.UIKit.ItemCell.swift; sourceTree = ""; }; B5A391AB24E9143B00E7E8BD /* Modern.ColorsDemo.UIKit.DetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.ColorsDemo.UIKit.DetailView.swift; sourceTree = ""; }; B5A391AD24E9150F00E7E8BD /* ⭐️Modern.ColorsDemo.UIKit.DetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "⭐️Modern.ColorsDemo.UIKit.DetailViewController.swift"; sourceTree = ""; }; @@ -214,6 +215,7 @@ B5A5440A25049492000DC5E3 /* Advanced.EvolutionDemo.V3.Creature.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Advanced.EvolutionDemo.V3.Creature.swift; sourceTree = ""; }; B5A5440C2504949C000DC5E3 /* Advanced.EvolutionDemo.V4.Creature.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Advanced.EvolutionDemo.V4.Creature.swift; sourceTree = ""; }; B5C18F3225138700001BEFB3 /* Advanced.EvolutionDemo.ProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Advanced.EvolutionDemo.ProgressView.swift; sourceTree = ""; }; + B5C795B525D8EE2B00BDACC1 /* Modern.PokedexDemo.UIKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.PokedexDemo.UIKit.swift; sourceTree = ""; }; B5D6F1F7250E07FD00DF5D2F /* Advanced.EvolutionDemo.V1.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Advanced.EvolutionDemo.V1.swift; sourceTree = ""; }; B5D6F1FB250E0B1700DF5D2F /* Advanced.EvolutionDemo.V2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Advanced.EvolutionDemo.V2.swift; sourceTree = ""; }; B5D6F1FD250E0B3F00DF5D2F /* Advanced.EvolutionDemo.GeologicalPeriod.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Advanced.EvolutionDemo.GeologicalPeriod.swift; sourceTree = ""; }; @@ -412,7 +414,7 @@ children = ( B5A3918E24E7E06500E7E8BD /* Modern.ColorsDemo.swift */, B5A3919724E7E67000E7E8BD /* Modern.ColorsDemo.Filter.swift */, - B5A391A524E8F4EA00E7E8BD /* ⭐️Modern.ColorsDemo.MainView.swift */, + B5A391A524E8F4EA00E7E8BD /* Modern.ColorsDemo.MainView.swift */, B5A3919C24E8EE9000E7E8BD /* ⭐️UIKit */, B5A3919B24E8EE8100E7E8BD /* ⭐️SwiftUI */, B5A3919024E7E0B000E7E8BD /* Models */, @@ -458,9 +460,7 @@ B5A391B024E96AF600E7E8BD /* Modern.PokedexDemo.swift */, B531EFEA24EB5ECD005F247D /* ⭐️Modern.PokedexDemo.Service.swift */, B531EFEC24EB7453005F247D /* Modern.PokedexDemo.MainView.swift */, - B566C8E724F9D406001134A1 /* Modern.PokedexDemo.ListView.swift */, - B566C8E924F9D412001134A1 /* Modern.PokedexDemo.ListViewController.swift */, - B566C8EB24F9D694001134A1 /* Modern.PokedexDemo.ItemCell.swift */, + B5C795B425D8EE1A00BDACC1 /* ⭐️UIKit */, B5A391B224E96B7400E7E8BD /* ⭐️Models */, ); path = "⭐️PokedexDemo"; @@ -549,6 +549,17 @@ name = Models; sourceTree = ""; }; + B5C795B425D8EE1A00BDACC1 /* ⭐️UIKit */ = { + isa = PBXGroup; + children = ( + B5C795B525D8EE2B00BDACC1 /* Modern.PokedexDemo.UIKit.swift */, + B566C8E724F9D406001134A1 /* Modern.PokedexDemo.UIKit.ListView.swift */, + B566C8E924F9D412001134A1 /* Modern.PokedexDemo.UIKit.ListViewController.swift */, + B566C8EB24F9D694001134A1 /* Modern.PokedexDemo.UIKit.ItemCell.swift */, + ); + name = "⭐️UIKit"; + sourceTree = ""; + }; B5D6F1F9250E08D200DF5D2F /* V1 */ = { isa = PBXGroup; children = ( @@ -704,6 +715,7 @@ B5A543ED24FB84BE000DC5E3 /* Classic.ColorsDemo.MainView.swift in Sources */, B5A543DD24FB78F9000DC5E3 /* Classic.ColorsDemo.Palette.swift in Sources */, B5A3919824E7E67000E7E8BD /* Modern.ColorsDemo.Filter.swift in Sources */, + B5C795B625D8EE2B00BDACC1 /* Modern.PokedexDemo.UIKit.swift in Sources */, B5A3919224E7E0C600E7E8BD /* Modern.ColorsDemo.Palette.swift in Sources */, B5A3919E24E8EEB600E7E8BD /* Modern.ColorsDemo.SwiftUI.swift in Sources */, B5A391A024E8F00A00E7E8BD /* Modern.ColorsDemo.UIKit.swift in Sources */, @@ -711,16 +723,16 @@ B5A3916024E6925900E7E8BD /* Modern.PlacemarksDemo.MapView.swift in Sources */, B5A3916524E698C700E7E8BD /* Modern.PlacemarksDemo.Place.swift in Sources */, B566C8EE24FA1EA3001134A1 /* Modern.PokedexDemo.Details.swift in Sources */, - B566C8EC24F9D694001134A1 /* Modern.PokedexDemo.ItemCell.swift in Sources */, - B566C8E824F9D406001134A1 /* Modern.PokedexDemo.ListView.swift in Sources */, - B566C8EA24F9D412001134A1 /* Modern.PokedexDemo.ListViewController.swift in Sources */, + B566C8EC24F9D694001134A1 /* Modern.PokedexDemo.UIKit.ItemCell.swift in Sources */, + B566C8E824F9D406001134A1 /* Modern.PokedexDemo.UIKit.ListView.swift in Sources */, + B566C8EA24F9D412001134A1 /* Modern.PokedexDemo.UIKit.ListViewController.swift in Sources */, B531EFED24EB7453005F247D /* Modern.PokedexDemo.MainView.swift in Sources */, B5A3918C24E7B44B00E7E8BD /* Modern.TimeZonesDemo.ItemView.swift in Sources */, B5A3918A24E7AD1800E7E8BD /* Modern.TimeZonesDemo.ListView.swift in Sources */, B5A3918624E7A54A00E7E8BD /* Modern.TimeZonesDemo.TimeZone.swift in Sources */, B5A543EB24FB84AF000DC5E3 /* ⭐️Classic.ColorsDemo.DetailViewController.swift in Sources */, B5A543F124FB84DD000DC5E3 /* ⭐️Classic.ColorsDemo.ListViewController.swift in Sources */, - B5A391A624E8F4EA00E7E8BD /* ⭐️Modern.ColorsDemo.MainView.swift in Sources */, + B5A391A624E8F4EA00E7E8BD /* Modern.ColorsDemo.MainView.swift in Sources */, B5A3916224E697BA00E7E8BD /* ⭐️Modern.PlacemarksDemo.MainView.swift in Sources */, B5A391B424E96C0A00E7E8BD /* ⭐️Modern.PokedexDemo.Form.swift in Sources */, B531EFE924EB5A53005F247D /* ⭐️Modern.PokedexDemo.PokedexEntry.swift in Sources */, diff --git a/Demo/⭐️Sources/Helpers/InstructionsView.swift b/Demo/⭐️Sources/Helpers/InstructionsView.swift index c2b1f75..b06ba8f 100644 --- a/Demo/⭐️Sources/Helpers/InstructionsView.swift +++ b/Demo/⭐️Sources/Helpers/InstructionsView.swift @@ -20,8 +20,8 @@ struct InstructionsView: View { var body: some View { ZStack(alignment: .center) { - Color.white - .cornerRadius(10) + RoundedRectangle(cornerRadius: 10, style: .continuous) + .fill(Color.white) .shadow(color: Color(.sRGB, white: 0.5, opacity: 0.3), radius: 2, x: 1, y: 1) VStack(alignment: .leading, spacing: 3) { ForEach(self.rows, id: \.header) { row in diff --git a/Demo/⭐️Sources/Helpers/Menu/Menu.MainView.swift b/Demo/⭐️Sources/Helpers/Menu/Menu.MainView.swift index 5a69d16..36a3129 100644 --- a/Demo/⭐️Sources/Helpers/Menu/Menu.MainView.swift +++ b/Demo/⭐️Sources/Helpers/Menu/Menu.MainView.swift @@ -73,7 +73,9 @@ extension Menu { title: "Pokedex API", subtitle: "Importing JSON data from external source", destination: { - Modern.PokedexDemo.MainView() + Modern.PokedexDemo.MainView( + listView: Modern.PokedexDemo.UIKit.ListView.init + ) } ) } diff --git a/Demo/⭐️Sources/⭐️Demos/⭐️Advanced/⭐️EvolutionDemo/Advanced.EvolutionDemo.ProgressView.swift b/Demo/⭐️Sources/⭐️Demos/⭐️Advanced/⭐️EvolutionDemo/Advanced.EvolutionDemo.ProgressView.swift index 4f3744b..3568cea 100644 --- a/Demo/⭐️Sources/⭐️Demos/⭐️Advanced/⭐️EvolutionDemo/Advanced.EvolutionDemo.ProgressView.swift +++ b/Demo/⭐️Sources/⭐️Demos/⭐️Advanced/⭐️EvolutionDemo/Advanced.EvolutionDemo.ProgressView.swift @@ -45,17 +45,16 @@ extension Advanced.EvolutionDemo { .padding(.horizontal) GeometryReader { geometry in ZStack(alignment: .leading) { - Color.gray - .opacity(0.2) + RoundedRectangle(cornerRadius: 4, style: .continuous) + .fill(Color.gray.opacity(0.2)) .frame(width: geometry.size.width, height: 8) - .cornerRadius(4.0) - Color.blue + RoundedRectangle(cornerRadius: 4, style: .continuous) + .fill(Color.blue) .frame( width: geometry.size.width * self.progressObserver.fractionCompleted, height: 8 ) - .cornerRadius(4.0) } } .fixedSize(horizontal: false, vertical: true) diff --git a/Demo/⭐️Sources/⭐️Demos/⭐️Classic/⭐️ColorsDemo/Classic.ColorsDemo.xcdatamodeld/ColorsDemo.xcdatamodel/contents b/Demo/⭐️Sources/⭐️Demos/⭐️Classic/⭐️ColorsDemo/Classic.ColorsDemo.xcdatamodeld/ColorsDemo.xcdatamodel/contents index e3e0f30..c3c29f3 100644 --- a/Demo/⭐️Sources/⭐️Demos/⭐️Classic/⭐️ColorsDemo/Classic.ColorsDemo.xcdatamodeld/ColorsDemo.xcdatamodel/contents +++ b/Demo/⭐️Sources/⭐️Demos/⭐️Classic/⭐️ColorsDemo/Classic.ColorsDemo.xcdatamodeld/ColorsDemo.xcdatamodel/contents @@ -1,12 +1,15 @@ - + + + + - + \ No newline at end of file diff --git a/Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️ColorsDemo/⭐️Modern.ColorsDemo.MainView.swift b/Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️ColorsDemo/Modern.ColorsDemo.MainView.swift similarity index 79% rename from Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️ColorsDemo/⭐️Modern.ColorsDemo.MainView.swift rename to Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️ColorsDemo/Modern.ColorsDemo.MainView.swift index bad5ee7..3bfeb9b 100644 --- a/Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️ColorsDemo/⭐️Modern.ColorsDemo.MainView.swift +++ b/Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️ColorsDemo/Modern.ColorsDemo.MainView.swift @@ -13,12 +13,6 @@ extension Modern.ColorsDemo { struct MainView: View { - /** - ⭐️ Sample 1: Setting a sectioned `ListPublisher` declared as an `@ObservedObject` - */ - @ObservedObject - private var listPublisher: ListPublisher - // MARK: Internal init( @@ -30,38 +24,24 @@ extension Modern.ColorsDemo { self.listView = listView self.detailView = detailView - self.listPublisher = Modern.ColorsDemo.palettesPublisher - self._filter = Binding( - get: { Modern.ColorsDemo.filter }, - set: { Modern.ColorsDemo.filter = $0 } - ) } // MARK: View var body: some View { - let detailView: AnyView - if let selectedPalette = self.selectedPalette { - - detailView = AnyView( - self.detailView(selectedPalette) - ) - } - else { - - detailView = AnyView(EmptyView()) - } - let listPublisher = self.listPublisher return VStack(spacing: 0) { - self.listView(listPublisher, { self.selectedPalette = $0 }) + self.listView(self.$palettes, { self.selectedPalette = $0 }) .navigationBarTitle( - Text("Colors (\(listPublisher.snapshot.numberOfItems) objects)") + Text("Colors (\(self.palettes.count) objects)") ) .frame(minHeight: 0, maxHeight: .infinity) - detailView - .edgesIgnoringSafeArea(.all) - .frame(minHeight: 0, maxHeight: .infinity) + self.selectedPalette.map { + + self.detailView($0) + .edgesIgnoringSafeArea(.all) + .frame(minHeight: 0, maxHeight: .infinity) + } } .navigationBarItems( leading: HStack { @@ -91,6 +71,9 @@ extension Modern.ColorsDemo { // MARK: Private + @LiveList(Modern.ColorsDemo.palettesPublisher) + private var palettes: LiveList.Items + private let listView: ( _ listPublisher: ListPublisher, _ onPaletteTapped: @escaping (ObjectPublisher) -> Void @@ -103,12 +86,13 @@ extension Modern.ColorsDemo { @State private var selectedPalette: ObjectPublisher? - @Binding - private var filter: Modern.ColorsDemo.Filter + @State + private var filter: Modern.ColorsDemo.Filter = Modern.ColorsDemo.filter private func changeFilter() { Modern.ColorsDemo.filter = Modern.ColorsDemo.filter.next() + self.filter = Modern.ColorsDemo.filter } private func clearColors() { diff --git a/Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️ColorsDemo/Modern.ColorsDemo.swift b/Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️ColorsDemo/Modern.ColorsDemo.swift index 03b0e8c..31ad4b3 100644 --- a/Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️ColorsDemo/Modern.ColorsDemo.swift +++ b/Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️ColorsDemo/Modern.ColorsDemo.swift @@ -2,6 +2,7 @@ // Demo // Copyright © 2020 John Rommel Estropia, Inc. All rights reserved. +import SwiftUI import CoreStore // MARK: - Modern diff --git a/Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️ColorsDemo/⭐️Modern.ColorsDemo.SwiftUI.DetailView.swift b/Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️ColorsDemo/⭐️Modern.ColorsDemo.SwiftUI.DetailView.swift index a368896..e0fe2dc 100644 --- a/Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️ColorsDemo/⭐️Modern.ColorsDemo.SwiftUI.DetailView.swift +++ b/Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️ColorsDemo/⭐️Modern.ColorsDemo.SwiftUI.DetailView.swift @@ -17,8 +17,8 @@ extension Modern.ColorsDemo.SwiftUI { /** ⭐️ Sample 1: Setting an `ObjectPublisher` declared as an `@ObservedObject` */ - @ObservedObject - private var palette: ObjectPublisher + @LiveObject + private var palette: LiveObject.Item? /** ⭐️ Sample 2: Setting properties that can be binded to controls (`Slider` in this case) by creating custom `@Binding` instances that updates the store when the values change. @@ -34,7 +34,7 @@ extension Modern.ColorsDemo.SwiftUI { init(_ palette: ObjectPublisher) { - self.palette = palette + self._palette = .init(palette) self._hue = Binding( get: { palette.hue ?? 0 }, set: { percentage in @@ -84,56 +84,50 @@ extension Modern.ColorsDemo.SwiftUI { var body: some View { - guard let snapshot = self.palette.snapshot else { + if let palette = self.palette { - return AnyView(EmptyView()) - } - return AnyView( - GeometryReader { geometry in - ZStack(alignment: .bottom) { - Color(snapshot.$color) - ZStack { - Color.white - .cornerRadius(10) - .shadow(color: Color(.sRGB, white: 0.5, opacity: 0.3), radius: 2, x: 1, y: 1) - VStack(alignment: .leading, spacing: 10) { - HStack { - Text("H: \(Int(snapshot.$hue * 359))°") - .frame(width: 80) - Slider( - value: self.$hue, - in: 0 ... 1, - step: 1 / 359 - ) - } - HStack { - Text("S: \(Int(snapshot.$saturation * 100))%") - .frame(width: 80) - Slider( - value: self.$saturation, - in: 0 ... 1, - step: 1 / 100 - ) - } - HStack { - Text("B: \(Int(snapshot.$brightness * 100))%") - .frame(width: 80) - Slider( - value: self.$brightness, - in: 0 ... 1, - step: 1 / 100 - ) - } + ZStack(alignment: .center) { + Color(palette.$color) + ZStack { + RoundedRectangle(cornerRadius: 10, style: .continuous) + .fill(Color.white) + .shadow(color: Color(.sRGB, white: 0.5, opacity: 0.3), radius: 2, x: 1, y: 1) + VStack(alignment: .leading, spacing: 10) { + HStack { + Text("H: \(Int(palette.$hue * 359))°") + .frame(width: 80) + Slider( + value: self.$hue, + in: 0 ... 1, + step: 1 / 359 + ) + } + HStack { + Text("S: \(Int(palette.$saturation * 100))%") + .frame(width: 80) + Slider( + value: self.$saturation, + in: 0 ... 1, + step: 1 / 100 + ) + } + HStack { + Text("B: \(Int(palette.$brightness * 100))%") + .frame(width: 80) + Slider( + value: self.$brightness, + in: 0 ... 1, + step: 1 / 100 + ) } - .foregroundColor(Color(.sRGB, white: 0, opacity: 0.8)) - .padding() } - .fixedSize(horizontal: false, vertical: true) + .foregroundColor(Color(.sRGB, white: 0, opacity: 0.8)) .padding() - .padding(geometry.safeAreaInsets) } + .fixedSize(horizontal: false, vertical: true) + .padding() } - ) + } } } } diff --git a/Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️ColorsDemo/⭐️Modern.ColorsDemo.SwiftUI.ItemView.swift b/Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️ColorsDemo/⭐️Modern.ColorsDemo.SwiftUI.ItemView.swift index 1ea723d..3a64513 100644 --- a/Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️ColorsDemo/⭐️Modern.ColorsDemo.SwiftUI.ItemView.swift +++ b/Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️ColorsDemo/⭐️Modern.ColorsDemo.SwiftUI.ItemView.swift @@ -16,15 +16,15 @@ extension Modern.ColorsDemo.SwiftUI { /** ⭐️ Sample 1: Setting an `ObjectPublisher` declared as an `@ObservedObject` */ - @ObservedObject - private var palette: ObjectPublisher + @LiveObject + private var palette: LiveObject.Item? // MARK: Internal internal init(_ palette: ObjectPublisher) { - self.palette = palette + self._palette = .init(palette) } @@ -32,18 +32,16 @@ extension Modern.ColorsDemo.SwiftUI { var body: some View { - guard let palette = self.palette.snapshot else { + if let palette = self.palette { - return AnyView(EmptyView()) - } - return AnyView( Color(palette.$color).overlay( Text(palette.$colorText) .foregroundColor(palette.$brightness > 0.6 ? .black : .white) .padding(), alignment: .leading ) - ) + .animation(.default) + } } } } diff --git a/Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️ColorsDemo/⭐️Modern.ColorsDemo.SwiftUI.ListView.swift b/Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️ColorsDemo/⭐️Modern.ColorsDemo.SwiftUI.ListView.swift index 53c02d4..884ea46 100644 --- a/Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️ColorsDemo/⭐️Modern.ColorsDemo.SwiftUI.ListView.swift +++ b/Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️ColorsDemo/⭐️Modern.ColorsDemo.SwiftUI.ListView.swift @@ -5,7 +5,6 @@ import CoreStore import SwiftUI - // MARK: - Modern.ColorsDemo.SwiftUI extension Modern.ColorsDemo.SwiftUI { @@ -17,39 +16,43 @@ extension Modern.ColorsDemo.SwiftUI { /** ⭐️ Sample 1: Setting a sectioned `ListPublisher` declared as an `@ObservedObject` */ - @ObservedObject - private var listPublisher: ListPublisher + @LiveList + private var palettes: LiveList.Items /** ⭐️ Sample 2: Assigning sections and items of the `ListPublisher` to corresponding `View`s */ var body: some View { - let listSnapshot = self.listPublisher.snapshot return List { - ForEach(listSnapshot.sectionIDs, id: \.self) { (sectionID) in + + ForEachSection(in: self.palettes) { sectionID, palettes in + Section(header: Text(sectionID)) { - ForEach(listSnapshot.items(inSectionWithID: sectionID), id: \.self) { palette in + + ForEach(palettes) { palette in + Button( action: { + self.onPaletteTapped(palette) }, label: { + Modern.ColorsDemo.SwiftUI.ItemView(palette) } ) .listRowInsets(.init()) } .onDelete { itemIndices in - + self.deleteColors(at: itemIndices, in: sectionID) } } } - GeometryReader { geometry in - Spacer(minLength: geometry.safeAreaInsets.bottom) - } } + .animation(.default) .listStyle(PlainListStyle()) + .edgesIgnoringSafeArea([]) } @@ -60,7 +63,7 @@ extension Modern.ColorsDemo.SwiftUI { onPaletteTapped: @escaping (ObjectPublisher) -> Void ) { - self.listPublisher = listPublisher + self._palettes = .init(listPublisher) self.onPaletteTapped = onPaletteTapped } @@ -71,7 +74,7 @@ extension Modern.ColorsDemo.SwiftUI { private func deleteColors(at indices: IndexSet, in sectionID: String) { - let objectIDsToDelete = self.listPublisher.snapshot.itemIDs( + let objectIDsToDelete = self.palettes.itemIDs( inSectionWithID: sectionID, atIndices: indices ) diff --git a/Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️ColorsDemo/⭐️Modern.ColorsDemo.UIKit.ListViewController.swift b/Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️ColorsDemo/⭐️Modern.ColorsDemo.UIKit.ListViewController.swift index fdac31c..11be071 100644 --- a/Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️ColorsDemo/⭐️Modern.ColorsDemo.UIKit.ListViewController.swift +++ b/Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️ColorsDemo/⭐️Modern.ColorsDemo.UIKit.ListViewController.swift @@ -36,17 +36,13 @@ extension Modern.ColorsDemo.UIKit { */ private func startObservingList() { + let dataSource = self.dataSource self.listPublisher.addObserver(self) { (listPublisher) in - self.dataSource.apply( - listPublisher.snapshot, - animatingDifferences: true - ) + dataSource.apply(listPublisher.snapshot, animatingDifferences: true) } - self.dataSource.apply( - listPublisher.snapshot, - animatingDifferences: false - ) + + dataSource.apply(self.listPublisher.snapshot, animatingDifferences: false) } /** diff --git a/Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️PokedexDemo/Modern.PokedexDemo.MainView.swift b/Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️PokedexDemo/Modern.PokedexDemo.MainView.swift index 1a932e3..49082a0 100644 --- a/Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️PokedexDemo/Modern.PokedexDemo.MainView.swift +++ b/Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️PokedexDemo/Modern.PokedexDemo.MainView.swift @@ -12,30 +12,28 @@ extension Modern.PokedexDemo { // MARK: - Modern.PokedexDemo.MainView - struct MainView: View { + struct MainView: View { // MARK: Internal - - init() { - - self.pokedexEntries = Modern.PokedexDemo.pokedexEntries + + init( + listView: @escaping () -> ListView + ) { + + self.listView = listView } // MARK: View var body: some View { - let pokedexEntries = self.pokedexEntries.snapshot - return ZStack { + ZStack { - Modern.PokedexDemo.ListView( - service: self.service, - listPublisher: self.pokedexEntries - ) + self.listView() .frame(minHeight: 0, maxHeight: .infinity) .edgesIgnoringSafeArea(.vertical) - if pokedexEntries.isEmpty { + if self.pokedexEntries.isEmpty { VStack(alignment: .center, spacing: 30) { Text("This demo needs to make a network connection to download Pokedex entries") @@ -64,11 +62,17 @@ extension Modern.PokedexDemo { // MARK: Private - @ObservedObject - private var pokedexEntries: ListPublisher + @LiveList( + From() + .orderBy(.ascending(\.$index)), + in: Modern.PokedexDemo.dataStack + ) + private var pokedexEntries @ObservedObject private var service: Modern.PokedexDemo.Service = .init() + + private let listView: () -> ListView } } @@ -82,7 +86,9 @@ struct _Demo_Modern_PokedexDemo_MainView_Preview: PreviewProvider { static var previews: some View { - Modern.PokedexDemo.MainView() + Modern.PokedexDemo.MainView( + listView: Modern.PokedexDemo.UIKit.ListView.init + ) } } diff --git a/Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️PokedexDemo/Modern.PokedexDemo.ItemCell.swift b/Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️PokedexDemo/Modern.PokedexDemo.UIKit.ItemCell.swift similarity index 98% rename from Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️PokedexDemo/Modern.PokedexDemo.ItemCell.swift rename to Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️PokedexDemo/Modern.PokedexDemo.UIKit.ItemCell.swift index 22d0d9a..900536f 100644 --- a/Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️PokedexDemo/Modern.PokedexDemo.ItemCell.swift +++ b/Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️PokedexDemo/Modern.PokedexDemo.UIKit.ItemCell.swift @@ -7,9 +7,9 @@ import CoreStore import UIKit -// MARK: - Modern.PokedexDemo +// MARK: - Modern.PokedexDemo.UIKit -extension Modern.PokedexDemo { +extension Modern.PokedexDemo.UIKit { // MARK: - Modern.PokedexDemo.ItemCell @@ -17,7 +17,7 @@ extension Modern.PokedexDemo { // MARK: Internal - static let reuseIdentifier: String = NSStringFromClass(Modern.PokedexDemo.ItemCell.self) + static let reuseIdentifier: String = NSStringFromClass(Modern.PokedexDemo.UIKit.ItemCell.self) func setPokedexEntry( _ pokedexEntry: Modern.PokedexDemo.PokedexEntry, @@ -61,6 +61,7 @@ extension Modern.PokedexDemo { contentView.backgroundColor = UIColor.placeholderText.withAlphaComponent(0.1) contentView.layer.cornerRadius = 10 + contentView.layer.cornerCurve = .continuous contentView.layer.masksToBounds = true } diff --git a/Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️PokedexDemo/Modern.PokedexDemo.ListView.swift b/Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️PokedexDemo/Modern.PokedexDemo.UIKit.ListView.swift similarity index 68% rename from Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️PokedexDemo/Modern.PokedexDemo.ListView.swift rename to Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️PokedexDemo/Modern.PokedexDemo.UIKit.ListView.swift index 66ab75f..e4c2cd7 100644 --- a/Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️PokedexDemo/Modern.PokedexDemo.ListView.swift +++ b/Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️PokedexDemo/Modern.PokedexDemo.UIKit.ListView.swift @@ -5,9 +5,9 @@ import CoreStore import SwiftUI -// MARK: - Modern.PokedexDemo +// MARK: - Modern.PokedexDemo.UIKit -extension Modern.PokedexDemo { +extension Modern.PokedexDemo.UIKit { // MARK: - Modern.PokedexDemo.ListView @@ -15,19 +15,20 @@ extension Modern.PokedexDemo { // MARK: Internal - init( - service: Modern.PokedexDemo.Service, - listPublisher: ListPublisher - ) { + init() { - self.service = service - self.listPublisher = listPublisher + self.service = Modern.PokedexDemo.Service.init() + self.listPublisher = Modern.PokedexDemo.dataStack + .publishList( + From() + .orderBy(.ascending(\.$index)) + ) } // MARK: UIViewControllerRepresentable - typealias UIViewControllerType = Modern.PokedexDemo.ListViewController + typealias UIViewControllerType = Modern.PokedexDemo.UIKit.ListViewController func makeUIViewController(context: Self.Context) -> UIViewControllerType { @@ -53,7 +54,7 @@ extension Modern.PokedexDemo { #if DEBUG -struct _Demo_Modern_PokedexDemo_ListView_Preview: PreviewProvider { +struct _Demo_Modern_PokedexDemo_UIKit_ListView_Preview: PreviewProvider { // MARK: PreviewProvider @@ -62,10 +63,7 @@ struct _Demo_Modern_PokedexDemo_ListView_Preview: PreviewProvider { let service = Modern.PokedexDemo.Service() service.fetchPokedexEntries() - return Modern.PokedexDemo.ListView( - service: service, - listPublisher: Modern.PokedexDemo.pokedexEntries - ) + return Modern.PokedexDemo.UIKit.ListView() } } diff --git a/Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️PokedexDemo/Modern.PokedexDemo.ListViewController.swift b/Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️PokedexDemo/Modern.PokedexDemo.UIKit.ListViewController.swift similarity index 89% rename from Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️PokedexDemo/Modern.PokedexDemo.ListViewController.swift rename to Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️PokedexDemo/Modern.PokedexDemo.UIKit.ListViewController.swift index f9b9c4f..cf2e716 100644 --- a/Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️PokedexDemo/Modern.PokedexDemo.ListViewController.swift +++ b/Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️PokedexDemo/Modern.PokedexDemo.UIKit.ListViewController.swift @@ -6,9 +6,9 @@ import CoreStore import UIKit -// MARK: - Modern.PokedexDemo +// MARK: - Modern.PokedexDemo.UIKit -extension Modern.PokedexDemo { +extension Modern.PokedexDemo.UIKit { // MARK: - Modern.PokedexDemo.ListViewController @@ -65,8 +65,8 @@ extension Modern.PokedexDemo { self.collectionView.backgroundColor = UIColor.systemBackground self.collectionView.register( - Modern.PokedexDemo.ItemCell.self, - forCellWithReuseIdentifier: Modern.PokedexDemo.ItemCell.reuseIdentifier + Modern.PokedexDemo.UIKit.ItemCell.self, + forCellWithReuseIdentifier: Modern.PokedexDemo.UIKit.ItemCell.reuseIdentifier ) self.startObservingList() @@ -84,9 +84,9 @@ extension Modern.PokedexDemo { cellProvider: { (collectionView, indexPath, pokedexEntry) in let cell = collectionView.dequeueReusableCell( - withReuseIdentifier: Modern.PokedexDemo.ItemCell.reuseIdentifier, + withReuseIdentifier: Modern.PokedexDemo.UIKit.ItemCell.reuseIdentifier, for: indexPath - ) as! Modern.PokedexDemo.ItemCell + ) as! Modern.PokedexDemo.UIKit.ItemCell cell.setPokedexEntry(pokedexEntry, service: self.service) return cell } diff --git a/Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️PokedexDemo/Modern.PokedexDemo.UIKit.swift b/Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️PokedexDemo/Modern.PokedexDemo.UIKit.swift new file mode 100644 index 0000000..759b301 --- /dev/null +++ b/Demo/⭐️Sources/⭐️Demos/⭐️Modern/⭐️PokedexDemo/Modern.PokedexDemo.UIKit.swift @@ -0,0 +1,13 @@ +// +// Demo +// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved. + + +// MARK: - Modern.PokedexDemo + +extension Modern.PokedexDemo { + + // MARK: - UIKit + + enum UIKit {} +} diff --git a/Sources/CoreDataNativeType.swift b/Sources/CoreDataNativeType.swift index 4f98347..86f4d68 100644 --- a/Sources/CoreDataNativeType.swift +++ b/Sources/CoreDataNativeType.swift @@ -33,7 +33,7 @@ import CoreData Objective-C Foundation types that are natively supported by Core Data managed attributes all conform to `CoreDataNativeType`. */ @objc -public protocol CoreDataNativeType: AnyObject, NSObjectProtocol {} +public protocol CoreDataNativeType: NSObjectProtocol {} // MARK: - NSNumber diff --git a/Sources/ForEach+SwiftUI.swift b/Sources/ForEach+SwiftUI.swift new file mode 100644 index 0000000..bb0aa2f --- /dev/null +++ b/Sources/ForEach+SwiftUI.swift @@ -0,0 +1,56 @@ +// +// ForEach+SwiftUI.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) && canImport(SwiftUI) + +import Combine +import SwiftUI + + +// MARK: - ForEach + +@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *) +extension ForEach where Content: View { + + // MARK: Public + + public init( + _ listSnapshot: Data, + @ViewBuilder content: @escaping (ObjectPublisher) -> Content + ) where Data == LiveList.Items, ID == O.ObjectID { + + self.init(listSnapshot, id: \.cs_objectID, content: content) + } + + public init( + _ objectPublishers: Data, + @ViewBuilder content: @escaping (ObjectPublisher) -> Content + ) where Data.Element == ObjectPublisher, ID == O.ObjectID { + + self.init(objectPublishers, id: \.cs_objectID, content: content) + } +} + +#endif diff --git a/Sources/ForEachSection.swift b/Sources/ForEachSection.swift new file mode 100644 index 0000000..5680626 --- /dev/null +++ b/Sources/ForEachSection.swift @@ -0,0 +1,59 @@ +// +// ForEachSection.swift +// CoreStore +// +// Created by John Rommel Estropia on 2021/02/13. +// Copyright © 2021 John Rommel Estropia. All rights reserved. +// + +#if canImport(Combine) && canImport(SwiftUI) + +import Combine +import SwiftUI + + +// MARK: - ForEachSection + +@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) +public struct ForEachSection: View { + + // MARK: Internal + + public init( + in listSnapshot: LiveList.Items, + @ViewBuilder sections: @escaping ( + _ sectionID: ListSnapshot.SectionID, + _ objects: [ObjectPublisher] + ) -> Sections + ) { + + self.listSnapshot = listSnapshot + self.sections = sections + } + + + // MARK: View + + public var body: some View { + + ForEach(self.listSnapshot.sectionIDs, id: \.self) { sectionID in + + self.sections( + sectionID, + self.listSnapshot.items(inSectionWithID: sectionID) + ) + } + } + + + // MARK: Private + + private let listSnapshot: LiveList.Items + + private let sections: ( + _ sectionID: ListSnapshot.SectionID, + _ objects: [ObjectPublisher] + ) -> Sections +} + +#endif diff --git a/Sources/ListPublisher.swift b/Sources/ListPublisher.swift index b2a48cf..d4aea23 100644 --- a/Sources/ListPublisher.swift +++ b/Sources/ListPublisher.swift @@ -201,6 +201,14 @@ public final class ListPublisher: Hashable { } ) } + + /** + Used internally by CoreStore. Do not call directly. + */ + public func cs_dataStack() -> DataStack? { + + return self.context.parentStack + } // MARK: Public (3rd Party Utilities) diff --git a/Sources/ListReader.swift b/Sources/ListReader.swift new file mode 100644 index 0000000..9fcd253 --- /dev/null +++ b/Sources/ListReader.swift @@ -0,0 +1,78 @@ +// +// ListReader.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) && canImport(SwiftUI) + +import Combine +import SwiftUI + + +// MARK: - ListReader + +@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) +public struct ListReader: View { + + // MARK: Internal + + public init( + _ listPublisher: ListPublisher, + @ViewBuilder content: @escaping (Value) -> Content + ) where Value == LiveList.Items { + + self._list = .init(listPublisher) + self.content = content + self.keyPath = \.self + } + + public init( + _ listPublisher: ListPublisher, + keyPath: KeyPath.Items, Value>, + @ViewBuilder content: @escaping (Value) -> Content + ) { + + self._list = .init(listPublisher) + self.content = content + self.keyPath = keyPath + } + + + // MARK: View + + public var body: some View { + + self.content(self.list[keyPath: self.keyPath]) + } + + + // MARK: Private + + @LiveList + private var list: LiveList.Items + + private let content: (Value) -> Content + private let keyPath: KeyPath.Items, Value> +} + +#endif diff --git a/Sources/LiveList.swift b/Sources/LiveList.swift new file mode 100644 index 0000000..9381d6c --- /dev/null +++ b/Sources/LiveList.swift @@ -0,0 +1,162 @@ +// +// LiveList.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) && canImport(SwiftUI) + +import Combine +import SwiftUI + + +// MARK: - LiveList + +@propertyWrapper +@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) +public struct LiveList: DynamicProperty { + + // MARK: Public + + public typealias Items = ListSnapshot + + public init( + _ listPublisher: ListPublisher + ) { + + self.observer = .init(listPublisher: listPublisher) + } + + public init( + _ from: From, + _ fetchClauses: FetchClause..., + in dataStack: DataStack + ) { + + self.init(from, fetchClauses, in: dataStack) + } + + public init( + _ from: From, + _ fetchClauses: [FetchClause], + in dataStack: DataStack + ) { + + self.init(dataStack.publishList(from, fetchClauses)) + } + + public init( + _ clauseChain: B, + in dataStack: DataStack + ) where B.ObjectType == Object { + + self.init(dataStack.publishList(clauseChain)) + } + + public init( + _ from: From, + _ sectionBy: SectionBy, + _ fetchClauses: FetchClause..., + in dataStack: DataStack + ) { + + self.init(from, sectionBy, fetchClauses, in: dataStack) + } + + public init( + _ from: From, + _ sectionBy: SectionBy, + _ fetchClauses: [FetchClause], + in dataStack: DataStack + ) { + + self.init(dataStack.publishList(from, sectionBy, fetchClauses)) + } + + public init( + _ clauseChain: B, + in dataStack: DataStack + ) where B.ObjectType == Object { + + self.init(dataStack.publishList(clauseChain)) + } + + + // MARK: @propertyWrapper + + public var wrappedValue: Items { + + return self.observer.items + } + + public var projectedValue: ListPublisher { + + return self.observer.listPublisher + } + + + // MARK: DynamicProperty + + public mutating func update() { + + self._observer.update() + } + + + // MARK: Private + + @ObservedObject + private var observer: Observer + + + // MARK: - Observer + + private final class Observer: ObservableObject { + + @Published + var items: Items + + let listPublisher: ListPublisher + + init(listPublisher: ListPublisher) { + + self.listPublisher = listPublisher + self.items = listPublisher.snapshot + + listPublisher.addObserver(self) { [weak self] (listPublisher) in + + guard let self = self else { + + return + } + self.items = listPublisher.snapshot + } + } + + deinit { + + self.listPublisher.removeObserver(self) + } + } +} + +#endif diff --git a/Sources/LiveObject.swift b/Sources/LiveObject.swift new file mode 100644 index 0000000..fe74276 --- /dev/null +++ b/Sources/LiveObject.swift @@ -0,0 +1,110 @@ +// +// LiveObject.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) && canImport(SwiftUI) + +import Combine +import SwiftUI + + +// MARK: - LiveObject + +@propertyWrapper +@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) +public struct LiveObject: DynamicProperty { + + // MARK: Public + + public typealias Item = ObjectSnapshot + + public init(_ objectPublisher: ObjectPublisher?) { + + self.observer = .init(objectPublisher: objectPublisher) + } + + + // MARK: @propertyWrapper + + public var wrappedValue: Item? { + + return self.observer.item + } + + + // MARK: DynamicProperty + + public mutating func update() { + + self._observer.update() + } + + + // MARK: Private + + @ObservedObject + private var observer: Observer + + + // MARK: - Observer + + private final class Observer: ObservableObject { + + @Published + var item: Item? + + let objectPublisher: ObjectPublisher? + + init(objectPublisher: ObjectPublisher?) { + + guard + let dataStack = objectPublisher?.cs_dataStack(), + let objectPublisher = objectPublisher?.asPublisher(in: dataStack) + else { + + self.objectPublisher = objectPublisher + self.item = nil + return + } + self.objectPublisher = objectPublisher + self.item = objectPublisher.snapshot + + objectPublisher.addObserver(self) { [weak self] (objectPublisher) in + + guard let self = self else { + + return + } + self.item = objectPublisher.snapshot + } + } + + deinit { + + self.objectPublisher?.removeObserver(self) + } + } +} + +#endif diff --git a/Sources/ObjectPublisher.swift b/Sources/ObjectPublisher.swift index 2e6380e..958f4ef 100644 --- a/Sources/ObjectPublisher.swift +++ b/Sources/ObjectPublisher.swift @@ -202,6 +202,11 @@ public final class ObjectPublisher: ObjectRepresentation, Hash // MARK: Internal + + internal var cs_objectID: O.ObjectID { + + return self.objectID() + } internal static func createUncached(objectID: O.ObjectID, context: NSManagedObjectContext) -> ObjectPublisher { diff --git a/Sources/ObjectReader.swift b/Sources/ObjectReader.swift index 4d311d3..8105a85 100644 --- a/Sources/ObjectReader.swift +++ b/Sources/ObjectReader.swift @@ -1,9 +1,26 @@ // -// ListState.swift +// ObjectReader.swift // CoreStore // -// Created by John Rommel Estropia on 2020/12/26. -// Copyright © 2020 John Rommel Estropia. All rights reserved. +// 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) && canImport(SwiftUI) @@ -15,40 +32,29 @@ import SwiftUI // MARK: - ObjectReader @available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) -public struct ObjectReader: View { +public struct ObjectReader: View { // MARK: Internal public init( _ objectPublisher: ObjectPublisher?, - @ViewBuilder content: @escaping (ObjectSnapshot) -> Content, - @ViewBuilder placeholder: @escaping () -> Placeholder - ) { + @ViewBuilder content: @escaping (Value) -> Content + ) where Value == LiveObject.Item { - self.objectPublisher = .init( - objectPublisher.flatMap { - - guard let dataStack = $0.cs_dataStack() else { - - return nil - } - return $0.asPublisher(in: dataStack) - } - ) + self._object = .init(objectPublisher) self.content = content - self.placeholder = placeholder + self.keyPath = \.self } public init( _ objectPublisher: ObjectPublisher?, - @ViewBuilder content: @escaping (ObjectSnapshot) -> Content - ) where Placeholder == EmptyView { + keyPath: KeyPath.Item, Value>, + @ViewBuilder content: @escaping (Value) -> Content + ) { - self.init( - objectPublisher, - content: content, - placeholder: EmptyView.init - ) + self._object = .init(objectPublisher) + self.content = content + self.keyPath = keyPath } @@ -56,45 +62,20 @@ public struct ObjectReader> + @LiveObject + private var object: LiveObject.Item? - private let content: (ObjectSnapshot) -> Content - private let placeholder: () -> Placeholder - - - // MARK: - OptionalObservedObject - - fileprivate final class OptionalObservedObject: ObservableObject where ObservableObjectPublisher == T.ObjectWillChangePublisher { - - // MARK: Internal - - let wrappedValue: T? - - init(_ wrappedValue: T?) { - - self.wrappedValue = wrappedValue - self.objectWillChange = wrappedValue.map(\.objectWillChange) ?? .init() - } - - // MARK: ObservableObject - - let objectWillChange: ObservableObjectPublisher - } + private let content: (Value) -> Content + private let keyPath: KeyPath.Item, Value> } #endif - diff --git a/Sources/SectionsReader.swift b/Sources/SectionsReader.swift new file mode 100644 index 0000000..c461224 --- /dev/null +++ b/Sources/SectionsReader.swift @@ -0,0 +1,65 @@ +// +// SectionsReader.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) && canImport(SwiftUI) + +import Combine +import SwiftUI + + +// MARK: - SectionsReader + +@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) +public struct SectionsReader: View { + + // MARK: Internal + + public init( + _ listPublisher: ListPublisher, + @ViewBuilder content: @escaping (LiveList.Items) -> Content + ) { + + self._list = .init(listPublisher) + self.content = content + } + + + // MARK: View + + public var body: some View { + + self.content(self.list) + } + + + // MARK: Private + + @LiveList + private var list: LiveList.Items + + private let content: (LiveList.Items) -> Content +} + +#endif