WIP: new demo app

This commit is contained in:
John Estropia
2020-08-17 09:06:25 +09:00
parent e720504855
commit d988daa025
62 changed files with 4304 additions and 2 deletions

Binary file not shown.

View File

@@ -1,6 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Demo/Demo.xcodeproj">
</FileRef>
<FileRef
location = "group:CoreStore.xcodeproj">
</FileRef>

View File

@@ -38,7 +38,7 @@ final class Palette: CoreStoreObject {
return colorName
}
let colorName: String
switch object.$hue.value % 360 {
switch object.$hue.value {
case 0 ..< 20: colorName = "Lower Reds"
case 20 ..< 57: colorName = "Oranges and Browns"
case 57 ..< 90: colorName = "Yellow-Greens"
@@ -47,7 +47,8 @@ final class Palette: CoreStoreObject {
case 197 ..< 241: colorName = "Blues"
case 241 ..< 297: colorName = "Violets"
case 297 ..< 331: colorName = "Magentas"
default: colorName = "Upper Reds"
case 331 ..< 360: colorName = "Upper Reds"
default: colorName = "<Invalid>"
}
field.primitiveValue = colorName
return colorName

Binary file not shown.

View File

@@ -0,0 +1,698 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 50;
objects = {
/* Begin PBXBuildFile section */
B5A3911D24E5429200E7E8BD /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A3911C24E5429200E7E8BD /* AppDelegate.swift */; };
B5A3911F24E5429200E7E8BD /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A3911E24E5429200E7E8BD /* SceneDelegate.swift */; };
B5A3912124E5429200E7E8BD /* Menu.MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A3912024E5429200E7E8BD /* Menu.MainView.swift */; };
B5A3912324E5429300E7E8BD /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B5A3912224E5429300E7E8BD /* Images.xcassets */; };
B5A3912624E5429300E7E8BD /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B5A3912524E5429300E7E8BD /* Preview Assets.xcassets */; };
B5A3912924E5429300E7E8BD /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B5A3912724E5429300E7E8BD /* LaunchScreen.storyboard */; };
B5A3913424E6170500E7E8BD /* Menu.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A3913324E6170500E7E8BD /* Menu.swift */; };
B5A3915324E6537F00E7E8BD /* Menu.ItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A3915224E6537F00E7E8BD /* Menu.ItemView.swift */; };
B5A3915924E685EC00E7E8BD /* Classic.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A3915824E685EC00E7E8BD /* Classic.swift */; };
B5A3915B24E685FE00E7E8BD /* Modern.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A3915A24E685FE00E7E8BD /* Modern.swift */; };
B5A3915E24E6922E00E7E8BD /* Modern.PlacemarksDemo.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A3915D24E6922E00E7E8BD /* Modern.PlacemarksDemo.swift */; };
B5A3916024E6925900E7E8BD /* Modern.PlacemarksDemo.MapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A3915F24E6925900E7E8BD /* Modern.PlacemarksDemo.MapView.swift */; };
B5A3916224E697BA00E7E8BD /* Modern.PlacemarksDemo.MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A3916124E697BA00E7E8BD /* Modern.PlacemarksDemo.MainView.swift */; };
B5A3916524E698C700E7E8BD /* Modern.PlacemarksDemo.Place.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A3916424E698C700E7E8BD /* Modern.PlacemarksDemo.Place.swift */; };
B5A3916B24E698F900E7E8BD /* CoreStore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B5A3916724E698F900E7E8BD /* CoreStore.framework */; };
B5A3916C24E698F900E7E8BD /* CoreStore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = B5A3916724E698F900E7E8BD /* CoreStore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
B5A3916D24E698F900E7E8BD /* CoreStore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B5A3916824E698F900E7E8BD /* CoreStore.framework */; };
B5A3916E24E698F900E7E8BD /* CoreStore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = B5A3916824E698F900E7E8BD /* CoreStore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
B5A3916F24E698F900E7E8BD /* CoreStore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B5A3916924E698F900E7E8BD /* CoreStore.framework */; };
B5A3917024E698F900E7E8BD /* CoreStore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = B5A3916924E698F900E7E8BD /* CoreStore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
B5A3917124E698F900E7E8BD /* CoreStore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B5A3916A24E698F900E7E8BD /* CoreStore.framework */; };
B5A3917224E698F900E7E8BD /* CoreStore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = B5A3916A24E698F900E7E8BD /* CoreStore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
B5A3917524E6990200E7E8BD /* MapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B5A3917424E6990200E7E8BD /* MapKit.framework */; };
B5A3917724E6990700E7E8BD /* CoreLocation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B5A3917624E6990700E7E8BD /* CoreLocation.framework */; };
B5A3917924E6991600E7E8BD /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B5A3917824E6991600E7E8BD /* SwiftUI.framework */; };
B5A3917C24E6A76C00E7E8BD /* LazyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A3917B24E6A76C00E7E8BD /* LazyView.swift */; };
B5A3917E24E7728400E7E8BD /* Modern.PlacemarksDemo.Geocoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A3917D24E7728400E7E8BD /* Modern.PlacemarksDemo.Geocoder.swift */; };
B5A3918024E787D900E7E8BD /* InstructionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A3917F24E787D900E7E8BD /* InstructionsView.swift */; };
B5A3918324E7A21800E7E8BD /* Modern.TimeZonesDemo.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A3918224E7A21800E7E8BD /* Modern.TimeZonesDemo.swift */; };
B5A3918624E7A54A00E7E8BD /* Modern.TimeZonesDemo.TimeZone.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A3918524E7A54A00E7E8BD /* Modern.TimeZonesDemo.TimeZone.swift */; };
B5A3918824E7A8F900E7E8BD /* Modern.TimeZonesDemo.MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A3918724E7A8F900E7E8BD /* Modern.TimeZonesDemo.MainView.swift */; };
B5A3918A24E7AD1800E7E8BD /* Modern.TimeZonesDemo.ListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A3918924E7AD1800E7E8BD /* Modern.TimeZonesDemo.ListView.swift */; };
B5A3918C24E7B44B00E7E8BD /* Modern.TimeZonesDemo.ItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A3918B24E7B44B00E7E8BD /* Modern.TimeZonesDemo.ItemView.swift */; };
B5A3918F24E7E06500E7E8BD /* Modern.ColorsDemo.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A3918E24E7E06500E7E8BD /* Modern.ColorsDemo.swift */; };
B5A3919224E7E0C600E7E8BD /* Modern.ColorsDemo.Palette.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A3919124E7E0C600E7E8BD /* Modern.ColorsDemo.Palette.swift */; };
B5A3919424E7E36700E7E8BD /* Modern.ColorsDemo.SwiftUI.ListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A3919324E7E36700E7E8BD /* Modern.ColorsDemo.SwiftUI.ListView.swift */; };
B5A3919624E7E4AC00E7E8BD /* Modern.ColorsDemo.SwiftUI.ItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A3919524E7E4AC00E7E8BD /* Modern.ColorsDemo.SwiftUI.ItemView.swift */; };
B5A3919824E7E67000E7E8BD /* Modern.ColorsDemo.Filter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A3919724E7E67000E7E8BD /* Modern.ColorsDemo.Filter.swift */; };
B5A3919A24E8207A00E7E8BD /* Modern.ColorsDemo.SwiftUI.DetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A3919924E8207A00E7E8BD /* Modern.ColorsDemo.SwiftUI.DetailView.swift */; };
B5A3919E24E8EEB600E7E8BD /* Modern.ColorsDemo.SwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A3919D24E8EEB600E7E8BD /* Modern.ColorsDemo.SwiftUI.swift */; };
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 */; };
B5A391A824E90F1000E7E8BD /* UIImage+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A391A724E90F1000E7E8BD /* UIImage+Extensions.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 */; };
B5A391B124E96AF600E7E8BD /* Modern.PokedexDemo.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A391B024E96AF600E7E8BD /* Modern.PokedexDemo.swift */; };
B5A391B424E96C0A00E7E8BD /* Modern.PokedexDemo.PokemonSpecies.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A391B324E96C0A00E7E8BD /* Modern.PokedexDemo.PokemonSpecies.swift */; };
B5A391B624E96C5500E7E8BD /* Modern.PokedexDemo.Move.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A391B524E96C5500E7E8BD /* Modern.PokedexDemo.Move.swift */; };
B5A391B924E96F8500E7E8BD /* Modern.PokedexDemo.PokemonForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A391B824E96F8500E7E8BD /* Modern.PokedexDemo.PokemonForm.swift */; };
B5A391BB24E970A400E7E8BD /* Modern.PokedexDemo.PokemonType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A391BA24E970A400E7E8BD /* Modern.PokedexDemo.PokemonType.swift */; };
B5A391BD24E977E500E7E8BD /* Modern.PokedexDemo.Ability.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A391BC24E977E500E7E8BD /* Modern.PokedexDemo.Ability.swift */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
B5A3917324E698F900E7E8BD /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
B5A3916C24E698F900E7E8BD /* CoreStore.framework in Embed Frameworks */,
B5A3917224E698F900E7E8BD /* CoreStore.framework in Embed Frameworks */,
B5A3917024E698F900E7E8BD /* CoreStore.framework in Embed Frameworks */,
B5A3916E24E698F900E7E8BD /* CoreStore.framework in Embed Frameworks */,
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
B5A3911924E5429200E7E8BD /* Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Demo.app; sourceTree = BUILT_PRODUCTS_DIR; };
B5A3911C24E5429200E7E8BD /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
B5A3911E24E5429200E7E8BD /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
B5A3912024E5429200E7E8BD /* Menu.MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Menu.MainView.swift; sourceTree = "<group>"; };
B5A3912224E5429300E7E8BD /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
B5A3912524E5429300E7E8BD /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
B5A3912824E5429300E7E8BD /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
B5A3913324E6170500E7E8BD /* Menu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Menu.swift; sourceTree = "<group>"; };
B5A3913924E62A9A00E7E8BD /* Rakefile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; fileEncoding = 4; path = Rakefile; sourceTree = SOURCE_ROOT; };
B5A3914124E62D3900E7E8BD /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
B5A3915224E6537F00E7E8BD /* Menu.ItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Menu.ItemView.swift; sourceTree = "<group>"; };
B5A3915824E685EC00E7E8BD /* Classic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Classic.swift; sourceTree = "<group>"; };
B5A3915A24E685FE00E7E8BD /* Modern.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.swift; sourceTree = "<group>"; };
B5A3915D24E6922E00E7E8BD /* Modern.PlacemarksDemo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.PlacemarksDemo.swift; sourceTree = "<group>"; };
B5A3915F24E6925900E7E8BD /* Modern.PlacemarksDemo.MapView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.PlacemarksDemo.MapView.swift; sourceTree = "<group>"; };
B5A3916124E697BA00E7E8BD /* Modern.PlacemarksDemo.MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.PlacemarksDemo.MainView.swift; sourceTree = "<group>"; };
B5A3916424E698C700E7E8BD /* Modern.PlacemarksDemo.Place.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.PlacemarksDemo.Place.swift; sourceTree = "<group>"; };
B5A3916724E698F900E7E8BD /* CoreStore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = CoreStore.framework; sourceTree = BUILT_PRODUCTS_DIR; };
B5A3916824E698F900E7E8BD /* CoreStore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = CoreStore.framework; sourceTree = BUILT_PRODUCTS_DIR; };
B5A3916924E698F900E7E8BD /* CoreStore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = CoreStore.framework; sourceTree = BUILT_PRODUCTS_DIR; };
B5A3916A24E698F900E7E8BD /* CoreStore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = CoreStore.framework; sourceTree = BUILT_PRODUCTS_DIR; };
B5A3917424E6990200E7E8BD /* MapKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MapKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.6.sdk/System/Library/Frameworks/MapKit.framework; sourceTree = DEVELOPER_DIR; };
B5A3917624E6990700E7E8BD /* CoreLocation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreLocation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.6.sdk/System/Library/Frameworks/CoreLocation.framework; sourceTree = DEVELOPER_DIR; };
B5A3917824E6991600E7E8BD /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.6.sdk/System/Library/Frameworks/SwiftUI.framework; sourceTree = DEVELOPER_DIR; };
B5A3917B24E6A76C00E7E8BD /* LazyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LazyView.swift; sourceTree = "<group>"; };
B5A3917D24E7728400E7E8BD /* Modern.PlacemarksDemo.Geocoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.PlacemarksDemo.Geocoder.swift; sourceTree = "<group>"; };
B5A3917F24E787D900E7E8BD /* InstructionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstructionsView.swift; sourceTree = "<group>"; };
B5A3918224E7A21800E7E8BD /* Modern.TimeZonesDemo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.TimeZonesDemo.swift; sourceTree = "<group>"; };
B5A3918524E7A54A00E7E8BD /* Modern.TimeZonesDemo.TimeZone.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.TimeZonesDemo.TimeZone.swift; sourceTree = "<group>"; };
B5A3918724E7A8F900E7E8BD /* Modern.TimeZonesDemo.MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.TimeZonesDemo.MainView.swift; sourceTree = "<group>"; };
B5A3918924E7AD1800E7E8BD /* Modern.TimeZonesDemo.ListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.TimeZonesDemo.ListView.swift; sourceTree = "<group>"; };
B5A3918B24E7B44B00E7E8BD /* Modern.TimeZonesDemo.ItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.TimeZonesDemo.ItemView.swift; sourceTree = "<group>"; };
B5A3918E24E7E06500E7E8BD /* Modern.ColorsDemo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.ColorsDemo.swift; sourceTree = "<group>"; };
B5A3919124E7E0C600E7E8BD /* Modern.ColorsDemo.Palette.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.ColorsDemo.Palette.swift; sourceTree = "<group>"; };
B5A3919324E7E36700E7E8BD /* Modern.ColorsDemo.SwiftUI.ListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.ColorsDemo.SwiftUI.ListView.swift; sourceTree = "<group>"; };
B5A3919524E7E4AC00E7E8BD /* Modern.ColorsDemo.SwiftUI.ItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.ColorsDemo.SwiftUI.ItemView.swift; sourceTree = "<group>"; };
B5A3919724E7E67000E7E8BD /* Modern.ColorsDemo.Filter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.ColorsDemo.Filter.swift; sourceTree = "<group>"; };
B5A3919924E8207A00E7E8BD /* Modern.ColorsDemo.SwiftUI.DetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.ColorsDemo.SwiftUI.DetailView.swift; sourceTree = "<group>"; };
B5A3919D24E8EEB600E7E8BD /* Modern.ColorsDemo.SwiftUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.ColorsDemo.SwiftUI.swift; sourceTree = "<group>"; };
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>"; };
B5A391A724E90F1000E7E8BD /* UIImage+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+Extensions.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>"; };
B5A391B024E96AF600E7E8BD /* Modern.PokedexDemo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.PokedexDemo.swift; sourceTree = "<group>"; };
B5A391B324E96C0A00E7E8BD /* Modern.PokedexDemo.PokemonSpecies.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.PokedexDemo.PokemonSpecies.swift; sourceTree = "<group>"; };
B5A391B524E96C5500E7E8BD /* Modern.PokedexDemo.Move.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.PokedexDemo.Move.swift; sourceTree = "<group>"; };
B5A391B824E96F8500E7E8BD /* Modern.PokedexDemo.PokemonForm.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.PokedexDemo.PokemonForm.swift; sourceTree = "<group>"; };
B5A391BA24E970A400E7E8BD /* Modern.PokedexDemo.PokemonType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.PokedexDemo.PokemonType.swift; sourceTree = "<group>"; };
B5A391BC24E977E500E7E8BD /* Modern.PokedexDemo.Ability.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.PokedexDemo.Ability.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
B5A3911624E5429200E7E8BD /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
B5A3917724E6990700E7E8BD /* CoreLocation.framework in Frameworks */,
B5A3916B24E698F900E7E8BD /* CoreStore.framework in Frameworks */,
B5A3917124E698F900E7E8BD /* CoreStore.framework in Frameworks */,
B5A3917924E6991600E7E8BD /* SwiftUI.framework in Frameworks */,
B5A3917524E6990200E7E8BD /* MapKit.framework in Frameworks */,
B5A3916F24E698F900E7E8BD /* CoreStore.framework in Frameworks */,
B5A3916D24E698F900E7E8BD /* CoreStore.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
B5A3910E24E5424E00E7E8BD = {
isa = PBXGroup;
children = (
B5A3913D24E62C6C00E7E8BD /* Metadata */,
B5A3911B24E5429200E7E8BD /* Sources */,
B5A3913E24E62CB200E7E8BD /* Resources */,
B5A3911A24E5429200E7E8BD /* Products */,
B5A3916624E698F900E7E8BD /* Frameworks */,
);
sourceTree = "<group>";
};
B5A3911A24E5429200E7E8BD /* Products */ = {
isa = PBXGroup;
children = (
B5A3911924E5429200E7E8BD /* Demo.app */,
);
name = Products;
sourceTree = "<group>";
};
B5A3911B24E5429200E7E8BD /* Sources */ = {
isa = PBXGroup;
children = (
B5A3911C24E5429200E7E8BD /* AppDelegate.swift */,
B5A3911E24E5429200E7E8BD /* SceneDelegate.swift */,
B5A3915524E6858A00E7E8BD /* Demos */,
B5A3917A24E6A75F00E7E8BD /* Helpers */,
);
path = Sources;
sourceTree = "<group>";
};
B5A3912424E5429300E7E8BD /* Preview Content */ = {
isa = PBXGroup;
children = (
B5A3912524E5429300E7E8BD /* Preview Assets.xcassets */,
);
path = "Preview Content";
sourceTree = "<group>";
};
B5A3913D24E62C6C00E7E8BD /* Metadata */ = {
isa = PBXGroup;
children = (
B5A3913924E62A9A00E7E8BD /* Rakefile */,
B5A3914124E62D3900E7E8BD /* Info.plist */,
);
name = Metadata;
sourceTree = "<group>";
};
B5A3913E24E62CB200E7E8BD /* Resources */ = {
isa = PBXGroup;
children = (
B5A3912724E5429300E7E8BD /* LaunchScreen.storyboard */,
B5A3912224E5429300E7E8BD /* Images.xcassets */,
B5A3912424E5429300E7E8BD /* Preview Content */,
);
path = Resources;
sourceTree = "<group>";
};
B5A3915424E6857F00E7E8BD /* Menu */ = {
isa = PBXGroup;
children = (
B5A3913324E6170500E7E8BD /* Menu.swift */,
B5A3912024E5429200E7E8BD /* Menu.MainView.swift */,
B5A3915224E6537F00E7E8BD /* Menu.ItemView.swift */,
);
path = Menu;
sourceTree = "<group>";
};
B5A3915524E6858A00E7E8BD /* Demos */ = {
isa = PBXGroup;
children = (
B5A3915624E685B700E7E8BD /* Modern */,
B5A3915724E685D300E7E8BD /* Classic */,
);
path = Demos;
sourceTree = "<group>";
};
B5A3915624E685B700E7E8BD /* Modern */ = {
isa = PBXGroup;
children = (
B5A3915A24E685FE00E7E8BD /* Modern.swift */,
B5A3915C24E6921E00E7E8BD /* PlacemarksDemo */,
B5A3918124E7A1EF00E7E8BD /* TimeZonesDemo */,
B5A3918D24E7DE7A00E7E8BD /* ColorsDemo */,
B5A391AF24E96AD600E7E8BD /* PokedexDemo */,
);
path = Modern;
sourceTree = "<group>";
};
B5A3915724E685D300E7E8BD /* Classic */ = {
isa = PBXGroup;
children = (
B5A3915824E685EC00E7E8BD /* Classic.swift */,
);
path = Classic;
sourceTree = "<group>";
};
B5A3915C24E6921E00E7E8BD /* PlacemarksDemo */ = {
isa = PBXGroup;
children = (
B5A3915D24E6922E00E7E8BD /* Modern.PlacemarksDemo.swift */,
B5A3916124E697BA00E7E8BD /* Modern.PlacemarksDemo.MainView.swift */,
B5A3915F24E6925900E7E8BD /* Modern.PlacemarksDemo.MapView.swift */,
B5A3917D24E7728400E7E8BD /* Modern.PlacemarksDemo.Geocoder.swift */,
B5A3916324E698B300E7E8BD /* Models */,
);
path = PlacemarksDemo;
sourceTree = "<group>";
};
B5A3916324E698B300E7E8BD /* Models */ = {
isa = PBXGroup;
children = (
B5A3916424E698C700E7E8BD /* Modern.PlacemarksDemo.Place.swift */,
);
name = Models;
sourceTree = "<group>";
};
B5A3916624E698F900E7E8BD /* Frameworks */ = {
isa = PBXGroup;
children = (
B5A3917824E6991600E7E8BD /* SwiftUI.framework */,
B5A3917624E6990700E7E8BD /* CoreLocation.framework */,
B5A3917424E6990200E7E8BD /* MapKit.framework */,
B5A3916724E698F900E7E8BD /* CoreStore.framework */,
B5A3916824E698F900E7E8BD /* CoreStore.framework */,
B5A3916924E698F900E7E8BD /* CoreStore.framework */,
B5A3916A24E698F900E7E8BD /* CoreStore.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
B5A3917A24E6A75F00E7E8BD /* Helpers */ = {
isa = PBXGroup;
children = (
B5A3917B24E6A76C00E7E8BD /* LazyView.swift */,
B5A3917F24E787D900E7E8BD /* InstructionsView.swift */,
B5A391A724E90F1000E7E8BD /* UIImage+Extensions.swift */,
B5A3915424E6857F00E7E8BD /* Menu */,
);
path = Helpers;
sourceTree = "<group>";
};
B5A3918124E7A1EF00E7E8BD /* TimeZonesDemo */ = {
isa = PBXGroup;
children = (
B5A3918224E7A21800E7E8BD /* Modern.TimeZonesDemo.swift */,
B5A3918724E7A8F900E7E8BD /* Modern.TimeZonesDemo.MainView.swift */,
B5A3918924E7AD1800E7E8BD /* Modern.TimeZonesDemo.ListView.swift */,
B5A3918B24E7B44B00E7E8BD /* Modern.TimeZonesDemo.ItemView.swift */,
B5A3918424E7A53300E7E8BD /* Models */,
);
path = TimeZonesDemo;
sourceTree = "<group>";
};
B5A3918424E7A53300E7E8BD /* Models */ = {
isa = PBXGroup;
children = (
B5A3918524E7A54A00E7E8BD /* Modern.TimeZonesDemo.TimeZone.swift */,
);
name = Models;
sourceTree = "<group>";
};
B5A3918D24E7DE7A00E7E8BD /* ColorsDemo */ = {
isa = PBXGroup;
children = (
B5A3918E24E7E06500E7E8BD /* Modern.ColorsDemo.swift */,
B5A3919724E7E67000E7E8BD /* Modern.ColorsDemo.Filter.swift */,
B5A391A524E8F4EA00E7E8BD /* Modern.ColorsDemo.MainView.swift */,
B5A3919C24E8EE9000E7E8BD /* UIKit */,
B5A3919B24E8EE8100E7E8BD /* SwiftUI */,
B5A3919024E7E0B000E7E8BD /* Models */,
);
path = ColorsDemo;
sourceTree = "<group>";
};
B5A3919024E7E0B000E7E8BD /* Models */ = {
isa = PBXGroup;
children = (
B5A3919124E7E0C600E7E8BD /* Modern.ColorsDemo.Palette.swift */,
);
name = Models;
sourceTree = "<group>";
};
B5A3919B24E8EE8100E7E8BD /* SwiftUI */ = {
isa = PBXGroup;
children = (
B5A3919D24E8EEB600E7E8BD /* Modern.ColorsDemo.SwiftUI.swift */,
B5A3919324E7E36700E7E8BD /* Modern.ColorsDemo.SwiftUI.ListView.swift */,
B5A3919524E7E4AC00E7E8BD /* Modern.ColorsDemo.SwiftUI.ItemView.swift */,
B5A3919924E8207A00E7E8BD /* Modern.ColorsDemo.SwiftUI.DetailView.swift */,
);
name = SwiftUI;
sourceTree = "<group>";
};
B5A3919C24E8EE9000E7E8BD /* UIKit */ = {
isa = PBXGroup;
children = (
B5A3919F24E8F00A00E7E8BD /* Modern.ColorsDemo.UIKit.swift */,
B5A391A324E8F04300E7E8BD /* Modern.ColorsDemo.UIKit.ListView.swift */,
B5A391A124E8F01F00E7E8BD /* Modern.ColorsDemo.UIKit.ListViewController.swift */,
B5A391A924E9104300E7E8BD /* Modern.ColorsDemo.UIKit.ItemCell.swift */,
B5A391AB24E9143B00E7E8BD /* Modern.ColorsDemo.UIKit.DetailView.swift */,
B5A391AD24E9150F00E7E8BD /* Modern.ColorsDemo.UIKit.DetailViewController.swift */,
);
name = UIKit;
sourceTree = "<group>";
};
B5A391AF24E96AD600E7E8BD /* PokedexDemo */ = {
isa = PBXGroup;
children = (
B5A391B024E96AF600E7E8BD /* Modern.PokedexDemo.swift */,
B5A391B224E96B7400E7E8BD /* Models */,
);
path = PokedexDemo;
sourceTree = "<group>";
};
B5A391B224E96B7400E7E8BD /* Models */ = {
isa = PBXGroup;
children = (
B5A391B324E96C0A00E7E8BD /* Modern.PokedexDemo.PokemonSpecies.swift */,
B5A391B824E96F8500E7E8BD /* Modern.PokedexDemo.PokemonForm.swift */,
B5A391B524E96C5500E7E8BD /* Modern.PokedexDemo.Move.swift */,
B5A391BC24E977E500E7E8BD /* Modern.PokedexDemo.Ability.swift */,
B5A391B724E96E8600E7E8BD /* Attributes */,
);
name = Models;
sourceTree = "<group>";
};
B5A391B724E96E8600E7E8BD /* Attributes */ = {
isa = PBXGroup;
children = (
B5A391BA24E970A400E7E8BD /* Modern.PokedexDemo.PokemonType.swift */,
);
name = Attributes;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
B5A3911824E5429200E7E8BD /* Demo */ = {
isa = PBXNativeTarget;
buildConfigurationList = B5A3912B24E5429300E7E8BD /* Build configuration list for PBXNativeTarget "Demo" */;
buildPhases = (
B5A3911524E5429200E7E8BD /* Sources */,
B5A3911624E5429200E7E8BD /* Frameworks */,
B5A3911724E5429200E7E8BD /* Resources */,
B5A3917324E698F900E7E8BD /* Embed Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = Demo;
productName = Demo;
productReference = B5A3911924E5429200E7E8BD /* Demo.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
B5A3910F24E5424E00E7E8BD /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 1160;
LastUpgradeCheck = 1160;
TargetAttributes = {
B5A3911824E5429200E7E8BD = {
CreatedOnToolsVersion = 11.6;
};
};
};
buildConfigurationList = B5A3911224E5424E00E7E8BD /* Build configuration list for PBXProject "Demo" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = B5A3910E24E5424E00E7E8BD;
productRefGroup = B5A3911A24E5429200E7E8BD /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
B5A3911824E5429200E7E8BD /* Demo */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
B5A3911724E5429200E7E8BD /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
B5A3912924E5429300E7E8BD /* LaunchScreen.storyboard in Resources */,
B5A3912624E5429300E7E8BD /* Preview Assets.xcassets in Resources */,
B5A3912324E5429300E7E8BD /* Images.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
B5A3911524E5429200E7E8BD /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
B5A3918824E7A8F900E7E8BD /* Modern.TimeZonesDemo.MainView.swift in Sources */,
B5A391AE24E9150F00E7E8BD /* Modern.ColorsDemo.UIKit.DetailViewController.swift in Sources */,
B5A391A224E8F01F00E7E8BD /* Modern.ColorsDemo.UIKit.ListViewController.swift in Sources */,
B5A391AA24E9104300E7E8BD /* Modern.ColorsDemo.UIKit.ItemCell.swift in Sources */,
B5A3917C24E6A76C00E7E8BD /* LazyView.swift in Sources */,
B5A3918324E7A21800E7E8BD /* Modern.TimeZonesDemo.swift in Sources */,
B5A3915324E6537F00E7E8BD /* Menu.ItemView.swift in Sources */,
B5A3911D24E5429200E7E8BD /* AppDelegate.swift in Sources */,
B5A391A024E8F00A00E7E8BD /* Modern.ColorsDemo.UIKit.swift in Sources */,
B5A3913424E6170500E7E8BD /* Menu.swift in Sources */,
B5A391B924E96F8500E7E8BD /* Modern.PokedexDemo.PokemonForm.swift in Sources */,
B5A3918624E7A54A00E7E8BD /* Modern.TimeZonesDemo.TimeZone.swift in Sources */,
B5A3915B24E685FE00E7E8BD /* Modern.swift in Sources */,
B5A391BB24E970A400E7E8BD /* Modern.PokedexDemo.PokemonType.swift in Sources */,
B5A3919624E7E4AC00E7E8BD /* Modern.ColorsDemo.SwiftUI.ItemView.swift in Sources */,
B5A3919A24E8207A00E7E8BD /* Modern.ColorsDemo.SwiftUI.DetailView.swift in Sources */,
B5A3916024E6925900E7E8BD /* Modern.PlacemarksDemo.MapView.swift in Sources */,
B5A391B124E96AF600E7E8BD /* Modern.PokedexDemo.swift in Sources */,
B5A3918A24E7AD1800E7E8BD /* Modern.TimeZonesDemo.ListView.swift in Sources */,
B5A3911F24E5429200E7E8BD /* SceneDelegate.swift in Sources */,
B5A3915924E685EC00E7E8BD /* Classic.swift in Sources */,
B5A3919824E7E67000E7E8BD /* Modern.ColorsDemo.Filter.swift in Sources */,
B5A391B624E96C5500E7E8BD /* Modern.PokedexDemo.Move.swift in Sources */,
B5A3916224E697BA00E7E8BD /* Modern.PlacemarksDemo.MainView.swift in Sources */,
B5A3917E24E7728400E7E8BD /* Modern.PlacemarksDemo.Geocoder.swift in Sources */,
B5A391A424E8F04300E7E8BD /* Modern.ColorsDemo.UIKit.ListView.swift in Sources */,
B5A391AC24E9143B00E7E8BD /* Modern.ColorsDemo.UIKit.DetailView.swift in Sources */,
B5A3919224E7E0C600E7E8BD /* Modern.ColorsDemo.Palette.swift in Sources */,
B5A3919424E7E36700E7E8BD /* Modern.ColorsDemo.SwiftUI.ListView.swift in Sources */,
B5A3918F24E7E06500E7E8BD /* Modern.ColorsDemo.swift in Sources */,
B5A3915E24E6922E00E7E8BD /* Modern.PlacemarksDemo.swift in Sources */,
B5A391B424E96C0A00E7E8BD /* Modern.PokedexDemo.PokemonSpecies.swift in Sources */,
B5A391A824E90F1000E7E8BD /* UIImage+Extensions.swift in Sources */,
B5A3918024E787D900E7E8BD /* InstructionsView.swift in Sources */,
B5A3918C24E7B44B00E7E8BD /* Modern.TimeZonesDemo.ItemView.swift in Sources */,
B5A3919E24E8EEB600E7E8BD /* Modern.ColorsDemo.SwiftUI.swift in Sources */,
B5A391A624E8F4EA00E7E8BD /* Modern.ColorsDemo.MainView.swift in Sources */,
B5A3916524E698C700E7E8BD /* Modern.PlacemarksDemo.Place.swift in Sources */,
B5A391BD24E977E500E7E8BD /* Modern.PokedexDemo.Ability.swift in Sources */,
B5A3912124E5429200E7E8BD /* Menu.MainView.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
B5A3912724E5429300E7E8BD /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
B5A3912824E5429300E7E8BD /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
B5A3911324E5424E00E7E8BD /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
};
name = Debug;
};
B5A3911424E5424E00E7E8BD /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
};
name = Release;
};
B5A3912C24E5429300E7E8BD /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_APPICON_NAME = appIcon;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_ASSET_PATHS = "\"Resources/Preview Content\"";
DEVELOPMENT_TEAM = 2JT32EJ5BH;
ENABLE_PREVIEWS = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
INFOPLIST_FILE = Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.6;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.johnestropia.Demo;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
B5A3912D24E5429300E7E8BD /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_APPICON_NAME = appIcon;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_ASSET_PATHS = "\"Resources/Preview Content\"";
DEVELOPMENT_TEAM = 2JT32EJ5BH;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_PREVIEWS = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
INFOPLIST_FILE = Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.6;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.johnestropia.Demo;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
B5A3911224E5424E00E7E8BD /* Build configuration list for PBXProject "Demo" */ = {
isa = XCConfigurationList;
buildConfigurations = (
B5A3911324E5424E00E7E8BD /* Debug */,
B5A3911424E5424E00E7E8BD /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
B5A3912B24E5429300E7E8BD /* Build configuration list for PBXNativeTarget "Demo" */ = {
isa = XCConfigurationList;
buildConfigurations = (
B5A3912C24E5429300E7E8BD /* Debug */,
B5A3912D24E5429300E7E8BD /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = B5A3910F24E5424E00E7E8BD /* Project object */;
}

View File

@@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1160"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B5A3911824E5429200E7E8BD"
BuildableName = "Demo.app"
BlueprintName = "Demo"
ReferencedContainer = "container:Demo.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B5A3911824E5429200E7E8BD"
BuildableName = "Demo.app"
BlueprintName = "Demo"
ReferencedContainer = "container:Demo.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B5A3911824E5429200E7E8BD"
BuildableName = "Demo.app"
BlueprintName = "Demo"
ReferencedContainer = "container:Demo.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SchemeUserState</key>
<dict>
<key>Demo.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>5</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>B5A3911824E5429200E7E8BD</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict>
</plist>

70
Demo/Info.plist Normal file
View File

@@ -0,0 +1,70 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneConfigurationName</key>
<string>Default Configuration</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
</dict>
</array>
</dict>
</dict>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UIStatusBarTintParameters</key>
<dict>
<key>UINavigationBar</key>
<dict>
<key>Style</key>
<string>UIBarStyleDefault</string>
<key>Translucent</key>
<false/>
</dict>
</dict>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>

42
Demo/Rakefile Normal file
View File

@@ -0,0 +1,42 @@
# coding: utf-8
task :format do
require 'xcodeproj'
require 'fileutils'
project_path = 'Demo.xcodeproj'
ignore_targets = []
# http://www.rubydoc.info/github/CocoaPods/Xcodeproj
project = Xcodeproj::Project.open(project_path)
validTargets = project.targets.select { |target| target.respond_to?(:product_type) }
validTargets.each do |target|
if ignore_targets.include?(target.display_name)
next
end
case target.product_type
when 'com.apple.product-type.application', 'com.apple.product-type.framework'
target.source_build_phase.files.sort! do |f1, f2|
result = (f1.display_name.count "+") <=> (f2.display_name.count "+")
if result != 0
next -result
end
result = (f1.display_name.count "-") <=> (f2.display_name.count "-")
if result != 0
next -result
end
result = (f1.display_name.count ".") <=> (f2.display_name.count ".")
if result != 0
next result
end
(f1.display_name <=> f2.display_name)
end
end
end
project.save()
end

View File

@@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16097.2" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
<capability name="Named colors" minToolsVersion="9.0"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController interfaceStyle="dark" id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Bp2-lt-3DL">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text=" Copyright © 2020 John Rommel Estropia. All rights reserved." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="Yn3-8H-uzI">
<rect key="frame" x="20" y="827" width="374" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" name="foreground"/>
<nil key="highlightedColor"/>
</label>
<imageView userInteractionEnabled="NO" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="coreStoreIcon" translatesAutoresizingMaskIntoConstraints="NO" id="IrK-8p-pit">
<rect key="frame" x="37" y="143.5" width="340" height="340"/>
</imageView>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="CoreStore" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="8Vu-0U-3hd">
<rect key="frame" x="20" y="503.5" width="374" height="57.5"/>
<fontDescription key="fontDescription" name="HelveticaNeue-UltraLight" family="Helvetica Neue" pointSize="50"/>
<color key="textColor" name="foreground"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" name="background"/>
<constraints>
<constraint firstItem="Yn3-8H-uzI" firstAttribute="leading" secondItem="Bp2-lt-3DL" secondAttribute="leading" constant="20" symbolic="YES" id="7Dq-xP-k2v"/>
<constraint firstItem="IrK-8p-pit" firstAttribute="centerY" secondItem="Bp2-lt-3DL" secondAttribute="centerY" multiplier="0.7" id="HUz-XL-l27"/>
<constraint firstItem="IrK-8p-pit" firstAttribute="centerX" secondItem="Bp2-lt-3DL" secondAttribute="centerX" id="TSf-yM-hl1"/>
<constraint firstAttribute="centerX" secondItem="8Vu-0U-3hd" secondAttribute="centerX" id="TX2-HT-cKs"/>
<constraint firstItem="Z3i-EZ-UGs" firstAttribute="bottom" secondItem="Yn3-8H-uzI" secondAttribute="bottom" constant="14" id="hAb-SJ-Qnm"/>
<constraint firstAttribute="centerX" secondItem="Yn3-8H-uzI" secondAttribute="centerX" id="pNf-eo-RXZ"/>
<constraint firstItem="8Vu-0U-3hd" firstAttribute="leading" secondItem="Bp2-lt-3DL" secondAttribute="leading" constant="20" symbolic="YES" id="pef-yD-C5e"/>
<constraint firstItem="8Vu-0U-3hd" firstAttribute="top" secondItem="IrK-8p-pit" secondAttribute="bottom" constant="20" id="xQP-tq-hNL"/>
</constraints>
<viewLayoutGuide key="safeArea" id="Z3i-EZ-UGs"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
<resources>
<image name="coreStoreIcon" width="340" height="340"/>
<namedColor name="background">
<color red="0.15700000524520874" green="0.2199999988079071" blue="0.29399999976158142" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</namedColor>
<namedColor name="foreground">
<color red="0.90600001811981201" green="0.92500001192092896" blue="0.92900002002716064" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</namedColor>
</resources>
</document>

View File

@@ -0,0 +1,115 @@
{
"images" : [
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "20x20"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "20x20"
},
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "29x29"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "29x29"
},
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "40x40"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "40x40"
},
{
"filename" : "Icon-60@2x.png",
"idiom" : "iphone",
"scale" : "2x",
"size" : "60x60"
},
{
"filename" : "Icon-60@3x-1.png",
"idiom" : "iphone",
"scale" : "3x",
"size" : "60x60"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "20x20"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "20x20"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "29x29"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "29x29"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "40x40"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "40x40"
},
{
"filename" : "Icon-76.png",
"idiom" : "ipad",
"scale" : "1x",
"size" : "76x76"
},
{
"filename" : "Icon-76@2x.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "76x76"
},
{
"filename" : "Icon-83.5@2x.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "83.5x83.5"
},
{
"filename" : "Mask + Oval 1 + Oval 1 + Oval 1.png",
"idiom" : "ios-marketing",
"scale" : "1x",
"size" : "1024x1024"
},
{
"idiom" : "car",
"scale" : "2x",
"size" : "60x60"
},
{
"filename" : "Icon-60@3x.png",
"idiom" : "car",
"scale" : "3x",
"size" : "60x60"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

View File

@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "CoreStoreIcon.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "original"
}
}

View File

@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,33 @@
//
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
import UIKit
// MARK: - AppDelegate
@UIApplicationMain
@objc final class AppDelegate: UIResponder, UIApplicationDelegate {
// MARK: UIApplicationDelegate
@objc dynamic func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
) -> Bool {
return true
}
@objc dynamic func application(
_ application: UIApplication,
configurationForConnecting connectingSceneSession: UISceneSession,
options: UIScene.ConnectionOptions
) -> UISceneConfiguration {
return UISceneConfiguration(
name: "Default Configuration",
sessionRole: connectingSceneSession.role
)
}
}

View File

@@ -0,0 +1,10 @@
//
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
// MARK: - Classic
/**
Sample usages for `NSManagedObject` subclasses
*/
enum Classic {}

View File

@@ -0,0 +1,36 @@
//
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
import CoreStore
// MARK: - Modern.ColorsDemo
extension Modern.ColorsDemo {
// MARK: - Modern.ColorsDemo.Filter
enum Filter: String, CaseIterable {
case all = "All Colors"
case light = "Light Colors"
case dark = "Dark Colors"
func next() -> Filter {
let allCases = Self.allCases
return allCases[(allCases.firstIndex(of: self)! + 1) % allCases.count]
}
func whereClause() -> Where<Modern.ColorsDemo.Palette> {
switch self {
case .all: return .init()
case .light: return (\Modern.ColorsDemo.Palette.$brightness >= 0.9)
case .dark: return (\Modern.ColorsDemo.Palette.$brightness <= 0.4)
}
}
}
}

View File

@@ -0,0 +1,190 @@
//
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
import CoreStore
import SwiftUI
// MARK: - Modern.ColorsDemo
extension Modern.ColorsDemo {
// MARK: - Modern.ColorsDemo.MainView
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(
listView: @escaping (
_ listPublisher: ListPublisher<Modern.ColorsDemo.Palette>,
_ onPaletteTapped: @escaping (ObjectPublisher<Modern.ColorsDemo.Palette>) -> Void
) -> ListView,
detailView: @escaping (ObjectPublisher<Modern.ColorsDemo.Palette>) -> DetailView) {
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 })
.navigationBarTitle(
Text("Colors (\(listPublisher.snapshot.numberOfItems) objects)")
)
.frame(minHeight: 0, maxHeight: .infinity)
detailView
.edgesIgnoringSafeArea(.all)
.frame(minHeight: 0, maxHeight: .infinity)
}
.navigationBarItems(
leading: HStack {
EditButton()
Button(
action: { self.clearColors() },
label: { Text("Clear") }
)
},
trailing: HStack {
Button(
action: { self.changeFilter() },
label: { Text(self.filter.rawValue) }
)
Button(
action: { self.shuffleColors() },
label: { Text("Shuffle") }
)
Button(
action: { self.addColor() },
label: { Text("Add") }
)
}
)
}
// MARK: Private
private let listView: (
_ listPublisher: ListPublisher<Modern.ColorsDemo.Palette>,
_ onPaletteTapped: @escaping (ObjectPublisher<Modern.ColorsDemo.Palette>) -> Void
) -> ListView
private let detailView: (
_ objectPublisher: ObjectPublisher<Modern.ColorsDemo.Palette>
) -> DetailView
@State
private var selectedPalette: ObjectPublisher<Modern.ColorsDemo.Palette>?
@Binding
private var filter: Modern.ColorsDemo.Filter
private func changeFilter() {
Modern.ColorsDemo.filter = Modern.ColorsDemo.filter.next()
}
private func clearColors() {
Modern.ColorsDemo.dataStack.perform(
asynchronous: { transaction in
try transaction.deleteAll(From<Modern.ColorsDemo.Palette>())
},
completion: { _ in }
)
}
private func addColor() {
Modern.ColorsDemo.dataStack.perform(
asynchronous: { transaction in
_ = transaction.create(Into<Modern.ColorsDemo.Palette>())
},
completion: { _ in }
)
}
private func shuffleColors() {
Modern.ColorsDemo.dataStack.perform(
asynchronous: { transaction in
for palette in try transaction.fetchAll(From<Modern.ColorsDemo.Palette>()) {
palette.setRandomHue()
}
},
completion: { _ in }
)
}
}
}
#if DEBUG
struct _Demo_Modern_ColorsDemo_MainView_Preview: PreviewProvider {
// MARK: PreviewProvider
static var previews: some View {
let minimumSamples = 10
try! Modern.ColorsDemo.dataStack.perform(
synchronous: { transaction in
let missing = minimumSamples
- (try transaction.fetchCount(From<Modern.ColorsDemo.Palette>()))
guard missing > 0 else {
return
}
for _ in 0..<missing {
let palette = transaction.create(Into<Modern.ColorsDemo.Palette>())
palette.setRandomHue()
}
}
)
return Modern.ColorsDemo.MainView(
listView: { listPublisher, onPaletteTapped in
Modern.ColorsDemo.SwiftUI.ListView(
listPublisher: listPublisher,
onPaletteTapped: onPaletteTapped
)
},
detailView: { objectPublisher in
Modern.ColorsDemo.SwiftUI.DetailView(objectPublisher)
}
)
}
}
#endif

View File

@@ -0,0 +1,143 @@
//
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
import UIKit
import CoreStore
// MARK: - Modern.ColorsDemo
extension Modern.ColorsDemo {
// MARK: - Modern.ColorsDemo.Palette
final class Palette: CoreStoreObject {
// MARK: Internal
@Field.Stored(
"hue",
customSetter: { object, field, value in
Palette.resetVirtualProperties(object)
field.primitiveValue = value
},
dynamicInitialValue: { Palette.randomHue() }
)
var hue: Float
@Field.Stored(
"saturation",
customSetter: { object, field, value in
Palette.resetVirtualProperties(object)
field.primitiveValue = value
},
dynamicInitialValue: { Palette.randomSaturation() }
)
var saturation: Float
@Field.Stored(
"brightness",
customSetter: { object, field, value in
Palette.resetVirtualProperties(object)
field.primitiveValue = value
},
dynamicInitialValue: { Palette.randomBrightness() }
)
var brightness: Float
@Field.Virtual(
"colorName",
customGetter: { object, field in
if let colorName = field.primitiveValue {
return colorName
}
let colorName: String
switch object.$hue.value * 359 {
case 0 ..< 20: colorName = "Lower Reds"
case 20 ..< 57: colorName = "Oranges and Browns"
case 57 ..< 90: colorName = "Yellow-Greens"
case 90 ..< 159: colorName = "Greens"
case 159 ..< 197: colorName = "Blue-Greens"
case 197 ..< 241: colorName = "Blues"
case 241 ..< 297: colorName = "Violets"
case 297 ..< 331: colorName = "Magentas"
default: colorName = "Upper Reds"
}
field.primitiveValue = colorName
return colorName
}
)
var colorName: String
@Field.Virtual(
"color",
customGetter: { object, field in
if let color = field.primitiveValue {
return color
}
let color = UIColor(
hue: CGFloat(object.$hue.value),
saturation: CGFloat(object.$saturation.value),
brightness: CGFloat(object.$brightness.value),
alpha: 1.0
)
field.primitiveValue = color
return color
}
)
var color: UIColor
@Field.Virtual(
"colorText",
customGetter: { object, field in
if let colorText = field.primitiveValue {
return colorText
}
let colorText = "H: \(object.$hue.value * 359)˚, S: \(round(object.$saturation.value * 100.0))%, B: \(round(object.$brightness.value * 100.0))%"
field.primitiveValue = colorText
return colorText
}
)
var colorText: String
func setRandomHue() {
self.hue = Self.randomHue()
}
// MARK: Private
private static func resetVirtualProperties(_ object: ObjectProxy<Modern.ColorsDemo.Palette>) {
object.$colorName.primitiveValue = nil
object.$color.primitiveValue = nil
object.$colorText.primitiveValue = nil
}
private static func randomHue() -> Float {
return Float.random(in: 0.0 ... 1.0)
}
private static func randomSaturation() -> Float {
return Float.random(in: 0.0 ... 1.0)
}
private static func randomBrightness() -> Float {
return Float.random(in: 0.0 ... 1.0)
}
}
}

View File

@@ -0,0 +1,166 @@
//
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
import Combine
import CoreStore
import SwiftUI
// MARK: - Modern.ColorsDemo.SwiftUI
extension Modern.ColorsDemo.SwiftUI {
// MARK: - Modern.ColorsDemo.SwiftUI.DetailView
struct DetailView: View {
/**
Sample 1: Setting an `ObjectPublisher` declared as an `@ObservedObject`
*/
@ObservedObject
private var palette: ObjectPublisher<Modern.ColorsDemo.Palette>
/**
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.
*/
@Binding
private var hue: Float
@Binding
private var saturation: Float
@Binding
private var brightness: Float
init(_ palette: ObjectPublisher<Modern.ColorsDemo.Palette>) {
self.palette = palette
self._hue = Binding(
get: { palette.hue ?? 0 },
set: { percentage in
Modern.ColorsDemo.dataStack.perform(
asynchronous: { (transaction) in
let palette = palette.asEditable(in: transaction)
palette?.hue = percentage
},
completion: { _ in }
)
}
)
self._saturation = Binding(
get: { palette.saturation ?? 0 },
set: { percentage in
Modern.ColorsDemo.dataStack.perform(
asynchronous: { (transaction) in
let palette = palette.asEditable(in: transaction)
palette?.saturation = percentage
},
completion: { _ in }
)
}
)
self._brightness = Binding(
get: { palette.brightness ?? 0 },
set: { percentage in
Modern.ColorsDemo.dataStack.perform(
asynchronous: { (transaction) in
let palette = palette.asEditable(in: transaction)
palette?.brightness = percentage
},
completion: { _ in }
)
}
)
}
// MARK: View
var body: some View {
guard let snapshot = self.palette.snapshot else {
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
)
}
}
.foregroundColor(Color(.sRGB, white: 0, opacity: 0.8))
.padding()
}
.fixedSize(horizontal: false, vertical: true)
.padding()
.padding(geometry.safeAreaInsets)
}
}
)
}
}
}
#if DEBUG
struct _Demo_Modern_ColorsDemo_SwiftUI_DetailView_Preview: PreviewProvider {
// MARK: PreviewProvider
static var previews: some View {
try! Modern.ColorsDemo.dataStack.perform(
synchronous: { transaction in
guard (try transaction.fetchCount(From<Modern.ColorsDemo.Palette>())) <= 0 else {
return
}
let palette = transaction.create(Into<Modern.ColorsDemo.Palette>())
palette.setRandomHue()
}
)
return Modern.ColorsDemo.SwiftUI.DetailView(
Modern.ColorsDemo.palettesPublisher.snapshot.first!
)
}
}
#endif

