Prototyping SwiftUI utilities

This commit is contained in:
John Estropia
2021-02-16 09:12:36 +09:00
parent edd8ba55d8
commit f7471f56a4
27 changed files with 853 additions and 227 deletions

View File

@@ -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 = "<group>"; };
B5BF7FC5234D7E460070E741 /* ObjectSnapshot.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObjectSnapshot.swift; sourceTree = "<group>"; };
B5BF7FCA234D80910070E741 /* Internals.LazyNonmutating.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Internals.LazyNonmutating.swift; sourceTree = "<group>"; };
B5C7958E25D7D18000BDACC1 /* LiveList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveList.swift; sourceTree = "<group>"; };
B5C7959325D7D18700BDACC1 /* LiveObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveObject.swift; sourceTree = "<group>"; };
B5C7959825D7D8B300BDACC1 /* ListReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListReader.swift; sourceTree = "<group>"; };
B5C795A025D7EB2200BDACC1 /* ForEach+SwiftUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ForEach+SwiftUI.swift"; sourceTree = "<group>"; };
B5C795AA25D7ED8600BDACC1 /* SectionsReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionsReader.swift; sourceTree = "<group>"; };
B5C795AF25D7EF8600BDACC1 /* ForEachSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForEachSection.swift; sourceTree = "<group>"; };
B5C976E21C6C9F6A00B1AF90 /* UnsafeDataTransaction+Observing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UnsafeDataTransaction+Observing.swift"; sourceTree = "<group>"; };
B5C976E61C6E3A5900B1AF90 /* Internals.CoreStoreFetchedResultsController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Internals.CoreStoreFetchedResultsController.swift; sourceTree = "<group>"; };
B5CA2B071F7E5ACA004B1936 /* WhereClauseType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WhereClauseType.swift; sourceTree = "<group>"; };
@@ -1543,7 +1573,9 @@
B52FEC722596DB6400368BFB /* SwiftUI */ = {
isa = PBXGroup;
children = (
B52FEC732596DBE000368BFB /* ObjectReader.swift */,
B5C7959F25D7EB1200BDACC1 /* Extensions */,
B5C7959E25D7E8B100BDACC1 /* Views */,
B5C7959D25D7E89B00BDACC1 /* PropertyWrappers */,
);
name = SwiftUI;
sourceTree = "<group>";
@@ -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 = "<group>";
};
B5C7959D25D7E89B00BDACC1 /* PropertyWrappers */ = {
isa = PBXGroup;
children = (
B5C7958E25D7D18000BDACC1 /* LiveList.swift */,
B5C7959325D7D18700BDACC1 /* LiveObject.swift */,
);
name = PropertyWrappers;
sourceTree = "<group>";
};
B5C7959E25D7E8B100BDACC1 /* Views */ = {
isa = PBXGroup;
children = (
B52FEC732596DBE000368BFB /* ObjectReader.swift */,
B5C7959825D7D8B300BDACC1 /* ListReader.swift */,
B5C795AA25D7ED8600BDACC1 /* SectionsReader.swift */,
B5C795AF25D7EF8600BDACC1 /* ForEachSection.swift */,
);
name = Views;
sourceTree = "<group>";
};
B5C7959F25D7EB1200BDACC1 /* Extensions */ = {
isa = PBXGroup;
children = (
B5C795A025D7EB2200BDACC1 /* ForEach+SwiftUI.swift */,
);
name = Extensions;
sourceTree = "<group>";
};
B5C795BE25D933C200BDACC1 /* Reactive Programming */ = {
isa = PBXGroup;
children = (
);
name = "Reactive Programming";
sourceTree = "<group>";
};
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 */,

View File

