mirror of
https://github.com/JohnEstropia/CoreStore.git
synced 2026-01-18 23:17:02 +01:00
added demo for classic ListMonitor
This commit is contained in:
@@ -25,7 +25,7 @@
|
||||
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 */; };
|
||||
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 */; };
|
||||
@@ -66,6 +66,16 @@
|
||||
B5A391B424E96C0A00E7E8BD /* ⭐️Modern.PokedexDemo.Form.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A391B324E96C0A00E7E8BD /* ⭐️Modern.PokedexDemo.Form.swift */; };
|
||||
B5A391B924E96F8500E7E8BD /* ⭐️Modern.PokedexDemo.Species.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A391B824E96F8500E7E8BD /* ⭐️Modern.PokedexDemo.Species.swift */; };
|
||||
B5A391BB24E970A400E7E8BD /* ⭐️Modern.PokedexDemo.PokemonType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A391BA24E970A400E7E8BD /* ⭐️Modern.PokedexDemo.PokemonType.swift */; };
|
||||
B5A543D724FB7478000DC5E3 /* Classic.ColorsDemo.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A543D624FB7478000DC5E3 /* Classic.ColorsDemo.swift */; };
|
||||
B5A543DB24FB7513000DC5E3 /* Classic.ColorsDemo.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = B5A543D924FB7513000DC5E3 /* Classic.ColorsDemo.xcdatamodeld */; };
|
||||
B5A543DD24FB78F9000DC5E3 /* Classic.ColorsDemo.Palette.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A543DC24FB78F9000DC5E3 /* Classic.ColorsDemo.Palette.swift */; };
|
||||
B5A543E724FB82BB000DC5E3 /* Classic.ColorsDemo.Filter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A543E624FB82BB000DC5E3 /* Classic.ColorsDemo.Filter.swift */; };
|
||||
B5A543E924FB84A1000DC5E3 /* Classic.ColorsDemo.DetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A543E824FB84A1000DC5E3 /* Classic.ColorsDemo.DetailView.swift */; };
|
||||
B5A543EB24FB84AF000DC5E3 /* ⭐️Classic.ColorsDemo.DetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A543EA24FB84AF000DC5E3 /* ⭐️Classic.ColorsDemo.DetailViewController.swift */; };
|
||||
B5A543ED24FB84BE000DC5E3 /* Classic.ColorsDemo.MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A543EC24FB84BE000DC5E3 /* Classic.ColorsDemo.MainView.swift */; };
|
||||
B5A543EF24FB84D1000DC5E3 /* Classic.ColorsDemo.LIstView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A543EE24FB84D1000DC5E3 /* Classic.ColorsDemo.LIstView.swift */; };
|
||||
B5A543F124FB84DD000DC5E3 /* ⭐️Classic.ColorsDemo.ListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A543F024FB84DD000DC5E3 /* ⭐️Classic.ColorsDemo.ListViewController.swift */; };
|
||||
B5A543F324FB84EC000DC5E3 /* Classic.ColorsDemo.ItemCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A543F224FB84EC000DC5E3 /* Classic.ColorsDemo.ItemCell.swift */; };
|
||||
B5E32C9024FA41F9003F46AD /* ImageDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E32C8F24FA41F9003F46AD /* ImageDownloader.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
@@ -108,7 +118,7 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
@@ -145,6 +155,16 @@
|
||||
B5A391B324E96C0A00E7E8BD /* ⭐️Modern.PokedexDemo.Form.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "⭐️Modern.PokedexDemo.Form.swift"; sourceTree = "<group>"; };
|
||||
B5A391B824E96F8500E7E8BD /* ⭐️Modern.PokedexDemo.Species.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "⭐️Modern.PokedexDemo.Species.swift"; sourceTree = "<group>"; };
|
||||
B5A391BA24E970A400E7E8BD /* ⭐️Modern.PokedexDemo.PokemonType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "⭐️Modern.PokedexDemo.PokemonType.swift"; sourceTree = "<group>"; };
|
||||
B5A543D624FB7478000DC5E3 /* Classic.ColorsDemo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Classic.ColorsDemo.swift; sourceTree = "<group>"; };
|
||||
B5A543DA24FB7513000DC5E3 /* ColorsDemo.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = ColorsDemo.xcdatamodel; sourceTree = "<group>"; };
|
||||
B5A543DC24FB78F9000DC5E3 /* Classic.ColorsDemo.Palette.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Classic.ColorsDemo.Palette.swift; sourceTree = "<group>"; };
|
||||
B5A543E624FB82BB000DC5E3 /* Classic.ColorsDemo.Filter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Classic.ColorsDemo.Filter.swift; sourceTree = "<group>"; };
|
||||
B5A543E824FB84A1000DC5E3 /* Classic.ColorsDemo.DetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Classic.ColorsDemo.DetailView.swift; sourceTree = "<group>"; };
|
||||
B5A543EA24FB84AF000DC5E3 /* ⭐️Classic.ColorsDemo.DetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "⭐️Classic.ColorsDemo.DetailViewController.swift"; sourceTree = "<group>"; };
|
||||
B5A543EC24FB84BE000DC5E3 /* Classic.ColorsDemo.MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Classic.ColorsDemo.MainView.swift; sourceTree = "<group>"; };
|
||||
B5A543EE24FB84D1000DC5E3 /* Classic.ColorsDemo.LIstView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Classic.ColorsDemo.LIstView.swift; sourceTree = "<group>"; };
|
||||
B5A543F024FB84DD000DC5E3 /* ⭐️Classic.ColorsDemo.ListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "⭐️Classic.ColorsDemo.ListViewController.swift"; sourceTree = "<group>"; };
|
||||
B5A543F224FB84EC000DC5E3 /* Classic.ColorsDemo.ItemCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Classic.ColorsDemo.ItemCell.swift; sourceTree = "<group>"; };
|
||||
B5E32C8F24FA41F9003F46AD /* ImageDownloader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageDownloader.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
@@ -170,7 +190,7 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B5A3913D24E62C6C00E7E8BD /* Metadata */,
|
||||
B5A3911B24E5429200E7E8BD /* Sources */,
|
||||
B5A3911B24E5429200E7E8BD /* ⭐️Sources */,
|
||||
B5A3913E24E62CB200E7E8BD /* Resources */,
|
||||
B5A3911A24E5429200E7E8BD /* Products */,
|
||||
B5A3916624E698F900E7E8BD /* Frameworks */,
|
||||
@@ -185,15 +205,15 @@
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B5A3911B24E5429200E7E8BD /* Sources */ = {
|
||||
B5A3911B24E5429200E7E8BD /* ⭐️Sources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B5A3911C24E5429200E7E8BD /* AppDelegate.swift */,
|
||||
B5A3911E24E5429200E7E8BD /* SceneDelegate.swift */,
|
||||
B5A3915524E6858A00E7E8BD /* Demos */,
|
||||
B5A3915524E6858A00E7E8BD /* ⭐️Demos */,
|
||||
B5A3917A24E6A75F00E7E8BD /* Helpers */,
|
||||
);
|
||||
path = Sources;
|
||||
path = "⭐️Sources";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B5A3912424E5429300E7E8BD /* Preview Content */ = {
|
||||
@@ -234,45 +254,46 @@
|
||||
path = Menu;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B5A3915524E6858A00E7E8BD /* Demos */ = {
|
||||
B5A3915524E6858A00E7E8BD /* ⭐️Demos */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B5A3915624E685B700E7E8BD /* Modern */,
|
||||
B5A3915724E685D300E7E8BD /* Classic */,
|
||||
B5A3915624E685B700E7E8BD /* ⭐️Modern */,
|
||||
B5A3915724E685D300E7E8BD /* ⭐️Classic */,
|
||||
);
|
||||
path = Demos;
|
||||
path = "⭐️Demos";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B5A3915624E685B700E7E8BD /* Modern */ = {
|
||||
B5A3915624E685B700E7E8BD /* ⭐️Modern */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B5A3915A24E685FE00E7E8BD /* Modern.swift */,
|
||||
B5A3915C24E6921E00E7E8BD /* PlacemarksDemo */,
|
||||
B5A3918124E7A1EF00E7E8BD /* TimeZonesDemo */,
|
||||
B5A3918D24E7DE7A00E7E8BD /* ColorsDemo */,
|
||||
B5A391AF24E96AD600E7E8BD /* PokedexDemo */,
|
||||
B5A3915C24E6921E00E7E8BD /* ⭐️PlacemarksDemo */,
|
||||
B5A3918124E7A1EF00E7E8BD /* ⭐️TimeZonesDemo */,
|
||||
B5A3918D24E7DE7A00E7E8BD /* ⭐️ColorsDemo */,
|
||||
B5A391AF24E96AD600E7E8BD /* ⭐️PokedexDemo */,
|
||||
);
|
||||
path = Modern;
|
||||
path = "⭐️Modern";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B5A3915724E685D300E7E8BD /* Classic */ = {
|
||||
B5A3915724E685D300E7E8BD /* ⭐️Classic */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B5A3915824E685EC00E7E8BD /* Classic.swift */,
|
||||
B5A543D524FB73B2000DC5E3 /* ⭐️ColorsDemo */,
|
||||
);
|
||||
path = Classic;
|
||||
path = "⭐️Classic";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B5A3915C24E6921E00E7E8BD /* PlacemarksDemo */ = {
|
||||
B5A3915C24E6921E00E7E8BD /* ⭐️PlacemarksDemo */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B5A3915D24E6922E00E7E8BD /* Modern.PlacemarksDemo.swift */,
|
||||
B5A3915D24E6922E00E7E8BD /* ⭐️Modern.PlacemarksDemo.swift */,
|
||||
B5A3916124E697BA00E7E8BD /* ⭐️Modern.PlacemarksDemo.MainView.swift */,
|
||||
B5A3915F24E6925900E7E8BD /* Modern.PlacemarksDemo.MapView.swift */,
|
||||
B5A3917D24E7728400E7E8BD /* Modern.PlacemarksDemo.Geocoder.swift */,
|
||||
B5A3916324E698B300E7E8BD /* Models */,
|
||||
);
|
||||
path = PlacemarksDemo;
|
||||
path = "⭐️PlacemarksDemo";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B5A3916324E698B300E7E8BD /* Models */ = {
|
||||
@@ -308,7 +329,7 @@
|
||||
path = Helpers;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B5A3918124E7A1EF00E7E8BD /* TimeZonesDemo */ = {
|
||||
B5A3918124E7A1EF00E7E8BD /* ⭐️TimeZonesDemo */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B5A3918224E7A21800E7E8BD /* Modern.TimeZonesDemo.swift */,
|
||||
@@ -317,7 +338,7 @@
|
||||
B5A3918B24E7B44B00E7E8BD /* Modern.TimeZonesDemo.ItemView.swift */,
|
||||
B5A3918424E7A53300E7E8BD /* Models */,
|
||||
);
|
||||
path = TimeZonesDemo;
|
||||
path = "⭐️TimeZonesDemo";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B5A3918424E7A53300E7E8BD /* Models */ = {
|
||||
@@ -328,17 +349,17 @@
|
||||
name = Models;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B5A3918D24E7DE7A00E7E8BD /* ColorsDemo */ = {
|
||||
B5A3918D24E7DE7A00E7E8BD /* ⭐️ColorsDemo */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B5A3918E24E7E06500E7E8BD /* Modern.ColorsDemo.swift */,
|
||||
B5A3919724E7E67000E7E8BD /* Modern.ColorsDemo.Filter.swift */,
|
||||
B5A391A524E8F4EA00E7E8BD /* ⭐️Modern.ColorsDemo.MainView.swift */,
|
||||
B5A3919C24E8EE9000E7E8BD /* UIKit */,
|
||||
B5A3919B24E8EE8100E7E8BD /* SwiftUI */,
|
||||
B5A3919C24E8EE9000E7E8BD /* ⭐️UIKit */,
|
||||
B5A3919B24E8EE8100E7E8BD /* ⭐️SwiftUI */,
|
||||
B5A3919024E7E0B000E7E8BD /* Models */,
|
||||
);
|
||||
path = ColorsDemo;
|
||||
path = "⭐️ColorsDemo";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B5A3919024E7E0B000E7E8BD /* Models */ = {
|
||||
@@ -349,7 +370,7 @@
|
||||
name = Models;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B5A3919B24E8EE8100E7E8BD /* SwiftUI */ = {
|
||||
B5A3919B24E8EE8100E7E8BD /* ⭐️SwiftUI */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B5A3919D24E8EEB600E7E8BD /* Modern.ColorsDemo.SwiftUI.swift */,
|
||||
@@ -357,10 +378,10 @@
|
||||
B5A3919524E7E4AC00E7E8BD /* ⭐️Modern.ColorsDemo.SwiftUI.ItemView.swift */,
|
||||
B5A3919924E8207A00E7E8BD /* ⭐️Modern.ColorsDemo.SwiftUI.DetailView.swift */,
|
||||
);
|
||||
name = SwiftUI;
|
||||
name = "⭐️SwiftUI";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B5A3919C24E8EE9000E7E8BD /* UIKit */ = {
|
||||
B5A3919C24E8EE9000E7E8BD /* ⭐️UIKit */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B5A3919F24E8F00A00E7E8BD /* Modern.ColorsDemo.UIKit.swift */,
|
||||
@@ -370,10 +391,10 @@
|
||||
B5A391AB24E9143B00E7E8BD /* Modern.ColorsDemo.UIKit.DetailView.swift */,
|
||||
B5A391AD24E9150F00E7E8BD /* ⭐️Modern.ColorsDemo.UIKit.DetailViewController.swift */,
|
||||
);
|
||||
name = UIKit;
|
||||
name = "⭐️UIKit";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B5A391AF24E96AD600E7E8BD /* PokedexDemo */ = {
|
||||
B5A391AF24E96AD600E7E8BD /* ⭐️PokedexDemo */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B5A391B024E96AF600E7E8BD /* Modern.PokedexDemo.swift */,
|
||||
@@ -382,29 +403,54 @@
|
||||
B566C8E724F9D406001134A1 /* Modern.PokedexDemo.ListView.swift */,
|
||||
B566C8E924F9D412001134A1 /* Modern.PokedexDemo.ListViewController.swift */,
|
||||
B566C8EB24F9D694001134A1 /* Modern.PokedexDemo.ItemCell.swift */,
|
||||
B5A391B224E96B7400E7E8BD /* Models */,
|
||||
B5A391B224E96B7400E7E8BD /* ⭐️Models */,
|
||||
);
|
||||
path = PokedexDemo;
|
||||
path = "⭐️PokedexDemo";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B5A391B224E96B7400E7E8BD /* Models */ = {
|
||||
B5A391B224E96B7400E7E8BD /* ⭐️Models */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B531EFE824EB5A52005F247D /* ⭐️Modern.PokedexDemo.PokedexEntry.swift */,
|
||||
B566C8ED24FA1EA3001134A1 /* Modern.PokedexDemo.Details.swift */,
|
||||
B5A391B824E96F8500E7E8BD /* ⭐️Modern.PokedexDemo.Species.swift */,
|
||||
B5A391B324E96C0A00E7E8BD /* ⭐️Modern.PokedexDemo.Form.swift */,
|
||||
B5A391B724E96E8600E7E8BD /* Attributes */,
|
||||
B5A391B724E96E8600E7E8BD /* ⭐️Attributes */,
|
||||
);
|
||||
name = Models;
|
||||
name = "⭐️Models";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B5A391B724E96E8600E7E8BD /* Attributes */ = {
|
||||
B5A391B724E96E8600E7E8BD /* ⭐️Attributes */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B5A391BA24E970A400E7E8BD /* ⭐️Modern.PokedexDemo.PokemonType.swift */,
|
||||
);
|
||||
name = Attributes;
|
||||
name = "⭐️Attributes";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B5A543D524FB73B2000DC5E3 /* ⭐️ColorsDemo */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B5A543D624FB7478000DC5E3 /* Classic.ColorsDemo.swift */,
|
||||
B5A543E624FB82BB000DC5E3 /* Classic.ColorsDemo.Filter.swift */,
|
||||
B5A543EC24FB84BE000DC5E3 /* Classic.ColorsDemo.MainView.swift */,
|
||||
B5A543EE24FB84D1000DC5E3 /* Classic.ColorsDemo.LIstView.swift */,
|
||||
B5A543F024FB84DD000DC5E3 /* ⭐️Classic.ColorsDemo.ListViewController.swift */,
|
||||
B5A543F224FB84EC000DC5E3 /* Classic.ColorsDemo.ItemCell.swift */,
|
||||
B5A543E824FB84A1000DC5E3 /* Classic.ColorsDemo.DetailView.swift */,
|
||||
B5A543EA24FB84AF000DC5E3 /* ⭐️Classic.ColorsDemo.DetailViewController.swift */,
|
||||
B5A543D824FB7483000DC5E3 /* Models */,
|
||||
);
|
||||
path = "⭐️ColorsDemo";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B5A543D824FB7483000DC5E3 /* Models */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B5A543D924FB7513000DC5E3 /* Classic.ColorsDemo.xcdatamodeld */,
|
||||
B5A543DC24FB78F9000DC5E3 /* Classic.ColorsDemo.Palette.swift */,
|
||||
);
|
||||
name = Models;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
@@ -484,19 +530,25 @@
|
||||
B5A3918024E787D900E7E8BD /* InstructionsView.swift in Sources */,
|
||||
B5A3917C24E6A76C00E7E8BD /* LazyView.swift in Sources */,
|
||||
B5A3913424E6170500E7E8BD /* Menu.swift in Sources */,
|
||||
B5A543DB24FB7513000DC5E3 /* Classic.ColorsDemo.xcdatamodeld in Sources */,
|
||||
B5A3915B24E685FE00E7E8BD /* Modern.swift in Sources */,
|
||||
B5A543D724FB7478000DC5E3 /* Classic.ColorsDemo.swift in Sources */,
|
||||
B5A3911F24E5429200E7E8BD /* SceneDelegate.swift in Sources */,
|
||||
B5A3915324E6537F00E7E8BD /* Menu.ItemView.swift in Sources */,
|
||||
B5A3912124E5429200E7E8BD /* Menu.MainView.swift in Sources */,
|
||||
B531EFE724EA762D005F247D /* Menu.PlaceholderView.swift in Sources */,
|
||||
B5A3918F24E7E06500E7E8BD /* Modern.ColorsDemo.swift in Sources */,
|
||||
B5A3915E24E6922E00E7E8BD /* Modern.PlacemarksDemo.swift in Sources */,
|
||||
B5A543EF24FB84D1000DC5E3 /* Classic.ColorsDemo.LIstView.swift in Sources */,
|
||||
B5A3915E24E6922E00E7E8BD /* ⭐️Modern.PlacemarksDemo.swift in Sources */,
|
||||
B5A543F324FB84EC000DC5E3 /* Classic.ColorsDemo.ItemCell.swift in Sources */,
|
||||
B5A391B124E96AF600E7E8BD /* Modern.PokedexDemo.swift in Sources */,
|
||||
B5A3918324E7A21800E7E8BD /* Modern.TimeZonesDemo.swift in Sources */,
|
||||
B5A3919824E7E67000E7E8BD /* Modern.ColorsDemo.Filter.swift in Sources */,
|
||||
B5A3919224E7E0C600E7E8BD /* Modern.ColorsDemo.Palette.swift in Sources */,
|
||||
B5A3919E24E8EEB600E7E8BD /* Modern.ColorsDemo.SwiftUI.swift in Sources */,
|
||||
B5A391A024E8F00A00E7E8BD /* Modern.ColorsDemo.UIKit.swift in Sources */,
|
||||
B5A543E724FB82BB000DC5E3 /* Classic.ColorsDemo.Filter.swift in Sources */,
|
||||
B5A543ED24FB84BE000DC5E3 /* Classic.ColorsDemo.MainView.swift in Sources */,
|
||||
B5A3917E24E7728400E7E8BD /* Modern.PlacemarksDemo.Geocoder.swift in Sources */,
|
||||
B5A3916024E6925900E7E8BD /* Modern.PlacemarksDemo.MapView.swift in Sources */,
|
||||
B5A3916524E698C700E7E8BD /* Modern.PlacemarksDemo.Place.swift in Sources */,
|
||||
@@ -510,6 +562,7 @@
|
||||
B5A3918624E7A54A00E7E8BD /* Modern.TimeZonesDemo.TimeZone.swift in Sources */,
|
||||
B5A391A624E8F4EA00E7E8BD /* ⭐️Modern.ColorsDemo.MainView.swift in Sources */,
|
||||
B5A3916224E697BA00E7E8BD /* ⭐️Modern.PlacemarksDemo.MainView.swift in Sources */,
|
||||
B5A543EB24FB84AF000DC5E3 /* ⭐️Classic.ColorsDemo.DetailViewController.swift in Sources */,
|
||||
B5A391B424E96C0A00E7E8BD /* ⭐️Modern.PokedexDemo.Form.swift in Sources */,
|
||||
B531EFE924EB5A53005F247D /* ⭐️Modern.PokedexDemo.PokedexEntry.swift in Sources */,
|
||||
B5A391BB24E970A400E7E8BD /* ⭐️Modern.PokedexDemo.PokemonType.swift in Sources */,
|
||||
@@ -519,11 +572,14 @@
|
||||
B5A391AC24E9143B00E7E8BD /* Modern.ColorsDemo.UIKit.DetailView.swift in Sources */,
|
||||
B5A391AA24E9104300E7E8BD /* Modern.ColorsDemo.UIKit.ItemCell.swift in Sources */,
|
||||
B5A391A424E8F04300E7E8BD /* Modern.ColorsDemo.UIKit.ListView.swift in Sources */,
|
||||
B5A543E924FB84A1000DC5E3 /* Classic.ColorsDemo.DetailView.swift in Sources */,
|
||||
B5A3919A24E8207A00E7E8BD /* ⭐️Modern.ColorsDemo.SwiftUI.DetailView.swift in Sources */,
|
||||
B5A543F124FB84DD000DC5E3 /* ⭐️Classic.ColorsDemo.ListViewController.swift in Sources */,
|
||||
B5A3919624E7E4AC00E7E8BD /* ⭐️Modern.ColorsDemo.SwiftUI.ItemView.swift in Sources */,
|
||||
B5A3919424E7E36700E7E8BD /* ⭐️Modern.ColorsDemo.SwiftUI.ListView.swift in Sources */,
|
||||
B5A391AE24E9150F00E7E8BD /* ⭐️Modern.ColorsDemo.UIKit.DetailViewController.swift in Sources */,
|
||||
B5A391A224E8F01F00E7E8BD /* ⭐️Modern.ColorsDemo.UIKit.ListViewController.swift in Sources */,
|
||||
B5A543DD24FB78F9000DC5E3 /* Classic.ColorsDemo.Palette.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -717,6 +773,19 @@
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCVersionGroup section */
|
||||
B5A543D924FB7513000DC5E3 /* Classic.ColorsDemo.xcdatamodeld */ = {
|
||||
isa = XCVersionGroup;
|
||||
children = (
|
||||
B5A543DA24FB7513000DC5E3 /* ColorsDemo.xcdatamodel */,
|
||||
);
|
||||
currentVersion = B5A543DA24FB7513000DC5E3 /* ColorsDemo.xcdatamodel */;
|
||||
path = Classic.ColorsDemo.xcdatamodeld;
|
||||
sourceTree = "<group>";
|
||||
versionGroupType = wrapper.xcdatamodel;
|
||||
};
|
||||
/* End XCVersionGroup section */
|
||||
};
|
||||
rootObject = B5A3910F24E5424E00E7E8BD /* Project object */;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
//
|
||||
// ImageDownloader.swift
|
||||
// Demo
|
||||
//
|
||||
// Created by John Rommel Estropia on 2020/08/29.
|
||||
//
|
||||
// Demo
|
||||
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
@@ -79,34 +79,23 @@ extension Menu {
|
||||
}
|
||||
Section(header: Text("Classic (NSManagedObject subclasses)")) {
|
||||
Menu.ItemView(
|
||||
title: "Placemarks (Swift)",
|
||||
title: "Placemarks",
|
||||
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() }
|
||||
)
|
||||
.disabled(true)
|
||||
Menu.ItemView(
|
||||
title: "Time Zones",
|
||||
subtitle: "Fetching objects and Querying raw values",
|
||||
destination: { EmptyView() }
|
||||
)
|
||||
.disabled(true)
|
||||
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() }
|
||||
title: "Colors",
|
||||
subtitle: "Observing list changes and single-object changes using ListMonitor",
|
||||
destination: {
|
||||
Classic.ColorsDemo.MainView()
|
||||
}
|
||||
)
|
||||
}
|
||||
Section(header: Text("Advanced")) {
|
||||
@@ -0,0 +1,79 @@
|
||||
//
|
||||
// Demo
|
||||
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
|
||||
|
||||
import Combine
|
||||
import CoreStore
|
||||
import SwiftUI
|
||||
|
||||
// MARK: - Classic.ColorsDemo
|
||||
|
||||
extension Classic.ColorsDemo {
|
||||
|
||||
// MARK: - Classic.ColorsDemo.DetailView
|
||||
|
||||
struct DetailView: UIViewControllerRepresentable {
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
init(_ palette: ObjectMonitor<Classic.ColorsDemo.Palette>) {
|
||||
|
||||
self.palette = palette
|
||||
}
|
||||
|
||||
// MARK: UIViewControllerRepresentable
|
||||
|
||||
typealias UIViewControllerType = Classic.ColorsDemo.DetailViewController
|
||||
|
||||
func makeUIViewController(context: Self.Context) -> UIViewControllerType {
|
||||
|
||||
return UIViewControllerType(self.palette)
|
||||
}
|
||||
|
||||
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Self.Context) {
|
||||
|
||||
uiViewController.palette = self.palette
|
||||
}
|
||||
|
||||
static func dismantleUIViewController(_ uiViewController: UIViewControllerType, coordinator: Void) {}
|
||||
|
||||
func makeCoordinator() -> ObjectMonitor<Classic.ColorsDemo.Palette> {
|
||||
|
||||
return self.palette
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let palette: ObjectMonitor<Classic.ColorsDemo.Palette>
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
|
||||
struct _Demo_Classic_ColorsDemo_DetailView_Preview: PreviewProvider {
|
||||
|
||||
// MARK: PreviewProvider
|
||||
|
||||
static var previews: some View {
|
||||
|
||||
try! Classic.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 Classic.ColorsDemo.DetailView(
|
||||
Classic.ColorsDemo.dataStack.monitorObject(
|
||||
Classic.ColorsDemo.palettesMonitor[0, 0]
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,36 @@
|
||||
//
|
||||
// Demo
|
||||
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
|
||||
|
||||
import CoreStore
|
||||
|
||||
|
||||
// MARK: - Classic.ColorsDemo
|
||||
|
||||
extension Classic.ColorsDemo {
|
||||
|
||||
// MARK: - Classic.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<Classic.ColorsDemo.Palette> {
|
||||
|
||||
switch self {
|
||||
|
||||
case .all: return .init()
|
||||
case .light: return (\.brightness >= 0.9)
|
||||
case .dark: return (\.brightness <= 0.4)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// Demo
|
||||
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
|
||||
|
||||
import CoreStore
|
||||
import UIKit
|
||||
|
||||
|
||||
// MARK: - Classic.ColorsDemo
|
||||
|
||||
extension Classic.ColorsDemo {
|
||||
|
||||
// MARK: - Classic.ColorsDemo.ItemCell
|
||||
|
||||
final class ItemCell: UITableViewCell {
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
static let reuseIdentifier: String = NSStringFromClass(Classic.ColorsDemo.ItemCell.self)
|
||||
|
||||
func setPalette(_ palette: Classic.ColorsDemo.Palette) {
|
||||
|
||||
self.contentView.backgroundColor = palette.color
|
||||
self.textLabel?.text = palette.colorText
|
||||
self.textLabel?.textColor = palette.brightness > 0.6 ? .black : .white
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
//
|
||||
// Demo
|
||||
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
|
||||
|
||||
import CoreStore
|
||||
import SwiftUI
|
||||
|
||||
// MARK: - Classic.ColorsDemo
|
||||
|
||||
extension Classic.ColorsDemo {
|
||||
|
||||
// MARK: - Classic.ColorsDemo.ListView
|
||||
|
||||
struct ListView: UIViewControllerRepresentable {
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
init(
|
||||
listMonitor: ListMonitor<Classic.ColorsDemo.Palette>,
|
||||
onPaletteTapped: @escaping (Classic.ColorsDemo.Palette) -> Void
|
||||
) {
|
||||
|
||||
self.listMonitor = listMonitor
|
||||
self.onPaletteTapped = onPaletteTapped
|
||||
}
|
||||
|
||||
|
||||
// MARK: UIViewControllerRepresentable
|
||||
|
||||
typealias UIViewControllerType = Classic.ColorsDemo.ListViewController
|
||||
|
||||
func makeUIViewController(context: Self.Context) -> UIViewControllerType {
|
||||
|
||||
return UIViewControllerType(
|
||||
listMonitor: self.listMonitor,
|
||||
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 listMonitor: ListMonitor<Classic.ColorsDemo.Palette>
|
||||
private let onPaletteTapped: (Classic.ColorsDemo.Palette) -> Void
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
|
||||
struct _Demo_Classic_ColorsDemo_ListView_Preview: PreviewProvider {
|
||||
|
||||
// MARK: PreviewProvider
|
||||
|
||||
static var previews: some View {
|
||||
|
||||
let minimumSamples = 10
|
||||
try! Classic.ColorsDemo.dataStack.perform(
|
||||
synchronous: { transaction in
|
||||
|
||||
let missing = minimumSamples
|
||||
- (try transaction.fetchCount(From<Classic.ColorsDemo.Palette>()))
|
||||
guard missing > 0 else {
|
||||
return
|
||||
}
|
||||
for _ in 0..<missing {
|
||||
|
||||
let palette = transaction.create(Into<Classic.ColorsDemo.Palette>())
|
||||
palette.setRandomHue()
|
||||
}
|
||||
}
|
||||
)
|
||||
return Classic.ColorsDemo.ListView(
|
||||
listMonitor: Classic.ColorsDemo.palettesMonitor,
|
||||
onPaletteTapped: { _ in }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,247 @@
|
||||
//
|
||||
// Demo
|
||||
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
|
||||
|
||||
import CoreStore
|
||||
import SwiftUI
|
||||
|
||||
// MARK: - Classic.ColorsDemo
|
||||
|
||||
extension Classic.ColorsDemo {
|
||||
|
||||
// MARK: - Classic.ColorsDemo.MainView
|
||||
|
||||
struct MainView: View {
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
init() {
|
||||
|
||||
let listMonitor = Classic.ColorsDemo.palettesMonitor
|
||||
self.listMonitor = listMonitor
|
||||
self.listHelper = .init(listMonitor: listMonitor)
|
||||
self._filter = Binding(
|
||||
get: { Classic.ColorsDemo.filter },
|
||||
set: { Classic.ColorsDemo.filter = $0 }
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// MARK: View
|
||||
|
||||
var body: some View {
|
||||
let detailView: AnyView
|
||||
if let selectedObject = self.listHelper.selectedObject() {
|
||||
|
||||
detailView = AnyView(
|
||||
Classic.ColorsDemo.DetailView(selectedObject)
|
||||
)
|
||||
}
|
||||
else {
|
||||
|
||||
detailView = AnyView(EmptyView())
|
||||
}
|
||||
let listMonitor = self.listMonitor
|
||||
return VStack(spacing: 0) {
|
||||
Classic.ColorsDemo.ListView
|
||||
.init(
|
||||
listMonitor: listMonitor,
|
||||
onPaletteTapped: {
|
||||
|
||||
self.listHelper.setSelectedPalette($0)
|
||||
}
|
||||
)
|
||||
.navigationBarTitle(
|
||||
Text("Colors (\(self.listHelper.count) objects)")
|
||||
)
|
||||
.frame(minHeight: 0, maxHeight: .infinity)
|
||||
.edgesIgnoringSafeArea(.vertical)
|
||||
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 listMonitor: ListMonitor<Classic.ColorsDemo.Palette>
|
||||
|
||||
@ObservedObject
|
||||
private var listHelper: ListHelper
|
||||
|
||||
@Binding
|
||||
private var filter: Classic.ColorsDemo.Filter
|
||||
|
||||
private func changeFilter() {
|
||||
|
||||
Classic.ColorsDemo.filter = Classic.ColorsDemo.filter.next()
|
||||
}
|
||||
|
||||
private func clearColors() {
|
||||
|
||||
Classic.ColorsDemo.dataStack.perform(
|
||||
asynchronous: { transaction in
|
||||
|
||||
try transaction.deleteAll(From<Classic.ColorsDemo.Palette>())
|
||||
},
|
||||
completion: { _ in }
|
||||
)
|
||||
}
|
||||
|
||||
private func addColor() {
|
||||
|
||||
Classic.ColorsDemo.dataStack.perform(
|
||||
asynchronous: { transaction in
|
||||
|
||||
_ = transaction.create(Into<Classic.ColorsDemo.Palette>())
|
||||
},
|
||||
completion: { _ in }
|
||||
)
|
||||
}
|
||||
|
||||
private func shuffleColors() {
|
||||
|
||||
Classic.ColorsDemo.dataStack.perform(
|
||||
asynchronous: { transaction in
|
||||
|
||||
for palette in try transaction.fetchAll(From<Classic.ColorsDemo.Palette>()) {
|
||||
|
||||
palette.setRandomHue()
|
||||
}
|
||||
},
|
||||
completion: { _ in }
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Classic.ColorsDemo.MainView.ListHelper
|
||||
|
||||
fileprivate final class ListHelper: ObservableObject, ListObjectObserver {
|
||||
|
||||
// MARK: FilePrivate
|
||||
|
||||
fileprivate private(set) var count: Int = 0
|
||||
|
||||
fileprivate init(listMonitor: ListMonitor<Classic.ColorsDemo.Palette>) {
|
||||
|
||||
listMonitor.addObserver(self)
|
||||
self.count = listMonitor.numberOfObjects()
|
||||
}
|
||||
|
||||
fileprivate func selectedObject() -> ObjectMonitor<Classic.ColorsDemo.Palette>? {
|
||||
|
||||
return self.selectedPalette.flatMap {
|
||||
|
||||
guard !$0.isDeleted else {
|
||||
|
||||
return nil
|
||||
}
|
||||
return Classic.ColorsDemo.dataStack.monitorObject($0)
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func setSelectedPalette(_ palette: Classic.ColorsDemo.Palette?) {
|
||||
|
||||
guard self.selectedPalette != palette else {
|
||||
|
||||
return
|
||||
}
|
||||
self.objectWillChange.send()
|
||||
if let palette = palette, !palette.isDeleted {
|
||||
|
||||
self.selectedPalette = palette
|
||||
}
|
||||
else {
|
||||
|
||||
self.selectedPalette = nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: ListObserver
|
||||
|
||||
typealias ListEntityType = Classic.ColorsDemo.Palette
|
||||
|
||||
func listMonitorDidChange(_ monitor: ListMonitor<Classic.ColorsDemo.Palette>) {
|
||||
|
||||
self.objectWillChange.send()
|
||||
self.count = monitor.numberOfObjects()
|
||||
}
|
||||
|
||||
func listMonitorDidRefetch(_ monitor: ListMonitor<ListEntityType>) {
|
||||
|
||||
self.objectWillChange.send()
|
||||
self.count = monitor.numberOfObjects()
|
||||
}
|
||||
|
||||
// MARK: ListObjectObserver
|
||||
|
||||
func listMonitor(_ monitor: ListMonitor<Classic.ColorsDemo.Palette>, didDeleteObject object: Classic.ColorsDemo.Palette, fromIndexPath indexPath: IndexPath) {
|
||||
|
||||
if self.selectedPalette == object {
|
||||
|
||||
self.setSelectedPalette(nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private var selectedPalette: Classic.ColorsDemo.Palette?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
|
||||
struct _Demo_Classic_ColorsDemo_MainView_Preview: PreviewProvider {
|
||||
|
||||
// MARK: PreviewProvider
|
||||
|
||||
static var previews: some View {
|
||||
|
||||
let minimumSamples = 10
|
||||
try! Classic.ColorsDemo.dataStack.perform(
|
||||
synchronous: { transaction in
|
||||
|
||||
let missing = minimumSamples
|
||||
- (try transaction.fetchCount(From<Classic.ColorsDemo.Palette>()))
|
||||
guard missing > 0 else {
|
||||
return
|
||||
}
|
||||
for _ in 0..<missing {
|
||||
|
||||
let palette = transaction.create(Into<Classic.ColorsDemo.Palette>())
|
||||
palette.setRandomHue()
|
||||
}
|
||||
}
|
||||
)
|
||||
return Classic.ColorsDemo.MainView()
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,101 @@
|
||||
//
|
||||
// Demo
|
||||
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
|
||||
|
||||
import CoreData
|
||||
import UIKit
|
||||
|
||||
|
||||
// MARK: - Classic.ColorsDemo.Palette
|
||||
|
||||
@objc(Classic_ColorsDemo_Palette)
|
||||
final class Classic_ColorsDemo_Palette: NSManagedObject {
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
@NSManaged
|
||||
dynamic var hue: Float
|
||||
|
||||
@NSManaged
|
||||
dynamic var saturation: Float
|
||||
|
||||
@NSManaged
|
||||
dynamic var brightness: Float
|
||||
|
||||
@objc
|
||||
dynamic var colorGroup: String! {
|
||||
|
||||
let key = #keyPath(colorGroup)
|
||||
if case let value as String = self.getValue(forKvcKey: key) {
|
||||
|
||||
return value
|
||||
}
|
||||
let newValue: String
|
||||
switch self.hue * 359 {
|
||||
|
||||
case 0 ..< 20: newValue = "Lower Reds"
|
||||
case 20 ..< 57: newValue = "Oranges and Browns"
|
||||
case 57 ..< 90: newValue = "Yellow-Greens"
|
||||
case 90 ..< 159: newValue = "Greens"
|
||||
case 159 ..< 197: newValue = "Blue-Greens"
|
||||
case 197 ..< 241: newValue = "Blues"
|
||||
case 241 ..< 297: newValue = "Violets"
|
||||
case 297 ..< 331: newValue = "Magentas"
|
||||
default: newValue = "Upper Reds"
|
||||
}
|
||||
self.setPrimitiveValue(newValue, forKey: key)
|
||||
return newValue
|
||||
}
|
||||
|
||||
var color: UIColor {
|
||||
|
||||
let newValue = UIColor(
|
||||
hue: CGFloat(self.hue),
|
||||
saturation: CGFloat(self.saturation),
|
||||
brightness: CGFloat(self.brightness),
|
||||
alpha: 1.0
|
||||
)
|
||||
return newValue
|
||||
}
|
||||
|
||||
var colorText: String {
|
||||
|
||||
let newValue: String = "H: \(self.hue * 359)˚, S: \(round(self.saturation * 100.0))%, B: \(round(self.brightness * 100.0))%"
|
||||
return newValue
|
||||
}
|
||||
|
||||
func setRandomHue() {
|
||||
|
||||
self.hue = Self.randomHue()
|
||||
}
|
||||
|
||||
|
||||
// MARK: NSManagedObject
|
||||
|
||||
public override func awakeFromInsert() {
|
||||
|
||||
super.awakeFromInsert()
|
||||
|
||||
self.hue = Self.randomHue()
|
||||
self.saturation = Self.randomSaturation()
|
||||
self.brightness = Self.randomBrightness()
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private static func randomHue() -> Float {
|
||||
|
||||
return Float.random(in: 0.0 ... 1.0)
|
||||
}
|
||||
|
||||
private static func randomSaturation() -> Float {
|
||||
|
||||
return Float.random(in: 0.4 ... 1.0)
|
||||
}
|
||||
|
||||
private static func randomBrightness() -> Float {
|
||||
|
||||
return Float.random(in: 0.0 ... 1.0)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
//
|
||||
// Demo
|
||||
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
|
||||
|
||||
import CoreStore
|
||||
|
||||
// MARK: - Classic
|
||||
|
||||
extension Classic {
|
||||
|
||||
// MARK: - Classic.ColorsDemo
|
||||
|
||||
/**
|
||||
Sample usages for observing lists or single instances of `NSManagedObject`s
|
||||
*/
|
||||
enum ColorsDemo {
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
typealias Palette = Classic_ColorsDemo_Palette
|
||||
|
||||
static let dataStack: DataStack = {
|
||||
|
||||
let dataStack = DataStack(
|
||||
xcodeModelName: "Classic.ColorsDemo",
|
||||
bundle: Bundle(for: Palette.self)
|
||||
)
|
||||
|
||||
/**
|
||||
- Important: `addStorageAndWait(_:)` was used here to simplify initializing the demo, but in practice the asynchronous function variants are recommended.
|
||||
*/
|
||||
try! dataStack.addStorageAndWait(
|
||||
SQLiteStore(
|
||||
fileName: "Classic.ColorsDemo.sqlite",
|
||||
localStorageOptions: .recreateStoreOnModelMismatch
|
||||
)
|
||||
)
|
||||
return dataStack
|
||||
}()
|
||||
|
||||
static let palettesMonitor: ListMonitor<Classic.ColorsDemo.Palette> = Classic.ColorsDemo.dataStack.monitorSectionedList(
|
||||
From<Classic.ColorsDemo.Palette>()
|
||||
.sectionBy(\.colorGroup)
|
||||
.where(Classic.ColorsDemo.filter.whereClause())
|
||||
.orderBy(.ascending(\.hue))
|
||||
)
|
||||
|
||||
static var filter: Classic.ColorsDemo.Filter = .all {
|
||||
|
||||
didSet {
|
||||
|
||||
Classic.ColorsDemo.palettesMonitor.refetch(
|
||||
self.filter.whereClause(),
|
||||
OrderBy<Classic.ColorsDemo.Palette>(.ascending(\.hue))
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="16119" systemVersion="19F101" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||
<entity name="Palette" representedClassName="Classic_ColorsDemo_Palette" syncable="YES">
|
||||
<attribute name="brightness" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="colorGroup" optional="YES" transient="YES" attributeType="String"/>
|
||||
<attribute name="hue" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="saturation" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
</entity>
|
||||
<elements>
|
||||
<element name="Palette" positionX="-63" positionY="-18" width="128" height="103"/>
|
||||
</elements>
|
||||
</model>
|
||||
@@ -0,0 +1,291 @@
|
||||
//
|
||||
// Demo
|
||||
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
|
||||
|
||||
import CoreStore
|
||||
import UIKit
|
||||
|
||||
|
||||
// MARK: - Classic.ColorsDemo
|
||||
|
||||
extension Classic.ColorsDemo {
|
||||
|
||||
// MARK: - Classic.ColorsDemo.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.
|
||||
*/
|
||||
var palette: ObjectMonitor<Classic.ColorsDemo.Palette> {
|
||||
|
||||
didSet {
|
||||
|
||||
oldValue.removeObserver(self)
|
||||
|
||||
self.startMonitoringObject()
|
||||
}
|
||||
}
|
||||
|
||||
init(_ palette: ObjectMonitor<Classic.ColorsDemo.Palette>) {
|
||||
|
||||
self.palette = palette
|
||||
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: Classic.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: \Classic.ColorsDemo.Palette.hue)) == true {
|
||||
|
||||
self.hueSlider.value = Float(palette.hue)
|
||||
}
|
||||
if changedKeys == nil
|
||||
|| changedKeys?.contains(String(keyPath: \Classic.ColorsDemo.Palette.saturation)) == true {
|
||||
|
||||
self.saturationSlider.value = palette.saturation
|
||||
}
|
||||
if changedKeys == nil
|
||||
|| changedKeys?.contains(String(keyPath: \Classic.ColorsDemo.Palette.brightness)) == true {
|
||||
|
||||
self.brightnessSlider.value = palette.brightness
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: ObjectObserver
|
||||
|
||||
func objectMonitor(
|
||||
_ monitor: ObjectMonitor<Classic.ColorsDemo.Palette>,
|
||||
didUpdateObject object: Classic.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
|
||||
|
||||
@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
|
||||
Classic.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
|
||||
Classic.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
|
||||
Classic.ColorsDemo.dataStack.perform(
|
||||
asynchronous: { [weak self] (transaction) in
|
||||
|
||||
let palette = transaction.edit(self?.palette.object)
|
||||
palette?.brightness = value
|
||||
},
|
||||
completion: { _ in }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,199 @@
|
||||
//
|
||||
// Demo
|
||||
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
|
||||
|
||||
import CoreStore
|
||||
import UIKit
|
||||
|
||||
|
||||
// MARK: - Classic.ColorsDemo
|
||||
|
||||
extension Classic.ColorsDemo {
|
||||
|
||||
// MARK: - Classic.ColorsDemo.ListViewController
|
||||
|
||||
final class ListViewController: UITableViewController, ListSectionObserver {
|
||||
|
||||
/**
|
||||
⭐️ Sample 1: Once the views are created, we can start observing `ListMonitor` updates. 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 `tableView.reloadData()` once.
|
||||
*/
|
||||
private func startObservingList() {
|
||||
|
||||
self.listMonitor.addObserver(self)
|
||||
self.tableView.reloadData()
|
||||
}
|
||||
|
||||
/**
|
||||
⭐️ Sample 2: We can end monitoring updates anytime. `removeObserver()` was called here for illustration purposes only. `ListMonitor`s safely remove deallocated observers automatically.
|
||||
*/
|
||||
deinit {
|
||||
|
||||
self.listMonitor.removeObserver(self)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
⭐️ Sample 3: `ListSectionObserver` (and inherently, `ListObjectObserver` and `ListObserver`) conformance
|
||||
*/
|
||||
|
||||
// MARK: ListObserver
|
||||
|
||||
typealias ListEntityType = Classic.ColorsDemo.Palette
|
||||
|
||||
func listMonitorWillChange(_ monitor: ListMonitor<Classic.ColorsDemo.Palette>) {
|
||||
|
||||
self.tableView.beginUpdates()
|
||||
}
|
||||
|
||||
func listMonitorDidChange(_ monitor: ListMonitor<Classic.ColorsDemo.Palette>) {
|
||||
|
||||
self.tableView.endUpdates()
|
||||
}
|
||||
|
||||
func listMonitorDidRefetch(_ monitor: ListMonitor<Classic.ColorsDemo.Palette>) {
|
||||
|
||||
self.tableView.reloadData()
|
||||
}
|
||||
|
||||
|
||||
// MARK: ListObjectObserver
|
||||
|
||||
func listMonitor(_ monitor: ListMonitor<ListEntityType>, didInsertObject object: ListEntityType, toIndexPath indexPath: IndexPath) {
|
||||
|
||||
self.tableView.insertRows(at: [indexPath], with: .automatic)
|
||||
}
|
||||
|
||||
func listMonitor(_ monitor: ListMonitor<ListEntityType>, didDeleteObject object: ListEntityType, fromIndexPath indexPath: IndexPath) {
|
||||
|
||||
self.tableView.deleteRows(at: [indexPath], with: .automatic)
|
||||
}
|
||||
|
||||
func listMonitor(_ monitor: ListMonitor<ListEntityType>, didUpdateObject object: ListEntityType, atIndexPath indexPath: IndexPath) {
|
||||
|
||||
if case let cell as Classic.ColorsDemo.ItemCell = self.tableView.cellForRow(at: indexPath) {
|
||||
|
||||
cell.setPalette(object)
|
||||
}
|
||||
}
|
||||
|
||||
func listMonitor(_ monitor: ListMonitor<ListEntityType>, didMoveObject object: ListEntityType, fromIndexPath: IndexPath, toIndexPath: IndexPath) {
|
||||
|
||||
self.tableView.deleteRows(at: [fromIndexPath], with: .automatic)
|
||||
self.tableView.insertRows(at: [toIndexPath], with: .automatic)
|
||||
}
|
||||
|
||||
|
||||
// MARK: ListSectionObserver
|
||||
|
||||
func listMonitor(_ monitor: ListMonitor<ListEntityType>, didInsertSection sectionInfo: NSFetchedResultsSectionInfo, toSectionIndex sectionIndex: Int) {
|
||||
|
||||
self.tableView.insertSections(IndexSet(integer: sectionIndex), with: .automatic)
|
||||
}
|
||||
|
||||
func listMonitor(_ monitor: ListMonitor<ListEntityType>, didDeleteSection sectionInfo: NSFetchedResultsSectionInfo, fromSectionIndex sectionIndex: Int) {
|
||||
|
||||
self.tableView.deleteSections(IndexSet(integer: sectionIndex), with: .automatic)
|
||||
}
|
||||
|
||||
|
||||
// MARK: UITableViewDataSource
|
||||
|
||||
override func numberOfSections(in tableView: UITableView) -> Int {
|
||||
|
||||
return self.listMonitor.numberOfSections()
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
|
||||
return self.listMonitor.numberOfObjects(in: section)
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
|
||||
let cell = tableView.dequeueReusableCell(
|
||||
withIdentifier: Classic.ColorsDemo.ItemCell.reuseIdentifier,
|
||||
for: indexPath
|
||||
) as! Classic.ColorsDemo.ItemCell
|
||||
cell.setPalette(self.listMonitor[indexPath])
|
||||
return cell
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||
|
||||
return self.listMonitor.sectionInfo(at: section).name
|
||||
}
|
||||
|
||||
|
||||
// MARK: UITableViewDelegate
|
||||
|
||||
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
|
||||
|
||||
switch editingStyle {
|
||||
|
||||
case .delete:
|
||||
let object = self.listMonitor[indexPath]
|
||||
Classic.ColorsDemo.dataStack.perform(
|
||||
asynchronous: { (transaction) in
|
||||
|
||||
transaction.delete(object)
|
||||
},
|
||||
completion: { _ in }
|
||||
)
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
init(
|
||||
listMonitor: ListMonitor<Classic.ColorsDemo.Palette>,
|
||||
onPaletteTapped: @escaping (Classic.ColorsDemo.Palette) -> Void
|
||||
) {
|
||||
|
||||
self.listMonitor = listMonitor
|
||||
self.onPaletteTapped = onPaletteTapped
|
||||
|
||||
super.init(style: .plain)
|
||||
}
|
||||
|
||||
|
||||
// MARK: UIViewController
|
||||
|
||||
override func viewDidLoad() {
|
||||
|
||||
super.viewDidLoad()
|
||||
|
||||
self.tableView.register(
|
||||
Classic.ColorsDemo.ItemCell.self,
|
||||
forCellReuseIdentifier: Classic.ColorsDemo.ItemCell.reuseIdentifier
|
||||
)
|
||||
|
||||
self.startObservingList()
|
||||
}
|
||||
|
||||
|
||||
// MARK: UITableViewDelegate
|
||||
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
|
||||
self.onPaletteTapped(
|
||||
self.listMonitor[indexPath]
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let listMonitor: ListMonitor<Classic.ColorsDemo.Palette>
|
||||
private let onPaletteTapped: (Classic.ColorsDemo.Palette) -> Void
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder: NSCoder) {
|
||||
|
||||
fatalError()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,8 +28,8 @@ extension Modern.ColorsDemo {
|
||||
switch self {
|
||||
|
||||
case .all: return .init()
|
||||
case .light: return (\Modern.ColorsDemo.Palette.$brightness >= 0.9)
|
||||
case .dark: return (\Modern.ColorsDemo.Palette.$brightness <= 0.4)
|
||||
case .light: return (\.$brightness >= 0.9)
|
||||
case .dark: return (\.$brightness <= 0.4)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -49,31 +49,31 @@ extension Modern.ColorsDemo {
|
||||
var brightness: Float
|
||||
|
||||
@Field.Virtual(
|
||||
"colorName",
|
||||
"colorGroup",
|
||||
customGetter: { object, field in
|
||||
|
||||
if let colorName = field.primitiveValue {
|
||||
if let colorGroup = field.primitiveValue {
|
||||
|
||||
return colorName
|
||||
return colorGroup
|
||||
}
|
||||
let colorName: String
|
||||
let colorGroup: 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"
|
||||
case 0 ..< 20: colorGroup = "Lower Reds"
|
||||
case 20 ..< 57: colorGroup = "Oranges and Browns"
|
||||
case 57 ..< 90: colorGroup = "Yellow-Greens"
|
||||
case 90 ..< 159: colorGroup = "Greens"
|
||||
case 159 ..< 197: colorGroup = "Blue-Greens"
|
||||
case 197 ..< 241: colorGroup = "Blues"
|
||||
case 241 ..< 297: colorGroup = "Violets"
|
||||
case 297 ..< 331: colorGroup = "Magentas"
|
||||
default: colorGroup = "Upper Reds"
|
||||
}
|
||||
field.primitiveValue = colorName
|
||||
return colorName
|
||||
field.primitiveValue = colorGroup
|
||||
return colorGroup
|
||||
}
|
||||
)
|
||||
var colorName: String
|
||||
var colorGroup: String
|
||||
|
||||
@Field.Virtual(
|
||||
"color",
|
||||
@@ -120,7 +120,7 @@ extension Modern.ColorsDemo {
|
||||
|
||||
private static func resetVirtualProperties(_ object: ObjectProxy<Modern.ColorsDemo.Palette>) {
|
||||
|
||||
object.$colorName.primitiveValue = nil
|
||||
object.$colorGroup.primitiveValue = nil
|
||||
object.$color.primitiveValue = nil
|
||||
object.$colorText.primitiveValue = nil
|
||||
}
|
||||
@@ -45,7 +45,7 @@ extension Modern {
|
||||
|
||||
static let palettesPublisher: ListPublisher<Modern.ColorsDemo.Palette> = Modern.ColorsDemo.dataStack.publishList(
|
||||
From<Modern.ColorsDemo.Palette>()
|
||||
.sectionBy(\.$colorName)
|
||||
.sectionBy(\.$colorGroup)
|
||||
.where(Modern.ColorsDemo.filter.whereClause())
|
||||
.orderBy(.ascending(\.$hue))
|
||||
)
|
||||
@@ -56,7 +56,7 @@ extension Modern {
|
||||
|
||||
try! Modern.ColorsDemo.palettesPublisher.refetch(
|
||||
From<Modern.ColorsDemo.Palette>()
|
||||
.sectionBy(\.$colorName)
|
||||
.sectionBy(\.$colorGroup)
|
||||
.where(self.filter.whereClause())
|
||||
.orderBy(.ascending(\.$hue))
|
||||
)
|
||||
@@ -17,6 +17,9 @@ extension Modern {
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
/**
|
||||
⭐️ Sample 1: Setting up the `DataStack` and storage
|
||||
*/
|
||||
static let dataStack: DataStack = {
|
||||
|
||||
let dataStack = DataStack(
|
||||
@@ -9,7 +9,7 @@ import UIKit
|
||||
|
||||
extension Modern.PokedexDemo {
|
||||
|
||||
// MARK: - Modern.PokedexDemo.Move
|
||||
// MARK: - Modern.PokedexDemo.PokemonType
|
||||
|
||||
/**
|
||||
⭐️ Sample 1: Types that will be used with `@Field.Stored` need to implement both `ImportableAttributeType` and `FieldStorableType`. In this case, `RawRepresentable` types with primitive `RawValue`s have built-in implementations so we only have to declare conformance to `ImportableAttributeType` and `FieldStorableType`.
|
||||
@@ -1914,14 +1914,14 @@ class Vehicle: CoreStoreObject {
|
||||
Built-in encoders such as `FieldCoders.NSCoding`, `FieldCoders.Json`, and `FieldCoders.Plist` are available, and custom encoding/decoding is also supported:
|
||||
```swift
|
||||
class Person: CoreStoreObject {
|
||||
<br />
|
||||
|
||||
struct CustomInfo: Codable {
|
||||
// ...
|
||||
}
|
||||
<br />
|
||||
|
||||
@Field.Coded("otherInfo", coder: FieldCoders.Json.self)
|
||||
var otherInfo: CustomInfo?
|
||||
<br />
|
||||
|
||||
@Field.Coded(
|
||||
"photo",
|
||||
coder: {
|
||||
|
||||
@@ -65,7 +65,8 @@ extension FieldContainer {
|
||||
var eyeColor: UIColor = .black
|
||||
}
|
||||
```
|
||||
- parameter initial: the initial value for the property when the object is first created.
|
||||
- Important: Any changes in the `coder` are not reflected in the VersionLock, so make sure that the encoder and decoder logic is compatible for all versions of your persistent store.
|
||||
- parameter initial: the initial value for the property that is shared for all instances of this object. Note that this is evaluated during `DataStack` setup, not during object creation. To assign a value during object creation, use the `dynamicInitialValue` argument instead.
|
||||
- parameter keyPath: the permanent attribute name for this property.
|
||||
- parameter versionHashModifier: used to mark or denote a property as being a different "version" than another even if all of the values which affect persistence are equal. (Such a difference is important in cases where the properties are unchanged but the format or content of its data are changed.)
|
||||
- parameter previousVersionKeyPath: used to resolve naming conflicts between models. When creating an entity mapping between entities in two managed object models, a source entity property's `keyPath` with a matching destination entity property's `previousVersionKeyPath` indicate that a property mapping should be configured to migrate from the source to the destination. If unset, the identifier will be the property's `keyPath`.
|
||||
@@ -99,6 +100,25 @@ extension FieldContainer {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
Initializes the metadata for the property.
|
||||
```
|
||||
class Person: CoreStoreObject {
|
||||
|
||||
@Field.Coded("eyeColor", coder: FieldCoders.NSCoding.self, dynamicInitialValue: { UIColor.random() })
|
||||
var eyeColor: UIColor
|
||||
}
|
||||
```
|
||||
- Important: Any changes in the `coder` are not reflected in the VersionLock, so make sure that the encoder and decoder logic is compatible for all versions of your persistent store.
|
||||
- parameter keyPath: the permanent attribute name for this property.
|
||||
- parameter versionHashModifier: used to mark or denote a property as being a different "version" than another even if all of the values which affect persistence are equal. (Such a difference is important in cases where the properties are unchanged but the format or content of its data are changed.)
|
||||
- parameter previousVersionKeyPath: used to resolve naming conflicts between models. When creating an entity mapping between entities in two managed object models, a source entity property's `keyPath` with a matching destination entity property's `previousVersionKeyPath` indicate that a property mapping should be configured to migrate from the source to the destination. If unset, the identifier will be the property's `keyPath`.
|
||||
- parameter coder: The `FieldCoderType` to be used for encoding and decoding the value
|
||||
- parameter customGetter: use this closure as an "override" for the default property getter. The closure receives a `ObjectProxy<O>`, which acts as a type-safe proxy for the receiver. When accessing the property value from `ObjectProxy<O>`, make sure to use `field.primitiveValue` instead of `field.value`, which would unintentionally execute the same closure again recursively. Do not make assumptions on the thread/queue that the closure is executed on; accessors may be called from `NSError` logs for example.
|
||||
- parameter customSetter: use this closure as an "override" for the default property setter. The closure receives a `ObjectProxy<O>`, which acts as a fast, type-safe KVC interface for `CoreStoreObject`. The reason a `CoreStoreObject` instance is not passed directly is because the Core Data runtime is not aware of `CoreStoreObject` properties' static typing, and so loading those info everytime KVO invokes this accessor method incurs a cumulative performance hit (especially in KVO-heavy operations such as `ListMonitor` observing.) When accessing the property value from `ObjectProxy<O>`, make sure to use `field.primitiveValue` instead of `field.value`, which would unintentionally execute the same closure again recursively.
|
||||
- parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`.
|
||||
- parameter dynamicInitialValue: the initial value for the property when the object is first created.
|
||||
*/
|
||||
public init<Coder: FieldCoderType>(
|
||||
_ keyPath: KeyPathString,
|
||||
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
||||
@@ -139,7 +159,8 @@ extension FieldContainer {
|
||||
var bloodType: BloodType = .unknown
|
||||
}
|
||||
```
|
||||
- parameter initial: the initial value for the property when the object is first created.
|
||||
- Important: Any changes in the encoder/decoder are not reflected in the VersionLock, so make sure that the encoder and decoder logic is compatible for all versions of your persistent store.
|
||||
- parameter initial: the initial value for the property that is shared for all instances of this object. Note that this is evaluated during `DataStack` setup, not during object creation. To assign a value during object creation, use the `dynamicInitialValue` argument instead.
|
||||
- parameter keyPath: the permanent attribute name for this property.
|
||||
- parameter versionHashModifier: used to mark or denote a property as being a different "version" than another even if all of the values which affect persistence are equal. (Such a difference is important in cases where the properties are unchanged but the format or content of its data are changed.)
|
||||
- parameter previousVersionKeyPath: used to resolve naming conflicts between models. When creating an entity mapping between entities in two managed object models, a source entity property's `keyPath` with a matching destination entity property's `previousVersionKeyPath` indicate that a property mapping should be configured to migrate from the source to the destination. If unset, the identifier will be the property's `keyPath`.
|
||||
@@ -172,7 +193,33 @@ extension FieldContainer {
|
||||
affectedByKeyPaths: affectedByKeyPaths
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Initializes the metadata for the property.
|
||||
```
|
||||
class Person: CoreStoreObject {
|
||||
|
||||
@Field.Coded(
|
||||
"bloodType",
|
||||
coder: {
|
||||
encode: { $0.toData() },
|
||||
decode: { BloodType(fromData: $0) }
|
||||
},
|
||||
dynamicInitialValue: { BloodType.random() }
|
||||
)
|
||||
var bloodType: BloodType
|
||||
}
|
||||
```
|
||||
- Important: Any changes in the encoder/decoder are not reflected in the VersionLock, so make sure that the encoder and decoder logic is compatible for all versions of your persistent store.
|
||||
- parameter keyPath: the permanent attribute name for this property.
|
||||
- parameter versionHashModifier: used to mark or denote a property as being a different "version" than another even if all of the values which affect persistence are equal. (Such a difference is important in cases where the properties are unchanged but the format or content of its data are changed.)
|
||||
- parameter previousVersionKeyPath: used to resolve naming conflicts between models. When creating an entity mapping between entities in two managed object models, a source entity property's `keyPath` with a matching destination entity property's `previousVersionKeyPath` indicate that a property mapping should be configured to migrate from the source to the destination. If unset, the identifier will be the property's `keyPath`.
|
||||
- parameter coder: The closures to be used for encoding and decoding the value
|
||||
- parameter customGetter: use this closure as an "override" for the default property getter. The closure receives a `ObjectProxy<O>`, which acts as a type-safe proxy for the receiver. When accessing the property value from `ObjectProxy<O>`, make sure to use `field.primitiveValue` instead of `field.value`, which would unintentionally execute the same closure again recursively. Do not make assumptions on the thread/queue that the closure is executed on; accessors may be called from `NSError` logs for example.
|
||||
- parameter customSetter: use this closure as an "override" for the default property setter. The closure receives a `ObjectProxy<O>`, which acts as a fast, type-safe KVC interface for `CoreStoreObject`. The reason a `CoreStoreObject` instance is not passed directly is because the Core Data runtime is not aware of `CoreStoreObject` properties' static typing, and so loading those info everytime KVO invokes this accessor method incurs a cumulative performance hit (especially in KVO-heavy operations such as `ListMonitor` observing.) When accessing the property value from `ObjectProxy<O>`, make sure to use `field.primitiveValue` instead of `field.value`, which would unintentionally execute the same closure again recursively.
|
||||
- parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`.
|
||||
- parameter dynamicInitialValue: the initial value for the property when the object is first created.
|
||||
*/
|
||||
public init(
|
||||
_ keyPath: KeyPathString,
|
||||
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
||||
@@ -467,7 +514,8 @@ extension FieldContainer.Coded where V: FieldOptionalType {
|
||||
var eyeColor: UIColor? = nil
|
||||
}
|
||||
```
|
||||
- parameter initial: the initial value for the property when the object is first created.
|
||||
- Important: Any changes in the `coder` are not reflected in the VersionLock, so make sure that the encoder and decoder logic is compatible for all versions of your persistent store.
|
||||
- parameter initial: the initial value for the property that is shared for all instances of this object. Note that this is evaluated during `DataStack` setup, not during object creation. To assign a value during object creation, use the `dynamicInitialValue` argument instead.
|
||||
- parameter keyPath: the permanent attribute name for this property.
|
||||
- parameter versionHashModifier: used to mark or denote a property as being a different "version" than another even if all of the values which affect persistence are equal. (Such a difference is important in cases where the properties are unchanged but the format or content of its data are changed.)
|
||||
- parameter previousVersionKeyPath: used to resolve naming conflicts between models. When creating an entity mapping between entities in two managed object models, a source entity property's `keyPath` with a matching destination entity property's `previousVersionKeyPath` indicate that a property mapping should be configured to migrate from the source to the destination. If unset, the identifier will be the property's `keyPath`.
|
||||
@@ -501,6 +549,25 @@ extension FieldContainer.Coded where V: FieldOptionalType {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
Initializes the metadata for the property.
|
||||
```
|
||||
class Person: CoreStoreObject {
|
||||
|
||||
@Field.Coded("eyeColor", coder: FieldCoders.NSCoding.self, dynamicInitialValue: { UIColor.random() })
|
||||
var eyeColor: UIColor?
|
||||
}
|
||||
```
|
||||
- Important: Any changes in the `coder` are not reflected in the VersionLock, so make sure that the encoder and decoder logic is compatible for all versions of your persistent store.
|
||||
- parameter keyPath: the permanent attribute name for this property.
|
||||
- parameter versionHashModifier: used to mark or denote a property as being a different "version" than another even if all of the values which affect persistence are equal. (Such a difference is important in cases where the properties are unchanged but the format or content of its data are changed.)
|
||||
- parameter previousVersionKeyPath: used to resolve naming conflicts between models. When creating an entity mapping between entities in two managed object models, a source entity property's `keyPath` with a matching destination entity property's `previousVersionKeyPath` indicate that a property mapping should be configured to migrate from the source to the destination. If unset, the identifier will be the property's `keyPath`.
|
||||
- parameter coder: The `FieldCoderType` to be used for encoding and decoding the value
|
||||
- parameter customGetter: use this closure as an "override" for the default property getter. The closure receives a `ObjectProxy<O>`, which acts as a type-safe proxy for the receiver. When accessing the property value from `ObjectProxy<O>`, make sure to use `field.primitiveValue` instead of `field.value`, which would unintentionally execute the same closure again recursively. Do not make assumptions on the thread/queue that the closure is executed on; accessors may be called from `NSError` logs for example.
|
||||
- parameter customSetter: use this closure as an "override" for the default property setter. The closure receives a `ObjectProxy<O>`, which acts as a fast, type-safe KVC interface for `CoreStoreObject`. The reason a `CoreStoreObject` instance is not passed directly is because the Core Data runtime is not aware of `CoreStoreObject` properties' static typing, and so loading those info everytime KVO invokes this accessor method incurs a cumulative performance hit (especially in KVO-heavy operations such as `ListMonitor` observing.) When accessing the property value from `ObjectProxy<O>`, make sure to use `field.primitiveValue` instead of `field.value`, which would unintentionally execute the same closure again recursively.
|
||||
- parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`.
|
||||
- parameter dynamicInitialValue: the initial value for the property when the object is first created.
|
||||
*/
|
||||
public init<Coder: FieldCoderType>(
|
||||
_ keyPath: KeyPathString,
|
||||
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
||||
@@ -541,7 +608,8 @@ extension FieldContainer.Coded where V: FieldOptionalType {
|
||||
var bloodType: BloodType?
|
||||
}
|
||||
```
|
||||
- parameter initial: the initial value for the property when the object is first created.
|
||||
- Important: Any changes in the encoder/decoder are not reflected in the VersionLock, so make sure that the encoder and decoder logic is compatible for all versions of your persistent store.
|
||||
- parameter initial: the initial value for the property that is shared for all instances of this object. Note that this is evaluated during `DataStack` setup, not during object creation. To assign a value during object creation, use the `dynamicInitialValue` argument instead.
|
||||
- parameter keyPath: the permanent attribute name for this property.
|
||||
- parameter versionHashModifier: used to mark or denote a property as being a different "version" than another even if all of the values which affect persistence are equal. (Such a difference is important in cases where the properties are unchanged but the format or content of its data are changed.)
|
||||
- parameter previousVersionKeyPath: used to resolve naming conflicts between models. When creating an entity mapping between entities in two managed object models, a source entity property's `keyPath` with a matching destination entity property's `previousVersionKeyPath` indicate that a property mapping should be configured to migrate from the source to the destination. If unset, the identifier will be the property's `keyPath`.
|
||||
@@ -574,7 +642,33 @@ extension FieldContainer.Coded where V: FieldOptionalType {
|
||||
affectedByKeyPaths: affectedByKeyPaths
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Initializes the metadata for the property.
|
||||
```
|
||||
class Person: CoreStoreObject {
|
||||
|
||||
@Field.Coded(
|
||||
"bloodType",
|
||||
coder: {
|
||||
encode: { $0.toData() },
|
||||
decode: { BloodType(fromData: $0) }
|
||||
},
|
||||
dynamicInitialValue: { BloodType.random() }
|
||||
)
|
||||
var bloodType: BloodType?
|
||||
}
|
||||
```
|
||||
- Important: Any changes in the encoder/decoder are not reflected in the VersionLock, so make sure that the encoder and decoder logic is compatible for all versions of your persistent store.
|
||||
- parameter keyPath: the permanent attribute name for this property.
|
||||
- parameter versionHashModifier: used to mark or denote a property as being a different "version" than another even if all of the values which affect persistence are equal. (Such a difference is important in cases where the properties are unchanged but the format or content of its data are changed.)
|
||||
- parameter previousVersionKeyPath: used to resolve naming conflicts between models. When creating an entity mapping between entities in two managed object models, a source entity property's `keyPath` with a matching destination entity property's `previousVersionKeyPath` indicate that a property mapping should be configured to migrate from the source to the destination. If unset, the identifier will be the property's `keyPath`.
|
||||
- parameter coder: The closures to be used for encoding and decoding the value
|
||||
- parameter customGetter: use this closure as an "override" for the default property getter. The closure receives a `ObjectProxy<O>`, which acts as a type-safe proxy for the receiver. When accessing the property value from `ObjectProxy<O>`, make sure to use `field.primitiveValue` instead of `field.value`, which would unintentionally execute the same closure again recursively. Do not make assumptions on the thread/queue that the closure is executed on; accessors may be called from `NSError` logs for example.
|
||||
- parameter customSetter: use this closure as an "override" for the default property setter. The closure receives a `ObjectProxy<O>`, which acts as a fast, type-safe KVC interface for `CoreStoreObject`. The reason a `CoreStoreObject` instance is not passed directly is because the Core Data runtime is not aware of `CoreStoreObject` properties' static typing, and so loading those info everytime KVO invokes this accessor method incurs a cumulative performance hit (especially in KVO-heavy operations such as `ListMonitor` observing.) When accessing the property value from `ObjectProxy<O>`, make sure to use `field.primitiveValue` instead of `field.value`, which would unintentionally execute the same closure again recursively.
|
||||
- parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`.
|
||||
- parameter dynamicInitialValue: the initial value for the property when the object is first created.
|
||||
*/
|
||||
public init(
|
||||
_ keyPath: KeyPathString,
|
||||
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
||||
@@ -615,7 +709,7 @@ extension FieldContainer.Coded where V: DefaultNSSecureCodable {
|
||||
var customInfo: NSDictionary = [:]
|
||||
}
|
||||
```
|
||||
- parameter initial: the initial value for the property when the object is first created.
|
||||
- parameter initial: the initial value for the property that is shared for all instances of this object. Note that this is evaluated during `DataStack` setup, not during object creation. To assign a value during object creation, use the `dynamicInitialValue` argument instead.
|
||||
- parameter keyPath: the permanent attribute name for this property.
|
||||
- parameter versionHashModifier: used to mark or denote a property as being a different "version" than another even if all of the values which affect persistence are equal. (Such a difference is important in cases where the properties are unchanged but the format or content of its data are changed.)
|
||||
- parameter previousVersionKeyPath: used to resolve naming conflicts between models. When creating an entity mapping between entities in two managed object models, a source entity property's `keyPath` with a matching destination entity property's `previousVersionKeyPath` indicate that a property mapping should be configured to migrate from the source to the destination. If unset, the identifier will be the property's `keyPath`.
|
||||
@@ -647,6 +741,23 @@ extension FieldContainer.Coded where V: DefaultNSSecureCodable {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
Initializes the metadata for the property. This overload is for types supported by Core Data's default NSSecureCodable implementation: `NSArray`, `NSDictionary`, `NSSet`, `NSString`, `NSNumber`, `NSDate`, `NSData`, `NSURL`, `NSUUID`, and `NSNull`.
|
||||
```
|
||||
class Person: CoreStoreObject {
|
||||
|
||||
@Field.Coded("customInfo", dynamicInitialValue: { ["id": UUID()] })
|
||||
var customInfo: NSDictionary
|
||||
}
|
||||
```
|
||||
- parameter keyPath: the permanent attribute name for this property.
|
||||
- parameter versionHashModifier: used to mark or denote a property as being a different "version" than another even if all of the values which affect persistence are equal. (Such a difference is important in cases where the properties are unchanged but the format or content of its data are changed.)
|
||||
- parameter previousVersionKeyPath: used to resolve naming conflicts between models. When creating an entity mapping between entities in two managed object models, a source entity property's `keyPath` with a matching destination entity property's `previousVersionKeyPath` indicate that a property mapping should be configured to migrate from the source to the destination. If unset, the identifier will be the property's `keyPath`.
|
||||
- parameter customGetter: use this closure as an "override" for the default property getter. The closure receives a `ObjectProxy<O>`, which acts as a type-safe proxy for the receiver. When accessing the property value from `ObjectProxy<O>`, make sure to use `field.primitiveValue` instead of `field.value`, which would unintentionally execute the same closure again recursively. Do not make assumptions on the thread/queue that the closure is executed on; accessors may be called from `NSError` logs for example.
|
||||
- parameter customSetter: use this closure as an "override" for the default property setter. The closure receives a `ObjectProxy<O>`, which acts as a fast, type-safe KVC interface for `CoreStoreObject`. The reason a `CoreStoreObject` instance is not passed directly is because the Core Data runtime is not aware of `CoreStoreObject` properties' static typing, and so loading those info everytime KVO invokes this accessor method incurs a cumulative performance hit (especially in KVO-heavy operations such as `ListMonitor` observing.) When accessing the property value from `ObjectProxy<O>`, make sure to use `field.primitiveValue` instead of `field.value`, which would unintentionally execute the same closure again recursively.
|
||||
- parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`.
|
||||
- parameter dynamicInitialValue: the initial value for the property when the object is first created.
|
||||
*/
|
||||
public init(
|
||||
_ keyPath: KeyPathString,
|
||||
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
||||
@@ -686,7 +797,7 @@ extension FieldContainer.Coded where V: FieldOptionalType, V.Wrapped: DefaultNSS
|
||||
var customInfo: NSDictionary? = nil
|
||||
}
|
||||
```
|
||||
- parameter initial: the initial value for the property when the object is first created.
|
||||
- parameter initial: the initial value for the property that is shared for all instances of this object. Note that this is evaluated during `DataStack` setup, not during object creation. To assign a value during object creation, use the `dynamicInitialValue` argument instead.
|
||||
- parameter keyPath: the permanent attribute name for this property.
|
||||
- parameter versionHashModifier: used to mark or denote a property as being a different "version" than another even if all of the values which affect persistence are equal. (Such a difference is important in cases where the properties are unchanged but the format or content of its data are changed.)
|
||||
- parameter previousVersionKeyPath: used to resolve naming conflicts between models. When creating an entity mapping between entities in two managed object models, a source entity property's `keyPath` with a matching destination entity property's `previousVersionKeyPath` indicate that a property mapping should be configured to migrate from the source to the destination. If unset, the identifier will be the property's `keyPath`.
|
||||
@@ -718,6 +829,23 @@ extension FieldContainer.Coded where V: FieldOptionalType, V.Wrapped: DefaultNSS
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
Initializes the metadata for the property. This overload is for types supported by Core Data's default NSSecureCodable implementation: `NSArray`, `NSDictionary`, `NSSet`, `NSString`, `NSNumber`, `NSDate`, `NSData`, `NSURL`, `NSUUID`, and `NSNull`.
|
||||
```
|
||||
class Person: CoreStoreObject {
|
||||
|
||||
@Field.Coded("customInfo", dynamicInitialValue: { ["id": UUID()] })
|
||||
var customInfo: NSDictionary?
|
||||
}
|
||||
```
|
||||
- parameter keyPath: the permanent attribute name for this property.
|
||||
- parameter versionHashModifier: used to mark or denote a property as being a different "version" than another even if all of the values which affect persistence are equal. (Such a difference is important in cases where the properties are unchanged but the format or content of its data are changed.)
|
||||
- parameter previousVersionKeyPath: used to resolve naming conflicts between models. When creating an entity mapping between entities in two managed object models, a source entity property's `keyPath` with a matching destination entity property's `previousVersionKeyPath` indicate that a property mapping should be configured to migrate from the source to the destination. If unset, the identifier will be the property's `keyPath`.
|
||||
- parameter customGetter: use this closure as an "override" for the default property getter. The closure receives a `ObjectProxy<O>`, which acts as a type-safe proxy for the receiver. When accessing the property value from `ObjectProxy<O>`, make sure to use `field.primitiveValue` instead of `field.value`, which would unintentionally execute the same closure again recursively. Do not make assumptions on the thread/queue that the closure is executed on; accessors may be called from `NSError` logs for example.
|
||||
- parameter customSetter: use this closure as an "override" for the default property setter. The closure receives a `ObjectProxy<O>`, which acts as a fast, type-safe KVC interface for `CoreStoreObject`. The reason a `CoreStoreObject` instance is not passed directly is because the Core Data runtime is not aware of `CoreStoreObject` properties' static typing, and so loading those info everytime KVO invokes this accessor method incurs a cumulative performance hit (especially in KVO-heavy operations such as `ListMonitor` observing.) When accessing the property value from `ObjectProxy<O>`, make sure to use `field.primitiveValue` instead of `field.value`, which would unintentionally execute the same closure again recursively.
|
||||
- parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`.
|
||||
- parameter dynamicInitialValue: the initial value for the property when the object is first created.
|
||||
*/
|
||||
public init(
|
||||
_ keyPath: KeyPathString,
|
||||
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
||||
|
||||
@@ -59,7 +59,7 @@ extension FieldContainer {
|
||||
var title: String = "Mr."
|
||||
}
|
||||
```
|
||||
- parameter initial: the initial value for the property when the object is first created.
|
||||
- parameter initial: the initial value for the property that is shared for all instances of this object. Note that this is evaluated during `DataStack` setup, not during object creation. To assign a value during object creation, use the `dynamicInitialValue` argument instead.
|
||||
- parameter keyPath: the permanent attribute name for this property.
|
||||
- parameter versionHashModifier: used to mark or denote a property as being a different "version" than another even if all of the values which affect persistence are equal. (Such a difference is important in cases where the properties are unchanged but the format or content of its data are changed.)
|
||||
- parameter previousVersionKeyPath: used to resolve naming conflicts between models. When creating an entity mapping between entities in two managed object models, a source entity property's `keyPath` with a matching destination entity property's `previousVersionKeyPath` indicate that a property mapping should be configured to migrate from the source to the destination. If unset, the identifier will be the property's `keyPath`.
|
||||
@@ -90,6 +90,23 @@ extension FieldContainer {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
Initializes the metadata for the property.
|
||||
```
|
||||
class Person: CoreStoreObject {
|
||||
|
||||
@Field.Stored("title", dynamicInitialValue: { Person.randomTitle() })
|
||||
var title: String
|
||||
}
|
||||
```
|
||||
- parameter keyPath: the permanent attribute name for this property.
|
||||
- parameter versionHashModifier: used to mark or denote a property as being a different "version" than another even if all of the values which affect persistence are equal. (Such a difference is important in cases where the properties are unchanged but the format or content of its data are changed.)
|
||||
- parameter previousVersionKeyPath: used to resolve naming conflicts between models. When creating an entity mapping between entities in two managed object models, a source entity property's `keyPath` with a matching destination entity property's `previousVersionKeyPath` indicate that a property mapping should be configured to migrate from the source to the destination. If unset, the identifier will be the property's `keyPath`.
|
||||
- parameter customGetter: use this closure as an "override" for the default property getter. The closure receives a `ObjectProxy<O>`, which acts as a type-safe proxy for the receiver. When accessing the property value from `ObjectProxy<O>`, make sure to use `field.primitiveValue` instead of `field.value`, which would unintentionally execute the same closure again recursively. Do not make assumptions on the thread/queue that the closure is executed on; accessors may be called from `NSError` logs for example.
|
||||
- parameter customSetter: use this closure as an "override" for the default property setter. The closure receives a `ObjectProxy<O>`, which acts as a fast, type-safe KVC interface for `CoreStoreObject`. The reason a `CoreStoreObject` instance is not passed directly is because the Core Data runtime is not aware of `CoreStoreObject` properties' static typing, and so loading those info everytime KVO invokes this accessor method incurs a cumulative performance hit (especially in KVO-heavy operations such as `ListMonitor` observing.) When accessing the property value from `ObjectProxy<O>`, make sure to use `field.primitiveValue` instead of `field.value`, which would unintentionally execute the same closure again recursively.
|
||||
- parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`.
|
||||
- parameter dynamicInitialValue: the initial value for the property when the object is first created.
|
||||
*/
|
||||
public init(
|
||||
_ keyPath: KeyPathString,
|
||||
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
||||
@@ -360,7 +377,7 @@ extension FieldContainer.Stored where V: FieldOptionalType {
|
||||
var nickname: String?
|
||||
}
|
||||
```
|
||||
- parameter initial: the initial value for the property when the object is first created.
|
||||
- parameter initial: the initial value for the property that is shared for all instances of this object. Note that this is evaluated during `DataStack` setup, not during object creation. To assign a value during object creation, use the `dynamicInitialValue` argument instead.
|
||||
- parameter keyPath: the permanent attribute name for this property.
|
||||
- parameter versionHashModifier: used to mark or denote a property as being a different "version" than another even if all of the values which affect persistence are equal. (Such a difference is important in cases where the properties are unchanged but the format or content of its data are changed.)
|
||||
- parameter previousVersionKeyPath: used to resolve naming conflicts between models. When creating an entity mapping between entities in two managed object models, a source entity property's `keyPath` with a matching destination entity property's `previousVersionKeyPath` indicate that a property mapping should be configured to migrate from the source to the destination. If unset, the identifier will be the property's `keyPath`.
|
||||
@@ -391,6 +408,23 @@ extension FieldContainer.Stored where V: FieldOptionalType {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
Initializes the metadata for the property.
|
||||
```
|
||||
class Person: CoreStoreObject {
|
||||
|
||||
@Field.Stored("nickname", dynamicInitialValue: { Person.randomNickname() })
|
||||
var nickname: String?
|
||||
}
|
||||
```
|
||||
- parameter keyPath: the permanent attribute name for this property.
|
||||
- parameter versionHashModifier: used to mark or denote a property as being a different "version" than another even if all of the values which affect persistence are equal. (Such a difference is important in cases where the properties are unchanged but the format or content of its data are changed.)
|
||||
- parameter previousVersionKeyPath: used to resolve naming conflicts between models. When creating an entity mapping between entities in two managed object models, a source entity property's `keyPath` with a matching destination entity property's `previousVersionKeyPath` indicate that a property mapping should be configured to migrate from the source to the destination. If unset, the identifier will be the property's `keyPath`.
|
||||
- parameter customGetter: use this closure as an "override" for the default property getter. The closure receives a `ObjectProxy<O>`, which acts as a type-safe proxy for the receiver. When accessing the property value from `ObjectProxy<O>`, make sure to use `field.primitiveValue` instead of `field.value`, which would unintentionally execute the same closure again recursively. Do not make assumptions on the thread/queue that the closure is executed on; accessors may be called from `NSError` logs for example.
|
||||
- parameter customSetter: use this closure as an "override" for the default property setter. The closure receives a `ObjectProxy<O>`, which acts as a fast, type-safe KVC interface for `CoreStoreObject`. The reason a `CoreStoreObject` instance is not passed directly is because the Core Data runtime is not aware of `CoreStoreObject` properties' static typing, and so loading those info everytime KVO invokes this accessor method incurs a cumulative performance hit (especially in KVO-heavy operations such as `ListMonitor` observing.) When accessing the property value from `ObjectProxy<O>`, make sure to use `field.primitiveValue` instead of `field.value`, which would unintentionally execute the same closure again recursively.
|
||||
- parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`.
|
||||
- parameter dynamicInitialValue: the initial value for the property when the object is first created.
|
||||
*/
|
||||
public init(
|
||||
_ keyPath: KeyPathString,
|
||||
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
||||
|
||||
@@ -74,7 +74,7 @@ extension TransformableContainer {
|
||||
}
|
||||
```
|
||||
- parameter keyPath: the permanent attribute name for this property.
|
||||
- parameter initial: the initial value for the property when the object is first created. Defaults to the `ImportableAttributeType`'s empty value if not specified.
|
||||
- parameter initial: the initial value for the property that is shared for all instances of this object. Note that this is evaluated during `DataStack` setup, not during object creation. Defaults to the `ImportableAttributeType`'s empty value if not specified.
|
||||
- parameter isTransient: `true` if the property is transient, otherwise `false`. Defaults to `false` if not specified. The transient flag specifies whether or not a property's value is ignored when an object is saved to a persistent store. Transient properties are not saved to the persistent store, but are still managed for undo, redo, validation, and so on.
|
||||
- parameter allowsExternalBinaryDataStorage: `true` if the attribute allows external binary storage, otherwise `false`.
|
||||
- parameter versionHashModifier: used to mark or denote a property as being a different "version" than another even if all of the values which affect persistence are equal. (Such a difference is important in cases where the properties are unchanged but the format or content of its data are changed.)
|
||||
|
||||
@@ -77,7 +77,7 @@ extension TransformableContainer {
|
||||
}
|
||||
```
|
||||
- parameter keyPath: the permanent attribute name for this property.
|
||||
- parameter initial: the initial value for the property when the object is first created. Defaults to the `ImportableAttributeType`'s empty value if not specified.
|
||||
- parameter initial: the initial value for the property that is shared for all instances of this object. Note that this is evaluated during `DataStack` setup, not during object creation. Defaults to the `ImportableAttributeType`'s empty value if not specified.
|
||||
- parameter isTransient: `true` if the property is transient, otherwise `false`. Defaults to `false` if not specified. The transient flag specifies whether or not a property's value is ignored when an object is saved to a persistent store. Transient properties are not saved to the persistent store, but are still managed for undo, redo, validation, and so on.
|
||||
- parameter allowsExternalBinaryDataStorage: `true` if the attribute allows external binary storage, otherwise `false`.
|
||||
- parameter versionHashModifier: used to mark or denote a property as being a different "version" than another even if all of the values which affect persistence are equal. (Such a difference is important in cases where the properties are unchanged but the format or content of its data are changed.)
|
||||
|
||||
@@ -71,7 +71,7 @@ extension ValueContainer {
|
||||
}
|
||||
```
|
||||
- parameter keyPath: the permanent attribute name for this property.
|
||||
- parameter initial: the initial value for the property when the object is first created. Defaults to `nil` if not specified.
|
||||
- parameter initial: the initial value for the property that is shared for all instances of this object. Note that this is evaluated during `DataStack` setup, not during object creation. Defaults to `nil` if not specified.
|
||||
- parameter isTransient: `true` if the property is transient, otherwise `false`. Defaults to `false` if not specified. The transient flag specifies whether or not a property's value is ignored when an object is saved to a persistent store. Transient properties are not saved to the persistent store, but are still managed for undo, redo, validation, and so on.
|
||||
- parameter versionHashModifier: used to mark or denote a property as being a different "version" than another even if all of the values which affect persistence are equal. (Such a difference is important in cases where the properties are unchanged but the format or content of its data are changed.)
|
||||
- parameter renamingIdentifier: used to resolve naming conflicts between models. When creating an entity mapping between entities in two managed object models, a source entity property and a destination entity property that share the same identifier indicate that a property mapping should be configured to migrate from the source to the destination. If unset, the identifier will be the property's name.
|
||||
|
||||
Reference in New Issue
Block a user