View File

@@ -0,0 +1,76 @@
//
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
import CoreStore
import SwiftUI
// MARK: - Modern.ColorsDemo.SwiftUI
extension Modern.ColorsDemo.SwiftUI {
// MARK: - Modern.ColorsDemo.SwiftUI.ItemView
struct ItemView: View {
/**
Sample 1: Setting an `ObjectPublisher` declared as an `@ObservedObject`
*/
@ObservedObject
private var palette: ObjectPublisher<Modern.ColorsDemo.Palette>
// MARK: Internal
internal init(_ palette: ObjectPublisher<Modern.ColorsDemo.Palette>) {
self.palette = palette
}
// MARK: View
var body: some View {
guard let palette = self.palette.snapshot else {
return AnyView(EmptyView())
}
return AnyView(
HStack {
Color(palette.$color)
.cornerRadius(5)
.frame(width: 30, height: 30, alignment: .leading)
Text(palette.$colorText)
}
)
}
}
}
#if DEBUG
struct _Demo_Modern_ColorsDemo_SwiftUI_ItemView_Preview: PreviewProvider {
// MARK: PreviewProvider
static var previews: some View {
try! Modern.ColorsDemo.dataStack.perform(
synchronous: { transaction in
guard (try transaction.fetchCount(From<Modern.ColorsDemo.Palette>())) <= 0 else {
return
}
let palette = transaction.create(Into<Modern.ColorsDemo.Palette>())
palette.setRandomHue()
}
)
return Modern.ColorsDemo.SwiftUI.ItemView(
Modern.ColorsDemo.palettesPublisher.snapshot.first!
)
}
}
#endif