@@ -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 = "<group>"; };
B54D2F7D25119DCE004BEC7D /* Advanced.EvolutionDemo.V1.FromV2.xcmappingmodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcmappingmodel; path = Advanced.EvolutionDemo.V1.FromV2.xcmappingmodel; sourceTree = "<group>"; };
B54D2F842511B70B004BEC7D /* Advanced.EvolutionDemo.CreaturesDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Advanced.EvolutionDemo.CreaturesDataSource.swift; sourceTree = "<group>"; };
B566C8E724F9D406001134A1 /* Modern.PokedexDemo.ListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.PokedexDemo.ListView.swift; sourceTree = "<group>"; };
B566C8E924F9D412001134A1 /* Modern.PokedexDemo.ListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.PokedexDemo.ListViewController.swift; sourceTree = "<group>"; };
B566C8EB24F9D694001134A1 /* Modern.PokedexDemo.ItemCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.PokedexDemo.ItemCell.swift; sourceTree = "<group>"; };
B566C8E724F9D406001134A1 /* Modern.PokedexDemo.UIKit.ListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.PokedexDemo.UIKit.ListView.swift; sourceTree = "<group>"; };
B566C8E924F9D412001134A1 /* Modern.PokedexDemo.UIKit.ListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.PokedexDemo.UIKit.ListViewController.swift; sourceTree = "<group>"; };
B566C8EB24F9D694001134A1 /* Modern.PokedexDemo.UIKit.ItemCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.PokedexDemo.UIKit.ItemCell.swift; sourceTree = "<group>"; };
B566C8ED24FA1EA3001134A1 /* Modern.PokedexDemo.Details.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.PokedexDemo.Details.swift; sourceTree = "<group>"; };
B5931B4125124756007DA58E /* Advanced.EvolutionDemo.V1.FromV2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Advanced.EvolutionDemo.V1.FromV2.swift; sourceTree = "<group>"; };
B5931B432512480A007DA58E /* Advanced.EvolutionDemo.V2.FromV1.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Advanced.EvolutionDemo.V2.FromV1.swift; sourceTree = "<group>"; };
@@ -185,7 +186,7 @@
B5A3919F24E8F00A00E7E8BD /* Modern.ColorsDemo.UIKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.ColorsDemo.UIKit.swift; sourceTree = "<group>"; };
B5A391A124E8F01F00E7E8BD /* ⭐Modern.ColorsDemo.UIKit.ListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "⭐Modern.ColorsDemo.UIKit.ListViewController.swift"; sourceTree = "<group>"; };
B5A391A324E8F04300E7E8BD /* Modern.ColorsDemo.UIKit.ListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.ColorsDemo.UIKit.ListView.swift; sourceTree = "<group>"; };
B5A391A524E8F4EA00E7E8BD /* ⭐️Modern.ColorsDemo.MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "⭐️Modern.ColorsDemo.MainView.swift"; sourceTree = "<group>"; };
B5A391A524E8F4EA00E7E8BD /* Modern.ColorsDemo.MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.ColorsDemo.MainView.swift; sourceTree = "<group>"; };
B5A391A924E9104300E7E8BD /* Modern.ColorsDemo.UIKit.ItemCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.ColorsDemo.UIKit.ItemCell.swift; sourceTree = "<group>"; };
B5A391AB24E9143B00E7E8BD /* Modern.ColorsDemo.UIKit.DetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.ColorsDemo.UIKit.DetailView.swift; sourceTree = "<group>"; };
B5A391AD24E9150F00E7E8BD /* ⭐Modern.ColorsDemo.UIKit.DetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "⭐Modern.ColorsDemo.UIKit.DetailViewController.swift"; sourceTree = "<group>"; };
@@ -214,6 +215,7 @@
B5A5440A25049492000DC5E3 /* Advanced.EvolutionDemo.V3.Creature.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Advanced.EvolutionDemo.V3.Creature.swift; sourceTree = "<group>"; };
B5A5440C2504949C000DC5E3 /* Advanced.EvolutionDemo.V4.Creature.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Advanced.EvolutionDemo.V4.Creature.swift; sourceTree = "<group>"; };
B5C18F3225138700001BEFB3 /* Advanced.EvolutionDemo.ProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Advanced.EvolutionDemo.ProgressView.swift; sourceTree = "<group>"; };
B5C795B525D8EE2B00BDACC1 /* Modern.PokedexDemo.UIKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.PokedexDemo.UIKit.swift; sourceTree = "<group>"; };
B5D6F1F7250E07FD00DF5D2F /* Advanced.EvolutionDemo.V1.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Advanced.EvolutionDemo.V1.swift; sourceTree = "<group>"; };
B5D6F1FB250E0B1700DF5D2F /* Advanced.EvolutionDemo.V2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Advanced.EvolutionDemo.V2.swift; sourceTree = "<group>"; };
B5D6F1FD250E0B3F00DF5D2F /* Advanced.EvolutionDemo.GeologicalPeriod.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Advanced.EvolutionDemo.GeologicalPeriod.swift; sourceTree = "<group>"; };
@@ -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 = "<group>";
};
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 = "<group>";
};
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 */,

View File

@@ -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

View File

@@ -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
)
}
)
}

View File

@@ -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)

View File

@@ -1,12 +1,15 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="16119" systemVersion="19F101" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="17709" systemVersion="20C5048l" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
<entity name="Palette" representedClassName="Classic_ColorsDemo_Palette" syncable="YES">
<attribute name="brightness" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="colorGroup" optional="YES" transient="YES" attributeType="String"/>
<attribute name="hue" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="saturation" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<fetchedProperty name="testFetchProperty" optional="YES">
<fetchRequest name="fetchedPropertyFetchRequest" entity="Palette"/>
</fetchedProperty>
</entity>
<elements>
<element name="Palette" positionX="-63" positionY="-18" width="128" height="103"/>
<element name="Palette" positionX="-63" positionY="-18" width="128" height="110"/>
</elements>
</model>