View File

@@ -0,0 +1,118 @@
//
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
import CoreStore
import SwiftUI
// MARK: - Modern.ColorsDemo.SwiftUI
extension Modern.ColorsDemo.SwiftUI {
// MARK: - Modern.ColorsDemo.SwiftUI.ListView
struct ListView: View {
/**
Sample 1: Setting a sectioned `ListPublisher` declared as an `@ObservedObject`
*/
@ObservedObject
private var listPublisher: ListPublisher<Modern.ColorsDemo.Palette>
/**
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
Section(header: Text(sectionID)) {
ForEach(listSnapshot.items(inSectionWithID: sectionID), id: \.self) { palette in
Button(
action: {
self.onPaletteTapped(palette)
},
label: {
Modern.ColorsDemo.SwiftUI.ItemView(palette)
}
)
}
.onDelete { itemIndices in
self.deleteColors(at: itemIndices, in: sectionID)
}
}
}
GeometryReader { geometry in
Spacer(minLength: geometry.safeAreaInsets.bottom)
}
}
.listStyle(PlainListStyle())
}
// MARK: Internal
init(
listPublisher: ListPublisher<Modern.ColorsDemo.Palette>,
onPaletteTapped: @escaping (ObjectPublisher<Modern.ColorsDemo.Palette>) -> Void
) {
self.listPublisher = listPublisher
self.onPaletteTapped = onPaletteTapped
}
// MARK: Private
private let onPaletteTapped: (ObjectPublisher<Modern.ColorsDemo.Palette>) -> Void
private func deleteColors(at indices: IndexSet, in sectionID: String) {
let objectIDsToDelete = self.listPublisher.snapshot.itemIDs(
inSectionWithID: sectionID,
atIndices: indices
)
Modern.ColorsDemo.dataStack.perform(
asynchronous: { transaction in
transaction.delete(objectIDs: objectIDsToDelete)
},
completion: { _ in }
)
}
}
}
#if DEBUG
struct _Demo_Modern_ColorsDemo_SwiftUI_ListView_Preview: PreviewProvider {
// MARK: PreviewProvider
static var previews: some View {
let minimumSamples = 10
try! Modern.ColorsDemo.dataStack.perform(
synchronous: { transaction in
let missing = minimumSamples
- (try transaction.fetchCount(From<Modern.ColorsDemo.Palette>()))
guard missing > 0 else {
return
}
for _ in 0..<missing {
let palette = transaction.create(Into<Modern.ColorsDemo.Palette>())
palette.setRandomHue()
}
}
)
return Modern.ColorsDemo.SwiftUI.ListView(
listPublisher: Modern.ColorsDemo.palettesPublisher,
onPaletteTapped: { _ in }
)
}
}
#endif

View File

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

View File

@@ -0,0 +1,69 @@
//
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
import Combine
import CoreStore
import SwiftUI
// MARK: - Modern.ColorsDemo.UIKit
extension Modern.ColorsDemo.UIKit {
// MARK: - Modern.ColorsDemo.UIKit.DetailView
struct DetailView: UIViewControllerRepresentable {
// MARK: Internal
init(_ palette: ObjectPublisher<Modern.ColorsDemo.Palette>) {
self.palette = palette
}
// MARK: UIViewControllerRepresentable
typealias UIViewControllerType = Modern.ColorsDemo.UIKit.DetailViewController
func makeUIViewController(context: Self.Context) -> UIViewControllerType {
return UIViewControllerType(self.palette)
}
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Self.Context) {}
static func dismantleUIViewController(_ uiViewController: UIViewControllerType, coordinator: Void) {}
// MARK: Private
private let palette: ObjectPublisher<Modern.ColorsDemo.Palette>
}
}
#if DEBUG
struct _Demo_Modern_ColorsDemo_UIKit_DetailView_Preview: PreviewProvider {
// MARK: PreviewProvider
static var previews: some View {
try! Modern.ColorsDemo.dataStack.perform(
synchronous: { transaction in
guard (try transaction.fetchCount(From<Modern.ColorsDemo.Palette>())) <= 0 else {
return
}
let palette = transaction.create(Into<Modern.ColorsDemo.Palette>())
palette.setRandomHue()
}
)
return Modern.ColorsDemo.UIKit.DetailView(
Modern.ColorsDemo.palettesPublisher.snapshot.first!
)
}
}
#endif

View File

@@ -0,0 +1,285 @@
//
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
import CoreStore
import UIKit
// MARK: - Modern.ColorsDemo.UIKit
extension Modern.ColorsDemo.UIKit {
// MARK: - Modern.ColorsDemo.UIKit.DetailViewController
final class DetailViewController: UIViewController, ObjectObserver {
/**
Sample 1: We can normally use `ObjectPublisher` directly, which is simpler. But for this demo, we will be using `ObjectMonitor` instead because we need to keep track of which properties change to prevent our `UISlider` from stuttering. Refer to the `objectMonitor(_:didUpdateObject:changedPersistentKeys:)` implementation below.
*/
init(_ palette: ObjectPublisher<Modern.ColorsDemo.Palette>) {
self.palette = Modern.ColorsDemo.dataStack.monitorObject(
palette.object!
)
super.init(nibName: nil, bundle: nil)
}
/**
Sample 2: Once the views are created, we can start receiving `ObjectMonitor` updates in our `ObjectObserver` conformance methods. We typically call this at the end of `viewDidLoad`. Note that after the `addObserver` call, only succeeding updates will trigger our `ObjectObserver` methods, so to immediately display the current values, we need to initialize our views once (in this case, using `reloadPaletteInfo(_:changedKeys:)`.
*/
private func startMonitoringObject() {
self.palette.addObserver(self)
if let palette = self.palette.object {
self.reloadPaletteInfo(palette, changedKeys: nil)
}
}
/**
Sample 3: We can end monitoring updates anytime. `removeObserver()` was called here for illustration purposes only. `ObjectMonitor`s safely remove deallocated observers automatically.
*/
deinit {
self.palette.removeObserver(self)
}
/**
Sample 4: Our `objectMonitor(_:didUpdateObject:changedPersistentKeys:)` implementation passes a `Set<KeyPathString>` to our reload method. We can then inspect which values were triggered by each `UISlider`, so we can avoid double-updates that can lag the `UISlider` dragging.
*/
func reloadPaletteInfo(
_ palette: Modern.ColorsDemo.Palette,
changedKeys: Set<KeyPathString>?
) {
self.view.backgroundColor = palette.color
self.hueLabel.text = "H: \(Int(palette.hue * 359))°"
self.saturationLabel.text = "S: \(Int(palette.saturation * 100))%"
self.brightnessLabel.text = "B: \(Int(palette.brightness * 100))%"
if changedKeys == nil
|| changedKeys?.contains(String(keyPath: \Modern.ColorsDemo.Palette.$hue)) == true {
self.hueSlider.value = Float(palette.hue)
}
if changedKeys == nil
|| changedKeys?.contains(String(keyPath: \Modern.ColorsDemo.Palette.$saturation)) == true {
self.saturationSlider.value = palette.saturation
}
if changedKeys == nil
|| changedKeys?.contains(String(keyPath: \Modern.ColorsDemo.Palette.$brightness)) == true {
self.brightnessSlider.value = palette.brightness
}
}
// MARK: ObjectObserver
func objectMonitor(
_ monitor: ObjectMonitor<Modern.ColorsDemo.Palette>,
didUpdateObject object: Modern.ColorsDemo.Palette,
changedPersistentKeys: Set<KeyPathString>
) {
self.reloadPaletteInfo(object, changedKeys: changedPersistentKeys)
}
// MARK: UIViewController
override func viewDidLoad() {
super.viewDidLoad()
let view = self.view!
let containerView = UIView()
do {
containerView.translatesAutoresizingMaskIntoConstraints = false
containerView.backgroundColor = UIColor.white
containerView.layer.cornerRadius = 10
containerView.layer.masksToBounds = true
containerView.layer.shadowColor = UIColor(white: 0.5, alpha: 0.3).cgColor
containerView.layer.shadowOffset = .init(width: 1, height: 1)
containerView.layer.shadowRadius = 2
view.addSubview(containerView)
}
let vStackView = UIStackView()
do {
vStackView.translatesAutoresizingMaskIntoConstraints = false
vStackView.axis = .vertical
vStackView.spacing = 10
vStackView.distribution = .fill
vStackView.alignment = .fill
containerView.addSubview(vStackView)
}
let palette = self.palette.object
let rows: [(label: UILabel, slider: UISlider, initialValue: Float, sliderValueChangedSelector: Selector)] = [
(
self.hueLabel,
self.hueSlider,
palette?.hue ?? 0,
#selector(self.hueSliderValueDidChange(_:))
),
(
self.saturationLabel,
self.saturationSlider,
palette?.saturation ?? 0,
#selector(self.saturationSliderValueDidChange(_:))
),
(
self.brightnessLabel,
self.brightnessSlider,
palette?.brightness ?? 0,
#selector(self.brightnessSliderValueDidChange(_:))
)
]
for (label, slider, initialValue, sliderValueChangedSelector) in rows {
let hStackView = UIStackView()
do {
hStackView.translatesAutoresizingMaskIntoConstraints = false
hStackView.axis = .horizontal
hStackView.spacing = 5
hStackView.distribution = .fill
hStackView.alignment = .center
vStackView.addArrangedSubview(hStackView)
}
do {
label.translatesAutoresizingMaskIntoConstraints = false
label.textColor = UIColor(white: 0, alpha: 0.8)
label.textAlignment = .center
hStackView.addArrangedSubview(label)
}
do {
slider.translatesAutoresizingMaskIntoConstraints = false
slider.minimumValue = 0
slider.maximumValue = 1
slider.value = initialValue
slider.addTarget(
self,
action: sliderValueChangedSelector,
for: .valueChanged
)
hStackView.addArrangedSubview(slider)
}
}
layout: do {
NSLayoutConstraint.activate(
[
containerView.leadingAnchor.constraint(
equalTo: view.safeAreaLayoutGuide.leadingAnchor,
constant: 10
),
containerView.bottomAnchor.constraint(
equalTo: view.safeAreaLayoutGuide.bottomAnchor,
constant: -10
),
containerView.trailingAnchor.constraint(
equalTo: view.safeAreaLayoutGuide.trailingAnchor,
constant: -10
),
vStackView.topAnchor.constraint(
equalTo: containerView.topAnchor,
constant: 15
),
vStackView.leadingAnchor.constraint(
equalTo: containerView.leadingAnchor,
constant: 15
),
vStackView.bottomAnchor.constraint(
equalTo: containerView.bottomAnchor,
constant: -15
),
vStackView.trailingAnchor.constraint(
equalTo: containerView.trailingAnchor,
constant: -15
)
]
)
NSLayoutConstraint.activate(
rows.map { label, _, _, _ in
label.widthAnchor.constraint(equalToConstant: 80)
}
)
}
self.startMonitoringObject()
}
// MARK: Private
private let palette: ObjectMonitor<Modern.ColorsDemo.Palette>
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError()
}
private let hueLabel: UILabel = .init()
private let saturationLabel: UILabel = .init()
private let brightnessLabel: UILabel = .init()
private let hueSlider: UISlider = .init()
private let saturationSlider: UISlider = .init()
private let brightnessSlider: UISlider = .init()
@objc
private dynamic func hueSliderValueDidChange(_ sender: UISlider) {
let value = sender.value
Modern.ColorsDemo.dataStack.perform(
asynchronous: { [weak self] (transaction) in
let palette = transaction.edit(self?.palette.object)
palette?.hue = value
},
completion: { _ in }
)
}
@objc
private dynamic func saturationSliderValueDidChange(_ sender: UISlider) {
let value = sender.value
Modern.ColorsDemo.dataStack.perform(
asynchronous: { [weak self] (transaction) in
let palette = transaction.edit(self?.palette.object)
palette?.saturation = value
},
completion: { _ in }
)
}
@objc
private dynamic func brightnessSliderValueDidChange(_ sender: UISlider) {
let value = sender.value
Modern.ColorsDemo.dataStack.perform(
asynchronous: { [weak self] (transaction) in
let palette = transaction.edit(self?.palette.object)
palette?.brightness = value
},
completion: { _ in }
)
}
}
}

View File

@@ -0,0 +1,31 @@
//
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
import CoreStore
import UIKit
// MARK: - Modern.ColorsDemo.UIKit
extension Modern.ColorsDemo.UIKit {
// MARK: - Modern.ColorsDemo.UIKit.ItemCell
final class ItemCell: UITableViewCell {
// MARK: Internal
static let reuseIdentifier: String = NSStringFromClass(Modern.ColorsDemo.UIKit.ItemCell.self)
func setPalette(_ palette: Modern.ColorsDemo.Palette) {
self.imageView?.image = UIImage(
color: palette.color,
size: .init(width: 30, height: 30),
cornerRadius: 5
)
self.textLabel?.text = palette.colorText
}
}
}

View File

@@ -0,0 +1,89 @@
//
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
import CoreStore
import SwiftUI
// MARK: - Modern.ColorsDemo.UIKit
extension Modern.ColorsDemo.UIKit {
// MARK: - Modern.ColorsDemo.UIKit.ListView
struct ListView: UIViewControllerRepresentable {
// MARK: Internal
init(
listPublisher: ListPublisher<Modern.ColorsDemo.Palette>,
onPaletteTapped: @escaping (ObjectPublisher<Modern.ColorsDemo.Palette>) -> Void
) {
self.listPublisher = listPublisher
self.onPaletteTapped = onPaletteTapped
}
// MARK: UIViewControllerRepresentable
typealias UIViewControllerType = Modern.ColorsDemo.UIKit.ListViewController
func makeUIViewController(context: Self.Context) -> UIViewControllerType {
return UIViewControllerType(
listPublisher: self.listPublisher,
onPaletteTapped: self.onPaletteTapped
)
}
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Self.Context) {
uiViewController.setEditing(
context.environment.editMode?.wrappedValue.isEditing == true,
animated: true
)
}
static func dismantleUIViewController(_ uiViewController: UIViewControllerType, coordinator: Void) {}
// MARK: Private
private let listPublisher: ListPublisher<Modern.ColorsDemo.Palette>
private let onPaletteTapped: (ObjectPublisher<Modern.ColorsDemo.Palette>) -> Void
}
}
#if DEBUG
struct _Demo_Modern_ColorsDemo_UIKit_ListView_Preview: PreviewProvider {
// MARK: PreviewProvider
static var previews: some View {
let minimumSamples = 10
try! Modern.ColorsDemo.dataStack.perform(
synchronous: { transaction in
let missing = minimumSamples
- (try transaction.fetchCount(From<Modern.ColorsDemo.Palette>()))
guard missing > 0 else {
return
}
for _ in 0..<missing {
let palette = transaction.create(Into<Modern.ColorsDemo.Palette>())
palette.setRandomHue()
}
}
)
return Modern.ColorsDemo.UIKit.ListView(
listPublisher: Modern.ColorsDemo.palettesPublisher,
onPaletteTapped: { _ in }
)
}
}
#endif