View File

@@ -13,12 +13,6 @@ extension Modern.ColorsDemo {
struct MainView<ListView: View, DetailView: View>: View {
/**
Sample 1: Setting a sectioned `ListPublisher` declared as an `@ObservedObject`
*/
@ObservedObject
private var listPublisher: ListPublisher<Modern.ColorsDemo.Palette>
// 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<Modern.ColorsDemo.Palette>.Items
private let listView: (
_ listPublisher: ListPublisher<Modern.ColorsDemo.Palette>,
_ onPaletteTapped: @escaping (ObjectPublisher<Modern.ColorsDemo.Palette>) -> Void
@@ -103,12 +86,13 @@ extension Modern.ColorsDemo {
@State
private var selectedPalette: ObjectPublisher<Modern.ColorsDemo.Palette>?
@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() {

View File

@@ -2,6 +2,7 @@
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
import SwiftUI
import CoreStore
// MARK: - Modern

View File

@@ -17,8 +17,8 @@ extension Modern.ColorsDemo.SwiftUI {
/**
Sample 1: Setting an `ObjectPublisher` declared as an `@ObservedObject`
*/
@ObservedObject
private var palette: ObjectPublisher<Modern.ColorsDemo.Palette>
@LiveObject
private var palette: LiveObject<Modern.ColorsDemo.Palette>.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<Modern.ColorsDemo.Palette>) {
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()
}
)
}
}
}
}

View File

@@ -16,15 +16,15 @@ extension Modern.ColorsDemo.SwiftUI {
/**
Sample 1: Setting an `ObjectPublisher` declared as an `@ObservedObject`
*/
@ObservedObject
private var palette: ObjectPublisher<Modern.ColorsDemo.Palette>
@LiveObject
private var palette: LiveObject<Modern.ColorsDemo.Palette>.Item?
// MARK: Internal
internal init(_ palette: ObjectPublisher<Modern.ColorsDemo.Palette>) {
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)
}
}
}
}

View File

@@ -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<Modern.ColorsDemo.Palette>
@LiveList
private var palettes: LiveList<Modern.ColorsDemo.Palette>.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<Modern.ColorsDemo.Palette>) -> 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
)

View File

@@ -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)
}
/**

View File

@@ -12,30 +12,28 @@ extension Modern.PokedexDemo {
// MARK: - Modern.PokedexDemo.MainView
struct MainView: View {
struct MainView<ListView: View>: 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<Modern.PokedexDemo.PokedexEntry>
@LiveList(
From<Modern.PokedexDemo.PokedexEntry>()
.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
)
}
}

View File

@@ -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
}

View File

@@ -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<Modern.PokedexDemo.PokedexEntry>
) {
init() {
self.service = service
self.listPublisher = listPublisher
self.service = Modern.PokedexDemo.Service.init()
self.listPublisher = Modern.PokedexDemo.dataStack
.publishList(
From<Modern.PokedexDemo.PokedexEntry>()
.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()
}
}

View File

@@ -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
}

View File

@@ -0,0 +1,13 @@
//
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
// MARK: - Modern.PokedexDemo
extension Modern.PokedexDemo {
// MARK: - UIKit
enum UIKit {}
}

View File

@@ -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

View File

@@ -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<O: DynamicObject>(
_ listSnapshot: Data,
@ViewBuilder content: @escaping (ObjectPublisher<O>) -> Content
) where Data == LiveList<O>.Items, ID == O.ObjectID {
self.init(listSnapshot, id: \.cs_objectID, content: content)
}
public init<O: DynamicObject>(
_ objectPublishers: Data,
@ViewBuilder content: @escaping (ObjectPublisher<O>) -> Content
) where Data.Element == ObjectPublisher<O>, ID == O.ObjectID {
self.init(objectPublishers, id: \.cs_objectID, content: content)
}
}
#endif

View File

@@ -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<Object: DynamicObject, Sections: View>: View {
// MARK: Internal
public init(
in listSnapshot: LiveList<Object>.Items,
@ViewBuilder sections: @escaping (
_ sectionID: ListSnapshot<Object>.SectionID,
_ objects: [ObjectPublisher<Object>]
) -> 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<Object>.Items
private let sections: (
_ sectionID: ListSnapshot<Object>.SectionID,
_ objects: [ObjectPublisher<Object>]
) -> Sections
}
#endif

View File

@@ -201,6 +201,14 @@ public final class ListPublisher<O: DynamicObject>: Hashable {
}
)
}
/**
Used internally by CoreStore. Do not call directly.
*/
public func cs_dataStack() -> DataStack? {
return self.context.parentStack
}
// MARK: Public (3rd Party Utilities)