View File

@@ -0,0 +1,139 @@
//
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
import CoreStore
import UIKit
// MARK: - Modern.ColorsDemo.UIKit
extension Modern.ColorsDemo.UIKit {
// MARK: - Modern.ColorsDemo.UIKit.ListViewController
final class ListViewController: UITableViewController {
/**
Sample 1: Setting up a `DiffableDataSource.TableViewAdapter` that will manage tableView snapshot updates automatically. We can use the built-in `DiffableDataSource.TableViewAdapter` type directly, but in our case we want to enabled `UITableView` cell deletions so we create a custom subclass `DeletionEnabledDataSource` (see declatation below).
*/
private lazy var dataSource: DiffableDataSource.TableViewAdapter<Modern.ColorsDemo.Palette> = DeletionEnabledDataSource(
tableView: self.tableView,
dataStack: Modern.ColorsDemo.dataStack,
cellProvider: { (tableView, indexPath, palette) in
let cell = tableView.dequeueReusableCell(
withIdentifier: Modern.ColorsDemo.UIKit.ItemCell.reuseIdentifier,
for: indexPath
) as! Modern.ColorsDemo.UIKit.ItemCell
cell.setPalette(palette)
return cell
}
)
/**
Sample 2: Once the views are created, we can start binding `ListPublisher` updates to the `DiffableDataSource`. We typically call this at the end of `viewDidLoad`. Note that the `addObserver`'s closure argument will only be called on the succeeding updates, so to immediately display the current values, we need to call `dataSource.apply()` once.
*/
private func startObservingList() {
self.listPublisher.addObserver(self) { (listPublisher) in
self.dataSource.apply(
listPublisher.snapshot,
animatingDifferences: true
)
}
self.dataSource.apply(
listPublisher.snapshot,
animatingDifferences: false
)
}
/**
Sample 3: We can end monitoring updates anytime. `removeObserver()` was called here for illustration purposes only. `ListPublisher`s safely remove deallocated observers automatically.
*/
deinit {
self.listPublisher.removeObserver(self)
}
/**
Sample 4: This is the custom `DiffableDataSource.TableViewAdapter` subclass we wrote that enabled swipe-to-delete gestures on the `UITableView`.
*/
final class DeletionEnabledDataSource: DiffableDataSource.TableViewAdapter<Modern.ColorsDemo.Palette> {
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
switch editingStyle {
case .delete:
guard let itemID = self.itemID(for: indexPath) else {
return
}
self.dataStack.perform(
asynchronous: { (transaction) in
transaction.delete(objectIDs: [itemID])
},
completion: { _ in }
)
default:
break
}
}
}
// MARK: Internal
init(
listPublisher: ListPublisher<Modern.ColorsDemo.Palette>,
onPaletteTapped: @escaping (ObjectPublisher<Modern.ColorsDemo.Palette>) -> Void
) {
self.listPublisher = listPublisher
self.onPaletteTapped = onPaletteTapped
super.init(style: .plain)
}
// MARK: UIViewController
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.register(
Modern.ColorsDemo.UIKit.ItemCell.self,
forCellReuseIdentifier: Modern.ColorsDemo.UIKit.ItemCell.reuseIdentifier
)
self.startObservingList()
}
// MARK: UITableViewDelegate
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.onPaletteTapped(
self.listPublisher.snapshot[indexPath]
)
}
// MARK: Private
private let listPublisher: ListPublisher<Modern.ColorsDemo.Palette>
private let onPaletteTapped: (ObjectPublisher<Modern.ColorsDemo.Palette>) -> Void
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError()
}
}
}

View File

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

View File

@@ -0,0 +1,66 @@
//
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
import CoreStore
// MARK: - Modern
extension Modern {
// MARK: - Modern.ColorsDemo
/**
Sample usages for observing lists or single instances of `CoreStoreObject`s
*/
enum ColorsDemo {
// MARK: Internal
static let dataStack: DataStack = {
let dataStack = DataStack(
CoreStoreSchema(
modelVersion: "V1",
entities: [
Entity<Modern.ColorsDemo.Palette>("Palette")
],
versionLock: [
"Palette": [0xbaf4eaee9353176a, 0xdd6ca918cc2b0c38, 0xd04fad8882d7cc34, 0x3e90ca38c091503f]
]
)
)
/**
- Important: `addStorageAndWait(_:)` was used here to simplify initializing the demo, but in practice the asynchronous function variants are recommended.
*/
try! dataStack.addStorageAndWait(
SQLiteStore(
fileName: "Modern.ColorsDemo.sqlite",
localStorageOptions: .recreateStoreOnModelMismatch
)
)
return dataStack
}()
static let palettesPublisher: ListPublisher<Modern.ColorsDemo.Palette> = Modern.ColorsDemo.dataStack.publishList(
From<Modern.ColorsDemo.Palette>()
.sectionBy(\.$colorName)
.where(Modern.ColorsDemo.filter.whereClause())
.orderBy(.ascending(\.$hue))
)
static var filter: Modern.ColorsDemo.Filter = .all {
didSet {
try! Modern.ColorsDemo.palettesPublisher.refetch(
From<Modern.ColorsDemo.Palette>()
.sectionBy(\.$colorName)
.where(self.filter.whereClause())
.orderBy(.ascending(\.$hue))
)
}
}
}
}

View File

@@ -0,0 +1,10 @@
//
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
// MARK: - Modern
/**
Sample usages for `CoreStoreObject` subclasses
*/
enum Modern {}

View File

@@ -0,0 +1,67 @@
//
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
import Contacts
import CoreLocation
import CoreStore
// MARK: - Modern.PlacemarksDemo
extension Modern.PlacemarksDemo {
// MARK: Geocoder
final class Geocoder {
// MARK: Internal
func geocode(
place: ObjectSnapshot<Modern.PlacemarksDemo.Place>,
completion: @escaping (_ title: String?, _ subtitle: String?) -> Void
) {
self.geocoder?.cancelGeocode()
let geocoder = CLGeocoder()
self.geocoder = geocoder
geocoder.reverseGeocodeLocation(
CLLocation(latitude: place.$latitude, longitude: place.$longitude),
completionHandler: { (placemarks, error) -> Void in
defer {
self.geocoder = nil
}
guard let placemark = placemarks?.first else {
return
}
let address = CNMutablePostalAddress()
address.street = placemark.thoroughfare ?? ""
address.subLocality = placemark.subThoroughfare ?? ""
address.city = placemark.locality ?? ""
address.subAdministrativeArea = placemark.subAdministrativeArea ?? ""
address.state = placemark.administrativeArea ?? ""
address.postalCode = placemark.postalCode ?? ""
address.country = placemark.country ?? ""
address.isoCountryCode = placemark.isoCountryCode ?? ""
completion(
placemark.name,
CNPostalAddressFormatter.string(
from: address,
style: .mailingAddress
)
)
}
)
}
// MARK: Private
private var geocoder: CLGeocoder?
}
}

View File

@@ -0,0 +1,153 @@
//
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
import CoreLocation
import Combine
import CoreStore
import Foundation
import MapKit
import SwiftUI
// MARK: - Modern.PlacemarksDemo
extension Modern.PlacemarksDemo {
// MARK: - Modern.PlacemarksDemo.MainView
struct MainView: View {
/**
Sample 1: Asynchronous transactions
*/
private func demoAsynchronousTransaction(coordinate: CLLocationCoordinate2D) {
Modern.PlacemarksDemo.dataStack.perform(
asynchronous: { (transaction) in
let place = self.place.asEditable(in: transaction)
place?.annotation = .init(coordinate: coordinate)
},
completion: { _ in }
)
}
/**
Sample 2: Synchronous transactions
- Important: `perform(synchronous:)` was used here for illustration purposes. In practice, `perform(asynchronous:completion:)` is the preferred transaction type as synchronous transactions are very likely to cause deadlocks.
*/
private func demoSynchronousTransaction() {
_ = try? Modern.PlacemarksDemo.dataStack.perform(
synchronous: { (transaction) in
let place = self.place.asEditable(in: transaction)
place?.setRandomLocation()
}
)
}
/**
Sample 3: Unsafe transactions
- Important: `beginUnsafe()` was used here for illustration purposes. In practice, `perform(asynchronous:completion:)` is the preferred transaction type. Use Unsafe Transactions only when you need to bypass CoreStore's serialized transactions.
*/
private func demoUnsafeTransaction(
title: String?,
subtitle: String?,
for snapshot: ObjectSnapshot<Modern.PlacemarksDemo.Place>
) {
let transaction = Modern.PlacemarksDemo.dataStack.beginUnsafe()
let place = snapshot.asEditable(in: transaction)
place?.title = title
place?.subtitle = subtitle
transaction.commit { (error) in
print("Commit failed: \(error as Any)")
}
}
// MARK: Internal
@ObservedObject
var place: ObjectPublisher<Modern.PlacemarksDemo.Place>
init() {
self.place = Modern.PlacemarksDemo.placePublisher
self.sinkCancellable = self.place.sink(
receiveCompletion: { _ in
// Deleted, do nothing
},
receiveValue: { [self] (snapshot) in
self.geocoder.geocode(place: snapshot) { (title, subtitle) in
guard self.place.snapshot == snapshot else {
return
}
self.demoUnsafeTransaction(
title: title,
subtitle: subtitle,
for: snapshot
)
}
}
)
}
// MARK: View
var body: some View {
Modern.PlacemarksDemo.MapView(
place: self.place.snapshot,
onTap: { coordinate in
self.demoAsynchronousTransaction(coordinate: coordinate)
}
)
.overlay(
InstructionsView(
("Random", "Sets random coordinate"),
("Tap", "Sets to tapped coordinate")
)
.padding(.leading, 10)
.padding(.bottom, 40),
alignment: .bottomLeading
)
.navigationBarTitle("Placemarks")
.navigationBarItems(
trailing: Button("Random") {
self.demoSynchronousTransaction()
}
)
}
// MARK: Private
private var sinkCancellable: AnyCancellable? = nil
private let geocoder = Modern.PlacemarksDemo.Geocoder()
}
}
#if DEBUG
struct _Demo_Modern_PlacemarksDemo_MainView_Preview: PreviewProvider {
// MARK: PreviewProvider
static var previews: some View {
Modern.PlacemarksDemo.MainView()
}
}
#endif

View File