78
Sources/ListReader.swift Normal file
View File

@@ -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<Object: DynamicObject, Content: View, Value>: View {
// MARK: Internal
public init(
_ listPublisher: ListPublisher<Object>,
@ViewBuilder content: @escaping (Value) -> Content
) where Value == LiveList<Object>.Items {
self._list = .init(listPublisher)
self.content = content
self.keyPath = \.self
}
public init(
_ listPublisher: ListPublisher<Object>,
keyPath: KeyPath<LiveList<Object>.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<Object>.Items
private let content: (Value) -> Content
private let keyPath: KeyPath<LiveList<Object>.Items, Value>
}
#endif

162
Sources/LiveList.swift Normal file
View File

@@ -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<Object: DynamicObject>: DynamicProperty {
// MARK: Public
public typealias Items = ListSnapshot<Object>
public init(
_ listPublisher: ListPublisher<Object>
) {
self.observer = .init(listPublisher: listPublisher)
}
public init(
_ from: From<Object>,
_ fetchClauses: FetchClause...,
in dataStack: DataStack
) {
self.init(from, fetchClauses, in: dataStack)
}
public init(
_ from: From<Object>,
_ fetchClauses: [FetchClause],
in dataStack: DataStack
) {
self.init(dataStack.publishList(from, fetchClauses))
}
public init<B: FetchChainableBuilderType>(
_ clauseChain: B,
in dataStack: DataStack
) where B.ObjectType == Object {
self.init(dataStack.publishList(clauseChain))
}
public init(
_ from: From<Object>,
_ sectionBy: SectionBy<Object>,
_ fetchClauses: FetchClause...,
in dataStack: DataStack
) {
self.init(from, sectionBy, fetchClauses, in: dataStack)
}
public init(
_ from: From<Object>,
_ sectionBy: SectionBy<Object>,
_ fetchClauses: [FetchClause],
in dataStack: DataStack
) {
self.init(dataStack.publishList(from, sectionBy, fetchClauses))
}
public init<B: SectionMonitorBuilderType>(
_ 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<Object> {
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<Object>
init(listPublisher: ListPublisher<Object>) {
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

110
Sources/LiveObject.swift Normal file
View File

@@ -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<O: DynamicObject>: DynamicProperty {
// MARK: Public
public typealias Item = ObjectSnapshot<O>
public init(_ objectPublisher: ObjectPublisher<O>?) {
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<O>?
init(objectPublisher: ObjectPublisher<O>?) {
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

View File

@@ -202,6 +202,11 @@ public final class ObjectPublisher<O: DynamicObject>: ObjectRepresentation, Hash
// MARK: Internal
internal var cs_objectID: O.ObjectID {
return self.objectID()
}
internal static func createUncached(objectID: O.ObjectID, context: NSManagedObjectContext) -> ObjectPublisher<O> {

View File

@@ -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<Object: DynamicObject, Content: View, Placeholder: View>: View {
public struct ObjectReader<Object: DynamicObject, Content: View, Value>: View {
// MARK: Internal
public init(
_ objectPublisher: ObjectPublisher<Object>?,
@ViewBuilder content: @escaping (ObjectSnapshot<Object>) -> Content,
@ViewBuilder placeholder: @escaping () -> Placeholder
) {
@ViewBuilder content: @escaping (Value) -> Content
) where Value == LiveObject<Object>.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<Object>?,
@ViewBuilder content: @escaping (ObjectSnapshot<Object>) -> Content
) where Placeholder == EmptyView {
keyPath: KeyPath<LiveObject<Object>.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<Object: DynamicObject, Content: View, Placeholder: Vi
public var body: some View {
if let snapshot = self.objectPublisher.wrappedValue?.snapshot {
if let object = self.object {
self.content(snapshot)
}
else {
self.placeholder()
self.content(object[keyPath: self.keyPath])
}
}
// MARK: Private
@ObservedObject
private var objectPublisher: OptionalObservedObject<ObjectPublisher<Object>>
@LiveObject
private var object: LiveObject<Object>.Item?
private let content: (ObjectSnapshot<Object>) -> Content
private let placeholder: () -> Placeholder
// MARK: - OptionalObservedObject
fileprivate final class OptionalObservedObject<T: ObservableObject>: 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<LiveObject<Object>.Item, Value>
}
#endif

View File

@@ -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<Object: DynamicObject, Content: View>: View {
// MARK: Internal
public init(
_ listPublisher: ListPublisher<Object>,
@ViewBuilder content: @escaping (LiveList<Object>.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<Object>.Items
private let content: (LiveList<Object>.Items) -> Content
}
#endif