@@ -0,0 +1,119 @@
//
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
import CoreLocation
import CoreStore
import MapKit
import UIKit
import SwiftUI
// MARK: - Modern.PlacemarksDemo
extension Modern.PlacemarksDemo {
// MARK: - Modern.PlacemarksDemo.MapView
struct MapView: UIViewRepresentable {
// MARK: Internal
var place: ObjectSnapshot<Modern.PlacemarksDemo.Place>?
let onTap: (CLLocationCoordinate2D) -> Void
// MARK: UIViewRepresentable
typealias UIViewType = MKMapView
func makeUIView(context: Context) -> UIViewType {
let coordinator = context.coordinator
let mapView = MKMapView()
mapView.delegate = coordinator
mapView.addGestureRecognizer(
UITapGestureRecognizer(
target: coordinator,
action: #selector(coordinator.tapGestureRecognized(_:))
)
)
return mapView
}
func updateUIView(_ view: UIViewType, context: Context) {
let currentAnnotations = view.annotations
view.removeAnnotations(currentAnnotations)
guard let newAnnotation = self.place?.$annotation else {
return
}
view.addAnnotation(newAnnotation)
view.setCenter(newAnnotation.coordinate, animated: true)
view.selectAnnotation(newAnnotation, animated: true)
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
final class Coordinator: NSObject, MKMapViewDelegate {
// MARK: Internal
init(_ parent: MapView) {
self.parent = parent
}
// MARK: MKMapViewDelegate
@objc dynamic func mapView(
_ mapView: MKMapView,
viewFor annotation: MKAnnotation
) -> MKAnnotationView? {
let identifier = "MKAnnotationView"
var annotationView: MKPinAnnotationView! = mapView.dequeueReusableAnnotationView(withIdentifier: identifier) as? MKPinAnnotationView
if annotationView == nil {
annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
annotationView.isEnabled = true
annotationView.canShowCallout = true
annotationView.animatesDrop = true
}
else {
annotationView.annotation = annotation
}
return annotationView
}
// MARK: FilePrivate
@objc
fileprivate dynamic func tapGestureRecognized(_ gesture: UILongPressGestureRecognizer) {
guard
case let mapView as MKMapView = gesture.view,
gesture.state == .recognized
else {
return
}
let coordinate = mapView.convert(
gesture.location(in: mapView),
toCoordinateFrom: mapView
)
self.parent.onTap(coordinate)
}
// MARK: Private
private var parent: MapView
}
}
}

View File

@@ -0,0 +1,120 @@
//
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
import CoreStore
import struct CoreLocation.CLLocationCoordinate2D
import protocol MapKit.MKAnnotation
// MARK: - Modern.PlacemarksDemo
extension Modern.PlacemarksDemo {
// MARK: - Modern.PlacemarksDemo.Place
final class Place: CoreStoreObject {
// MARK: Internal
@Field.Stored("latitude")
var latitude: Double = 0
@Field.Stored("longitude")
var longitude: Double = 0
@Field.Stored("title")
var title: String?
@Field.Stored("subtitle")
var subtitle: String?
@Field.Virtual(
"annotation",
customGetter: { object, field in
Annotation(
latitude: object.$latitude.value,
longitude: object.$longitude.value,
title: object.$title.value,
subtitle: object.$subtitle.value
)
},
customSetter: { object, field, newValue in
object.$latitude.value = newValue.coordinate.latitude
object.$longitude.value = newValue.coordinate.longitude
object.$title.value = "\(newValue.coordinate.latitude), \(newValue.coordinate.longitude)"
object.$subtitle.value = nil
}
)
var annotation: Annotation
func setRandomLocation() {
self.latitude = Double(arc4random_uniform(180)) - 90
self.longitude = Double(arc4random_uniform(360)) - 180
self.title = "\(self.latitude), \(self.longitude)"
self.subtitle = nil
}
// MARK: - Annotation
final class Annotation: NSObject, MKAnnotation {
// MARK: Internal
init(coordinate: CLLocationCoordinate2D) {
self.coordinate = coordinate
self.title = nil
self.subtitle = nil
}
// MARK: MKAnnotation
let coordinate: CLLocationCoordinate2D
let title: String?
let subtitle: String?
// MARK: NSObjectProtocol
override func isEqual(_ object: Any?) -> Bool {
guard case let object as Annotation = object else {
return false
}
return self.coordinate.latitude == object.coordinate.latitude
&& self.coordinate.longitude == object.coordinate.longitude
&& self.title == object.title
&& self.subtitle == object.subtitle
}
override var hash: Int {
var hasher = Hasher()
hasher.combine(self.coordinate.latitude)
hasher.combine(self.coordinate.longitude)
hasher.combine(self.title)
hasher.combine(self.subtitle)
return hasher.finalize()
}
// MARK: FilePrivate
fileprivate init(
latitude: Double,
longitude: Double,
title: String?,
subtitle: String?
) {
self.coordinate = .init(latitude: latitude, longitude: longitude)
self.title = title
self.subtitle = subtitle
}
}
}
}

View File

@@ -0,0 +1,64 @@
//
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
import CoreStore
// MARK: - Modern
extension Modern {
// MARK: - Modern.PlacemarksDemo
/**
Sample usages for `CoreStoreObject` transactions
*/
enum PlacemarksDemo {
// MARK: Internal
static let dataStack: DataStack = {
let dataStack = DataStack(
CoreStoreSchema(
modelVersion: "V1",
entities: [
Entity<Modern.PlacemarksDemo.Place>("Place")
],
versionLock: [
"Place": [0xa7eec849af5e8fcb, 0x638e69c040090319, 0x4e976d66ed400447, 0x18e96bc0438d07bb]
]
)
)
/**
- Important: `addStorageAndWait(_:)` and `perform(synchronous:)` methods were used here to simplify initializing the demo, but in practice the asynchronous function variants are recommended.
*/
try! dataStack.addStorageAndWait(
SQLiteStore(
fileName: "Modern.PlacemarksDemo.sqlite",
localStorageOptions: .recreateStoreOnModelMismatch
)
)
return dataStack
}()
static let placePublisher: ObjectPublisher<Modern.PlacemarksDemo.Place> = {
let dataStack = Modern.PlacemarksDemo.dataStack
if let place = try! dataStack.fetchOne(From<Place>()) {
return dataStack.publishObject(place)
}
_ = try! dataStack.perform(
synchronous: { (transaction) in
let place = transaction.create(Into<Place>())
place.setRandomLocation()
}
)
let place = try! dataStack.fetchOne(From<Place>())
return dataStack.publishObject(place!)
}()
}
}

View File

@@ -0,0 +1,33 @@
//
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
import CoreStore
// MARK: - Modern.PokedexDemo
extension Modern.PokedexDemo {
// MARK: - Modern.PokedexDemo.Ability
final class Ability: CoreStoreObject {
// MARK: Internal
@Field.Stored("id")
var id: Int = 0
@Field.Stored("name")
var name: String = ""
@Field.Stored("text")
var text: String = ""
@Field.Stored("isHiddenAbility")
var isHiddenAbility: Bool = false
@Field.Relationship("learners")
var learners: Set<Modern.PokedexDemo.PokemonForm>
}
}

View File

@@ -0,0 +1,48 @@
//
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
import CoreStore
// MARK: - Modern.PokedexDemo
extension Modern.PokedexDemo {
// MARK: - Modern.PokedexDemo.Move
final class Move: CoreStoreObject {
// MARK: Internal
@Field.Stored("id")
var id: Int = 0
@Field.Stored("name")
var name: String = ""
@Field.Stored("text")
var text: String = ""
@Field.Stored("pokemonType")
var pokemonType: Modern.PokedexDemo.PokemonType = .normal
@Field.Stored("power")
var power: Int = 0
@Field.Stored("accuracy")
var accuracy: Int = 0
@Field.Stored("powerPoints")
var powerPoints: Int = 0
@Field.Stored("effectChance")
var effectChance: Int = 0
@Field.Stored("priority")
var priority: Int = 0
@Field.Relationship("learners")
var learners: Set<Modern.PokedexDemo.PokemonForm>
}
}

View File

@@ -0,0 +1,71 @@
//
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
import CoreStore
// MARK: - Modern.PokedexDemo
extension Modern.PokedexDemo {
// MARK: - Modern.PokedexDemo.PokemonForm
final class PokemonForm: CoreStoreObject {
// MARK: Internal
@Field.Stored("id")
var id: Int = 0
@Field.Stored("name")
var name: String = ""
@Field.Stored("pokemonType1")
var pokemonType1: Modern.PokedexDemo.PokemonType = .normal
@Field.Stored("pokemonType2")
var pokemonType2: Modern.PokedexDemo.PokemonType?
@Field.Relationship("species")
var species: Modern.PokedexDemo.PokemonSpecies?
@Field.Stored("statHitPoints")
var statHitPoints: Int = 0
@Field.Stored("statAttack")
var statAttack: Int = 0
@Field.Stored("statDefense")
var statDefense: Int = 0
@Field.Stored("statSpecialAttack")
var statSpecialAttack: Int = 0
@Field.Stored("statSpecialDefense")
var statSpecialDefense: Int = 0
@Field.Stored("statSpeed")
var statSpeed: Int = 0
@Field.Stored("spriteFrontURL")
var spriteFrontURL: URL?
@Field.Stored("spriteBackURL")
var spriteBackURL: URL?
@Field.Stored("spriteShinyFrontURL")
var spriteShinyFrontURL: URL?
@Field.Stored("spriteShinyBackURL")
var spriteShinyBackURL: URL?
@Field.Relationship("abilities", inverse: \.$learners)
var abilities: Set<Modern.PokedexDemo.Ability>
@Field.Relationship("moves", inverse: \.$learners)
var moves: Set<Modern.PokedexDemo.Move>
}
}

View File

@@ -0,0 +1,29 @@
//
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
import CoreStore
// MARK: - Modern.PokedexDemo
extension Modern.PokedexDemo {
// MARK: - Modern.PokedexDemo.PokemonSpecies
final class PokemonSpecies: CoreStoreObject {
// MARK: Internal
@Field.Stored("id")
var id: Int = 0
@Field.Stored("name")
var name: String = ""
@Field.Stored("weight")
var weight: Int = 0
@Field.Relationship("forms", inverse: \.$species)
var forms: Set<Modern.PokedexDemo.PokemonForm>
}
}

View File

@@ -0,0 +1,36 @@
//
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
import CoreStore
// MARK: - Modern.PokedexDemo
extension Modern.PokedexDemo {
// MARK: - Modern.PokedexDemo.Move
enum PokemonType: String, CaseIterable, FieldStorableType {
// MARK: Internal
case bug
case dark
case dragon
case electric
case fairy
case fighting
case fire
case flying
case ghost
case grass
case ground
case ice
case normal
case poison
case psychic
case rock
case steel
case water
}
}

View File

@@ -0,0 +1,66 @@
//
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
import CoreStore
// MARK: - Modern
extension Modern {
// MARK: - Modern.PokedexDemo
/**
Sample usages for importing external data into `CoreStoreObject` attributes
*/
enum PokedexDemo {
// MARK: Internal
static let dataStack: DataStack = {
let dataStack = DataStack(
CoreStoreSchema(
modelVersion: "V1",
entities: [
Entity<Modern.ColorsDemo.Palette>("Palette")
],
versionLock: [
"Palette": [0xbaf4eaee9353176a, 0xdd6ca918cc2b0c38, 0xd04fad8882d7cc34, 0x3e90ca38c091503f]
]
)
)
/**
- Important: `addStorageAndWait(_:)` was used here to simplify initializing the demo, but in practice the asynchronous function variants are recommended.
*/
try! dataStack.addStorageAndWait(
SQLiteStore(
fileName: "Modern.ColorsDemo.sqlite",
localStorageOptions: .recreateStoreOnModelMismatch
)
)
return dataStack
}()
static let palettesPublisher: ListPublisher<Modern.ColorsDemo.Palette> = Modern.ColorsDemo.dataStack.publishList(
From<Modern.ColorsDemo.Palette>()
.sectionBy(\.$colorName)
.where(Modern.ColorsDemo.filter.whereClause())
.orderBy(.ascending(\.$hue))
)
static var filter: Modern.ColorsDemo.Filter = .all {
didSet {
try! Modern.ColorsDemo.palettesPublisher.refetch(
From<Modern.ColorsDemo.Palette>()
.sectionBy(\.$colorName)
.where(self.filter.whereClause())
.orderBy(.ascending(\.$hue))
)
}
}
}
}

View File

@@ -0,0 +1,58 @@
//
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
import SwiftUI
// MARK: - Modern.TimeZonesDemo
extension Modern.TimeZonesDemo {
// MARK: - Modern.TimeZonesDemo.ItemView
struct ItemView: View {
// MARK: Internal
init(title: String, subtitle: String) {
self.title = title
self.subtitle = subtitle
}
// MARK: View
var body: some View {
VStack(alignment: .leading) {
Text(self.title)
.font(.headline)
.foregroundColor(.primary)
Text(self.subtitle)
.font(.subheadline)
.foregroundColor(.secondary)
}
}
// MARK: FilePrivate
fileprivate let title: String
fileprivate let subtitle: String
}
}
#if DEBUG
struct _Demo_Modern_TimeZone_ItemView_Preview: PreviewProvider {
// MARK: PreviewProvider
static var previews: some View {
Modern.TimeZonesDemo.ItemView(
title: "Item Title",
subtitle: "A subtitle caption for this item"
)
}
}
#endif

View File

@@ -0,0 +1,94 @@
//
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
import CoreStore
import SwiftUI
// MARK: - Modern.TimeZonesDemo
extension Modern.TimeZonesDemo {
// MARK: - Modern.TimeZonesDemo.ListView
struct ListView: View {
// MARK: Internal
init(title: String, objects: [Modern.TimeZonesDemo.TimeZone]) {
self.title = title
self.values = objects.map {
(title: $0.name, subtitle: $0.abbreviation)
}
}
init(title: String, value: Any?) {
self.title = title
switch value {
case (let array as [Any])?:
self.values = array.map {
(
title: String(describing: $0),
dsubtitleetail: String(reflecting: type(of: $0))
)
}
case let item?:
self.values = [
(
title: String(describing: item),
subtitle: String(reflecting: type(of: item))
)
]
case nil:
self.values = []
}
}
// MARK: View
var body: some View {
List {
ForEach(self.values, id: \.title) { item in
Modern.TimeZonesDemo.ItemView(
title: item.title,
subtitle: item.subtitle
)
}
}
.navigationBarTitle(self.title)
}
// MARK: Private
private let title: String
private let values: [(title: String, subtitle: String)]
}
}
#if DEBUG
struct _Demo_Modern_TimeZonesDemo_ListView_Preview: PreviewProvider {
// MARK: PreviewProvider
static var previews: some View {
Modern.TimeZonesDemo.ListView(
title: "Title",
objects: try! Modern.TimeZonesDemo.dataStack.fetchAll(
From<Modern.TimeZonesDemo.TimeZone>()
.orderBy(.ascending(\.$name))
)
)
}
}
#endif

View File

@@ -0,0 +1,234 @@
//
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
import CoreStore
import SwiftUI
// MARK: - Modern.TimeZonesDemo
extension Modern.TimeZonesDemo {
// MARK: - Modern.TimeZonesDemo.MainView
struct MainView: View {
/**
Sample 1: Plain object fetch
*/
private func fetchAllTimeZones() -> [Modern.TimeZonesDemo.TimeZone] {
return try! Modern.TimeZonesDemo.dataStack.fetchAll(
From<Modern.TimeZonesDemo.TimeZone>()
.orderBy(.ascending(\.$secondsFromGMT))
)
}
/**
Sample 2: Plain object fetch with simple `where` clause
*/
private func fetchTimeZonesWithDST() -> [Modern.TimeZonesDemo.TimeZone] {
return try! Modern.TimeZonesDemo.dataStack.fetchAll(
From<Modern.TimeZonesDemo.TimeZone>()
.where(\.$isDaylightSavingTime == true)
.orderBy(.ascending(\.$name))
)
}
/**
Sample 3: Plain object fetch with custom `where` clause
*/
private func fetchTimeZonesInAsia() -> [Modern.TimeZonesDemo.TimeZone] {
return try! Modern.TimeZonesDemo.dataStack.fetchAll(
From<Modern.TimeZonesDemo.TimeZone>()
.where(
format: "%K BEGINSWITH[c] %@",
String(keyPath: \Modern.TimeZonesDemo.TimeZone.$name),
"Asia"
)
.orderBy(.ascending(\.$secondsFromGMT))
)
}
/**
Sample 4: Plain object fetch with complex `where` clauses
*/
private func fetchTimeZonesNearUTC() -> [Modern.TimeZonesDemo.TimeZone] {
let secondsIn3Hours = 60 * 60 * 3
return try! Modern.TimeZonesDemo.dataStack.fetchAll(
From<Modern.TimeZonesDemo.TimeZone>()
.where((-secondsIn3Hours ... secondsIn3Hours) ~= \.$secondsFromGMT)
/// equivalent to:
/// ```
/// .where(\.$secondsFromGMT >= -secondsIn3Hours
/// && \.$secondsFromGMT <= secondsIn3Hours)
/// ```
.orderBy(.ascending(\.$secondsFromGMT))
)
}
/**
Sample 5: Querying single raw value with simple `select` clause
*/
private func queryNumberOfTimeZones() -> Int? {
return try! Modern.TimeZonesDemo.dataStack.queryValue(
From<Modern.TimeZonesDemo.TimeZone>()
.select(Int.self, .count(\.$name))
)
}
/**
Sample 6: Querying single raw values with `select` and `where` clauses
*/
private func queryTokyoTimeZoneAbbreviation() -> String? {
return try! Modern.TimeZonesDemo.dataStack.queryValue(
From<Modern.TimeZonesDemo.TimeZone>()
.select(String.self, .attribute(\.$abbreviation))
.where(
format: "%K ENDSWITH[c] %@",
String(keyPath: \Modern.TimeZonesDemo.TimeZone.$name),
"Tokyo"
)
)
}
/**
Sample 7: Querying a list of raw values with multiple attributes
*/
private func queryAllNamesAndAbbreviations() -> [[String: Any]]? {
return try! Modern.TimeZonesDemo.dataStack.queryAttributes(
From<Modern.TimeZonesDemo.TimeZone>()
.select(
NSDictionary.self,
.attribute(\.$name),
.attribute(\.$abbreviation)
)
.orderBy(.ascending(\.$name))
)
}
/**
Sample 7: Querying a list of raw values grouped by similar field
*/
private func queryNumberOfCountriesWithAndWithoutDST() -> [[String: Any]]? {
return try! Modern.TimeZonesDemo.dataStack.queryAttributes(
From<Modern.TimeZonesDemo.TimeZone>()
.select(
NSDictionary.self,
.count(\.$isDaylightSavingTime, as: "numberOfCountries"),
.attribute(\.$isDaylightSavingTime)
)
.groupBy(\.$isDaylightSavingTime)
.orderBy(
.ascending(\.$isDaylightSavingTime),
.ascending(\.$name)
)
)
}
// MARK: View
var body: some View {
List {
Section(header: Text("Fetching objects")) {
ForEach(self.fetchingItems, id: \.title) { item in
Menu.ItemView(
title: item.title,
destination: {
Modern.TimeZonesDemo.ListView(
title: item.title,
objects: item.objects()
)
}
)
}
}
Section(header: Text("Querying raw values")) {
ForEach(self.queryingItems, id: \.title) { item in
Menu.ItemView(
title: item.title,
destination: {
Modern.TimeZonesDemo.ListView(
title: item.title,
value: item.value()
)
}
)
}
}
}
.listStyle(GroupedListStyle())
.navigationBarTitle("Time Zones")
}
// MARK: Private
private var fetchingItems: [(title: String, objects: () -> [Modern.TimeZonesDemo.TimeZone])] {
return [
(
"All Time Zones",
self.fetchAllTimeZones
),
(
"Time Zones with Daylight Savings",
self.fetchTimeZonesWithDST
),
(
"Time Zones in Asia",
self.fetchTimeZonesInAsia
),
(
"Time Zones at most 3 hours away from UTC",
self.fetchTimeZonesNearUTC
)
]
}
private var queryingItems: [(title: String, value: () -> Any?)] {
return [
(
"Number of Time Zones",
self.queryNumberOfTimeZones
),
(
"Abbreviation for Tokyo's Time Zone",
self.queryTokyoTimeZoneAbbreviation
),
(
"All Names and Abbreviations",
self.queryAllNamesAndAbbreviations
),
(
"Number of Countries with and without DST",
self.queryNumberOfCountriesWithAndWithoutDST
)
]
}
}
}
#if DEBUG
struct _Demo_Modern_TimeZonesDemo_MainView_Preview: PreviewProvider {
// MARK: PreviewProvider
static var previews: some View {
Modern.TimeZonesDemo.MainView()
}
}
#endif

View File

@@ -0,0 +1,32 @@
//
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
import CoreStore
// MARK: - Modern.TimeZonesDemo
extension Modern.TimeZonesDemo {
// MARK: - Modern.TimeZonesDemo.TimeZone
final class TimeZone: CoreStoreObject {
// MARK: Internal
@Field.Stored("secondsFromGMT")
var secondsFromGMT: Int = 0
@Field.Stored("abbreviation")
var abbreviation: String = ""
@Field.Stored("isDaylightSavingTime")
var isDaylightSavingTime: Bool = false
@Field.Stored("daylightSavingTimeOffset")
var daylightSavingTimeOffset: Double = 0
@Field.Stored("name")
var name: String = ""
}
}

View File

@@ -0,0 +1,64 @@
//
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
import CoreStore
// MARK: - Modern
extension Modern {
// MARK: - Modern.TimeZonesDemo
/**
Sample usages for creating Fetch and Query clauses for `CoreStoreObject`s
*/
enum TimeZonesDemo {
// MARK: Internal
static let dataStack: DataStack = {
let dataStack = DataStack(
CoreStoreSchema(
modelVersion: "V1",
entities: [
Entity<Modern.TimeZonesDemo.TimeZone>("TimeZone")
],
versionLock: [
"TimeZone": [0x9b1d35108434c8fd, 0x4cb8a80903e66b64, 0x405acca3c1945fe3, 0x3b49dccaee0753d8]
]
)
)
/**
- Important: `addStorageAndWait(_:)` and `perform(synchronous:)` methods were used here to simplify initializing the demo, but in practice the asynchronous function variants are recommended.
*/
try! dataStack.addStorageAndWait(
SQLiteStore(
fileName: "Modern.TimeZonesDemo.sqlite",
localStorageOptions: .recreateStoreOnModelMismatch
)
)
_ = try! dataStack.perform(
synchronous: { (transaction) in
try transaction.deleteAll(From<TimeZone>())
for name in NSTimeZone.knownTimeZoneNames {
let rawTimeZone = NSTimeZone(name: name)!
let cachedTimeZone = transaction.create(Into<TimeZone>())
cachedTimeZone.name = rawTimeZone.name
cachedTimeZone.abbreviation = rawTimeZone.abbreviation ?? ""
cachedTimeZone.secondsFromGMT = rawTimeZone.secondsFromGMT
cachedTimeZone.isDaylightSavingTime = rawTimeZone.isDaylightSavingTime
cachedTimeZone.daylightSavingTimeOffset = rawTimeZone.daylightSavingTimeOffset
}
}
)
return dataStack
}()
}
}

View File

@@ -0,0 +1,58 @@
//
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
import SwiftUI
// MARK: - InstructionsView
struct InstructionsView: View {
// MARK: Internal
init(_ rows: (header: String, description: String)...) {
self.rows = rows.map({ .init(header: $0, description: $1) })
}
// MARK: View
var body: some View {
ZStack(alignment: .center) {
Color.white
.cornerRadius(10)
.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
HStack(alignment: .firstTextBaseline, spacing: 5) {
Text(row.header)
.font(.callout)
.fontWeight(.bold)
Text(row.description)
.font(.footnote)
}
}
}
.foregroundColor(Color(.sRGB, white: 0, opacity: 0.8))
.padding(.horizontal, 10)
.padding(.vertical, 4)
}
.fixedSize()
}
// MARK: Private
private let rows: [InstructionsView.Row]
// MARK: - Row
struct Row: Hashable {
// MARK: Internal
let header: String
let description: String
}
}

View File

@@ -0,0 +1,29 @@
//
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
import SwiftUI
// MARK: - LazyView
struct LazyView<Content: View>: View {
// MARK: Internal
init(_ load: @escaping () -> Content) {
self.load = load
}
// MARK: View
var body: Content {
self.load()
}
// MARK: Private
private let load: () -> Content
}

View File

@@ -0,0 +1,71 @@
//
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
import SwiftUI
// MARK: - Menu
extension Menu {
// MARK: - Menu.ItemView
struct ItemView<Destination: View>: View {
// MARK: Internal
init(
title: String,
subtitle: String? = nil,
destination: @escaping () -> Destination
) {
self.title = title
self.subtitle = subtitle
self.destination = destination
}
// MARK: View
var body: some View {
NavigationLink(destination: LazyView(self.destination)) {
VStack(alignment: .leading) {
Text(self.title)
.font(.headline)
.foregroundColor(.primary)
self.subtitle.map {
Text($0)
.font(.subheadline)
.foregroundColor(.secondary)
}
}
}
}
// MARK: FilePrivate
fileprivate let title: String
fileprivate let subtitle: String?
fileprivate let destination: () -> Destination
}
}
#if DEBUG
struct _Demo_Menu_ItemView_Preview: PreviewProvider {
// MARK: PreviewProvider
static var previews: some View {
Menu.ItemView(
title: "Item Title",
subtitle: "A subtitle caption for this item",
destination: {
Color.blue
}
)
}
}
#endif

View File

@@ -0,0 +1,161 @@
//
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
import Foundation
import SwiftUI
// MARK: - Menu
extension Menu {
// MARK: - Menu.MainView
struct MainView: View {
// MARK: View
var body: some View {
NavigationView {
List {
Section(header: Text("Modern (CoreStoreObject subclasses)")) {
Menu.ItemView(
title: "Placemarks",
subtitle: "Making changes using Transactions",
destination: {
Modern.PlacemarksDemo.MainView()
}
)
Menu.ItemView(
title: "Time Zones",
subtitle: "Fetching objects and Querying raw values",
destination: {
Modern.TimeZonesDemo.MainView()
}
)
Menu.ItemView(
title: "Colors (UIKit)",
subtitle: "Observing list changes and single-object changes using DiffableDataSources",
destination: {
Modern.ColorsDemo.MainView(
listView: { listPublisher, onPaletteTapped in
Modern.ColorsDemo.UIKit.ListView(
listPublisher: listPublisher,
onPaletteTapped: onPaletteTapped
)
.edgesIgnoringSafeArea(.all)
},
detailView: { objectPublisher in
Modern.ColorsDemo.UIKit.DetailView(objectPublisher)
}
)
}
)
Menu.ItemView(
title: "Colors (SwiftUI)",
subtitle: "Observing list changes and single-object changes using SwiftUI bindings",
destination: {
Modern.ColorsDemo.MainView(
listView: { listPublisher, onPaletteTapped in
Modern.ColorsDemo.SwiftUI.ListView(
listPublisher: listPublisher,
onPaletteTapped: onPaletteTapped
)
},
detailView: { objectPublisher in
Modern.ColorsDemo.SwiftUI.DetailView(objectPublisher)
}
)
}
)
Menu.ItemView(
title: "Pokedex API",
subtitle: "Importing JSON data from external source",
destination: { EmptyView() }
)
}
Section(header: Text("Classic (NSManagedObject subclasses)")) {
Menu.ItemView(
title: "Placemarks (Swift)",
subtitle: "Making changes using transactions in Swift",
destination: { EmptyView() }
)
Menu.ItemView(
title: "Placemarks (Objective-C)",
subtitle: "Making changes using transactions in Objective-C",
destination: { EmptyView() }
)
Menu.ItemView(
title: "Time Zones",
subtitle: "Fetching objects and Querying raw values",
destination: { EmptyView() }
)
Menu.ItemView(
title: "Colors (Swift)",
subtitle: "Observing list changes and single-object changes in Swift",
destination: { EmptyView() }
)
Menu.ItemView(
title: "Colors (Objective-C)",
subtitle: "Observing list changes and single-object changes in Objective-C",
destination: { EmptyView() }
)
Menu.ItemView(
title: "Pokedex API",
subtitle: "Importing JSON data from external source",
destination: { EmptyView() }
)
}
Section(header: Text("Advanced")) {
Menu.ItemView(
title: "Accounts",
subtitle: "Switching between multiple persistent stores",
destination: { EmptyView() }
)
Menu.ItemView(
title: "Evolution",
subtitle: "Migrating and reverse-migrating stores",
destination: { EmptyView() }
)
Menu.ItemView(
title: "Logger",
subtitle: "Implementing a custom logger",
destination: { EmptyView() }
)
}
}
.listStyle(GroupedListStyle())
.navigationBarTitle("CoreStore Demos")
Menu.DetailView()
}
.navigationViewStyle(DoubleColumnNavigationViewStyle())
}
}
fileprivate struct DetailView: View {
var selectedDate: Date?
var body: some View {
Group {
Text("Detail view content goes here")
}
.navigationBarTitle(Text("Detail"))
}
}
}
#if DEBUG
struct _Demo_Menu_MainView_Preview: PreviewProvider {
// MARK: PreviewProvider
static var previews: some View {
Menu.MainView()
}
}
#endif

View File

@@ -0,0 +1,10 @@
//
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
import Foundation
// MARK: - Menu
enum Menu {}

View File

@@ -0,0 +1,43 @@
//
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
import UIKit
// MARK: - UIImage
extension UIImage {
// MARK: Internal
convenience init(
color: UIColor,
size: CGSize = CGSize(width: 1, height: 1),
cornerRadius: CGFloat = 0
) {
let rect = CGRect(origin: .zero, size: size)
let scale = UIScreen.main.scale
UIGraphicsBeginImageContextWithOptions(rect.size, false, scale)
defer {
UIGraphicsEndImageContext()
}
let context = UIGraphicsGetCurrentContext()!
if cornerRadius > 0 {
UIBezierPath(
roundedRect: rect,
cornerRadius: cornerRadius
)
.addClip()
}
color.setFill()
context.fill(rect)
self.init(
cgImage: UIGraphicsGetImageFromCurrentImageContext()!.cgImage!,
scale: scale,
orientation: .up
)
}
}

View File

@@ -0,0 +1,36 @@
//
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
import SwiftUI
import UIKit
// MARK: - SceneDelegate
@objc final class SceneDelegate: UIResponder, UIWindowSceneDelegate {
// MARK: UIWindowSceneDelegate
@objc dynamic var window: UIWindow?
// MARK: UISceneDelegate
@objc dynamic func scene(
_ scene: UIScene,
willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions
) {
guard case let scene as UIWindowScene = scene else {
return
}
let window = UIWindow(windowScene: scene)
window.rootViewController = UIHostingController(
rootView: Menu.MainView()
)
self.window = window
window.makeKeyAndVisible()
}
}