Migrations demo done

This commit is contained in:
John Estropia
2020-09-17 23:21:41 +09:00
parent 11f5cb9a05
commit 2f93ee7591
29 changed files with 1399 additions and 61 deletions

View File

@@ -11,10 +11,20 @@
B531EFE924EB5A53005F247D /* ⭐Modern.PokedexDemo.PokedexEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = B531EFE824EB5A52005F247D /* ⭐Modern.PokedexDemo.PokedexEntry.swift */; };
B531EFEB24EB5ECD005F247D /* ⭐Modern.PokedexDemo.Service.swift in Sources */ = {isa = PBXBuildFile; fileRef = B531EFEA24EB5ECD005F247D /* ⭐Modern.PokedexDemo.Service.swift */; };
B531EFED24EB7453005F247D /* Modern.PokedexDemo.MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B531EFEC24EB7453005F247D /* Modern.PokedexDemo.MainView.swift */; };
B54D2F7A25119540004BEC7D /* Advanced.EvolutionDemo.V2.FromV1.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = B54D2F7925119540004BEC7D /* Advanced.EvolutionDemo.V2.FromV1.xcmappingmodel */; };
B54D2F7C251196B6004BEC7D /* Advanced.EvolutionDemo.V2.FromV1MigrationPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = B54D2F7B251196B6004BEC7D /* Advanced.EvolutionDemo.V2.FromV1MigrationPolicy.swift */; };
B54D2F7E25119DCE004BEC7D /* Advanced.EvolutionDemo.V1.FromV2.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = B54D2F7D25119DCE004BEC7D /* Advanced.EvolutionDemo.V1.FromV2.xcmappingmodel */; };
B54D2F852511B70B004BEC7D /* Advanced.EvolutionDemo.CreaturesDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = B54D2F842511B70B004BEC7D /* Advanced.EvolutionDemo.CreaturesDataSource.swift */; };
B566C8E824F9D406001134A1 /* Modern.PokedexDemo.ListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B566C8E724F9D406001134A1 /* Modern.PokedexDemo.ListView.swift */; };
B566C8EA24F9D412001134A1 /* Modern.PokedexDemo.ListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B566C8E924F9D412001134A1 /* Modern.PokedexDemo.ListViewController.swift */; };
B566C8EC24F9D694001134A1 /* Modern.PokedexDemo.ItemCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B566C8EB24F9D694001134A1 /* Modern.PokedexDemo.ItemCell.swift */; };
B566C8EE24FA1EA3001134A1 /* Modern.PokedexDemo.Details.swift in Sources */ = {isa = PBXBuildFile; fileRef = B566C8ED24FA1EA3001134A1 /* Modern.PokedexDemo.Details.swift */; };
B5931B4225124756007DA58E /* Advanced.EvolutionDemo.V1.FromV2.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5931B4125124756007DA58E /* Advanced.EvolutionDemo.V1.FromV2.swift */; };
B5931B442512480A007DA58E /* Advanced.EvolutionDemo.V2.FromV1.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5931B432512480A007DA58E /* Advanced.EvolutionDemo.V2.FromV1.swift */; };
B5931B46251248B0007DA58E /* Advanced.EvolutionDemo.V3.FromV2.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5931B45251248B0007DA58E /* Advanced.EvolutionDemo.V3.FromV2.swift */; };
B5931B4825124940007DA58E /* Advanced.EvolutionDemo.V4.FromV3.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5931B4725124940007DA58E /* Advanced.EvolutionDemo.V4.FromV3.swift */; };
B5931B4A25124993007DA58E /* Advanced.EvolutionDemo.V3.FromV4.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5931B4925124993007DA58E /* Advanced.EvolutionDemo.V3.FromV4.swift */; };
B5931B4C251249EE007DA58E /* Advanced.EvolutionDemo.V2.FromV3.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5931B4B251249EE007DA58E /* Advanced.EvolutionDemo.V2.FromV3.swift */; };
B5A3911D24E5429200E7E8BD /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A3911C24E5429200E7E8BD /* AppDelegate.swift */; };
B5A3911F24E5429200E7E8BD /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A3911E24E5429200E7E8BD /* SceneDelegate.swift */; };
B5A3912124E5429200E7E8BD /* Menu.MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A3912024E5429200E7E8BD /* Menu.MainView.swift */; };
@@ -86,6 +96,7 @@
B5A5440925049489000DC5E3 /* Advanced.EvolutionDemo.V2.Creature.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A5440825049489000DC5E3 /* Advanced.EvolutionDemo.V2.Creature.swift */; };
B5A5440B25049492000DC5E3 /* Advanced.EvolutionDemo.V3.Creature.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A5440A25049492000DC5E3 /* Advanced.EvolutionDemo.V3.Creature.swift */; };
B5A5440D2504949C000DC5E3 /* Advanced.EvolutionDemo.V4.Creature.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A5440C2504949C000DC5E3 /* Advanced.EvolutionDemo.V4.Creature.swift */; };
B5C18F3325138700001BEFB3 /* Advanced.EvolutionDemo.ProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C18F3225138700001BEFB3 /* Advanced.EvolutionDemo.ProgressView.swift */; };
B5D6F1F8250E07FD00DF5D2F /* Advanced.EvolutionDemo.V1.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D6F1F7250E07FD00DF5D2F /* Advanced.EvolutionDemo.V1.swift */; };
B5D6F1FC250E0B1700DF5D2F /* Advanced.EvolutionDemo.V2.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D6F1FB250E0B1700DF5D2F /* Advanced.EvolutionDemo.V2.swift */; };
B5D6F1FE250E0B3F00DF5D2F /* Advanced.EvolutionDemo.GeologicalPeriod.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D6F1FD250E0B3F00DF5D2F /* Advanced.EvolutionDemo.GeologicalPeriod.swift */; };
@@ -118,10 +129,20 @@
B531EFE824EB5A52005F247D /* ⭐Modern.PokedexDemo.PokedexEntry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "⭐Modern.PokedexDemo.PokedexEntry.swift"; sourceTree = "<group>"; };
B531EFEA24EB5ECD005F247D /* ⭐Modern.PokedexDemo.Service.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "⭐Modern.PokedexDemo.Service.swift"; sourceTree = "<group>"; };
B531EFEC24EB7453005F247D /* Modern.PokedexDemo.MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.PokedexDemo.MainView.swift; sourceTree = "<group>"; };
B54D2F7925119540004BEC7D /* Advanced.EvolutionDemo.V2.FromV1.xcmappingmodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcmappingmodel; path = Advanced.EvolutionDemo.V2.FromV1.xcmappingmodel; sourceTree = "<group>"; };
B54D2F7B251196B6004BEC7D /* Advanced.EvolutionDemo.V2.FromV1MigrationPolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Advanced.EvolutionDemo.V2.FromV1MigrationPolicy.swift; sourceTree = "<group>"; };
B54D2F7D25119DCE004BEC7D /* Advanced.EvolutionDemo.V1.FromV2.xcmappingmodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcmappingmodel; path = Advanced.EvolutionDemo.V1.FromV2.xcmappingmodel; sourceTree = "<group>"; };
B54D2F842511B70B004BEC7D /* Advanced.EvolutionDemo.CreaturesDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Advanced.EvolutionDemo.CreaturesDataSource.swift; sourceTree = "<group>"; };
B566C8E724F9D406001134A1 /* Modern.PokedexDemo.ListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.PokedexDemo.ListView.swift; sourceTree = "<group>"; };
B566C8E924F9D412001134A1 /* Modern.PokedexDemo.ListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.PokedexDemo.ListViewController.swift; sourceTree = "<group>"; };
B566C8EB24F9D694001134A1 /* Modern.PokedexDemo.ItemCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.PokedexDemo.ItemCell.swift; sourceTree = "<group>"; };
B566C8ED24FA1EA3001134A1 /* Modern.PokedexDemo.Details.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modern.PokedexDemo.Details.swift; sourceTree = "<group>"; };
B5931B4125124756007DA58E /* Advanced.EvolutionDemo.V1.FromV2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Advanced.EvolutionDemo.V1.FromV2.swift; sourceTree = "<group>"; };
B5931B432512480A007DA58E /* Advanced.EvolutionDemo.V2.FromV1.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Advanced.EvolutionDemo.V2.FromV1.swift; sourceTree = "<group>"; };
B5931B45251248B0007DA58E /* Advanced.EvolutionDemo.V3.FromV2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Advanced.EvolutionDemo.V3.FromV2.swift; sourceTree = "<group>"; };
B5931B4725124940007DA58E /* Advanced.EvolutionDemo.V4.FromV3.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Advanced.EvolutionDemo.V4.FromV3.swift; sourceTree = "<group>"; };
B5931B4925124993007DA58E /* Advanced.EvolutionDemo.V3.FromV4.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Advanced.EvolutionDemo.V3.FromV4.swift; sourceTree = "<group>"; };
B5931B4B251249EE007DA58E /* Advanced.EvolutionDemo.V2.FromV3.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Advanced.EvolutionDemo.V2.FromV3.swift; sourceTree = "<group>"; };
B5A3911924E5429200E7E8BD /* Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Demo.app; sourceTree = BUILT_PRODUCTS_DIR; };
B5A3911C24E5429200E7E8BD /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
B5A3911E24E5429200E7E8BD /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
@@ -192,6 +213,7 @@
B5A5440825049489000DC5E3 /* Advanced.EvolutionDemo.V2.Creature.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Advanced.EvolutionDemo.V2.Creature.swift; sourceTree = "<group>"; };
B5A5440A25049492000DC5E3 /* Advanced.EvolutionDemo.V3.Creature.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Advanced.EvolutionDemo.V3.Creature.swift; sourceTree = "<group>"; };
B5A5440C2504949C000DC5E3 /* Advanced.EvolutionDemo.V4.Creature.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Advanced.EvolutionDemo.V4.Creature.swift; sourceTree = "<group>"; };
B5C18F3225138700001BEFB3 /* Advanced.EvolutionDemo.ProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Advanced.EvolutionDemo.ProgressView.swift; sourceTree = "<group>"; };
B5D6F1F7250E07FD00DF5D2F /* Advanced.EvolutionDemo.V1.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Advanced.EvolutionDemo.V1.swift; sourceTree = "<group>"; };
B5D6F1FB250E0B1700DF5D2F /* Advanced.EvolutionDemo.V2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Advanced.EvolutionDemo.V2.swift; sourceTree = "<group>"; };
B5D6F1FD250E0B3F00DF5D2F /* Advanced.EvolutionDemo.GeologicalPeriod.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Advanced.EvolutionDemo.GeologicalPeriod.swift; sourceTree = "<group>"; };
@@ -516,6 +538,8 @@
B5A54400250487C7000DC5E3 /* Advanced.EvolutionDemo.ListView.swift */,
B5A54402250487D4000DC5E3 /* Advanced.EvolutionDemo.ItemView.swift */,
B5D6F208250E14AA00DF5D2F /* Advanced.EvolutionDemo.Migrator.swift */,
B5C18F3225138700001BEFB3 /* Advanced.EvolutionDemo.ProgressView.swift */,
B54D2F842511B70B004BEC7D /* Advanced.EvolutionDemo.CreaturesDataSource.swift */,
B5A543FD25048794000DC5E3 /* Models */,
);
path = EvolutionDemo;
@@ -554,6 +578,8 @@
children = (
B5D6F1F7250E07FD00DF5D2F /* Advanced.EvolutionDemo.V1.swift */,
B5A5440625049480000DC5E3 /* Advanced.EvolutionDemo.V1.Creature.swift */,
B5931B4125124756007DA58E /* Advanced.EvolutionDemo.V1.FromV2.swift */,
B54D2F7D25119DCE004BEC7D /* Advanced.EvolutionDemo.V1.FromV2.xcmappingmodel */,
);
name = V1;
sourceTree = "<group>";
@@ -563,6 +589,10 @@
children = (
B5D6F1FB250E0B1700DF5D2F /* Advanced.EvolutionDemo.V2.swift */,
B5A5440825049489000DC5E3 /* Advanced.EvolutionDemo.V2.Creature.swift */,
B5931B432512480A007DA58E /* Advanced.EvolutionDemo.V2.FromV1.swift */,
B54D2F7925119540004BEC7D /* Advanced.EvolutionDemo.V2.FromV1.xcmappingmodel */,
B54D2F7B251196B6004BEC7D /* Advanced.EvolutionDemo.V2.FromV1MigrationPolicy.swift */,
B5931B4B251249EE007DA58E /* Advanced.EvolutionDemo.V2.FromV3.swift */,
);
name = V2;
sourceTree = "<group>";
@@ -572,6 +602,8 @@
children = (
B5D6F204250E0DD600DF5D2F /* Advanced.EvolutionDemo.V3.swift */,
B5A5440A25049492000DC5E3 /* Advanced.EvolutionDemo.V3.Creature.swift */,
B5931B45251248B0007DA58E /* Advanced.EvolutionDemo.V3.FromV2.swift */,
B5931B4925124993007DA58E /* Advanced.EvolutionDemo.V3.FromV4.swift */,
);
name = V3;
sourceTree = "<group>";
@@ -581,6 +613,7 @@
children = (
B5D6F206250E0E3B00DF5D2F /* Advanced.EvolutionDemo.V4.swift */,
B5A5440C2504949C000DC5E3 /* Advanced.EvolutionDemo.V4.Creature.swift */,
B5931B4725124940007DA58E /* Advanced.EvolutionDemo.V4.FromV3.swift */,
);
name = V4;
sourceTree = "<group>";
@@ -675,26 +708,26 @@
B5A391B124E96AF600E7E8BD /* Modern.PokedexDemo.swift in Sources */,
B5A3918324E7A21800E7E8BD /* Modern.TimeZonesDemo.swift in Sources */,
B5A3915E24E6922E00E7E8BD /* ⭐Modern.PlacemarksDemo.swift in Sources */,
B5D6F207250E0E3B00DF5D2F /* Advanced.EvolutionDemo.V4.swift in Sources */,
B5A54405250487F5000DC5E3 /* Advanced.EvolutionDemo.CreatureType.swift in Sources */,
B5A5440725049480000DC5E3 /* Advanced.EvolutionDemo.V1.Creature.swift in Sources */,
B54D2F852511B70B004BEC7D /* Advanced.EvolutionDemo.CreaturesDataSource.swift in Sources */,
B5D6F1FE250E0B3F00DF5D2F /* Advanced.EvolutionDemo.GeologicalPeriod.swift in Sources */,
B5A5440925049489000DC5E3 /* Advanced.EvolutionDemo.V2.Creature.swift in Sources */,
B5A5440B25049492000DC5E3 /* Advanced.EvolutionDemo.V3.Creature.swift in Sources */,
B5A5440D2504949C000DC5E3 /* Advanced.EvolutionDemo.V4.Creature.swift in Sources */,
B5A54403250487D5000DC5E3 /* Advanced.EvolutionDemo.ItemView.swift in Sources */,
B5D6F1F8250E07FD00DF5D2F /* Advanced.EvolutionDemo.V1.swift in Sources */,
B5A54401250487C7000DC5E3 /* Advanced.EvolutionDemo.ListView.swift in Sources */,
B5A543FF250487B1000DC5E3 /* Advanced.EvolutionDemo.MainView.swift in Sources */,
B5D6F209250E14AA00DF5D2F /* Advanced.EvolutionDemo.Migrator.swift in Sources */,
B5C18F3325138700001BEFB3 /* Advanced.EvolutionDemo.ProgressView.swift in Sources */,
B5D6F1F8250E07FD00DF5D2F /* Advanced.EvolutionDemo.V1.swift in Sources */,
B5D6F210250E1E3200DF5D2F /* Advanced.EvolutionDemo.V1.xcdatamodeld in Sources */,
B5D6F1FC250E0B1700DF5D2F /* Advanced.EvolutionDemo.V2.swift in Sources */,
B5D6F205250E0DD600DF5D2F /* Advanced.EvolutionDemo.V3.swift in Sources */,
B5D6F207250E0E3B00DF5D2F /* Advanced.EvolutionDemo.V4.swift in Sources */,
B5A543E924FB84A1000DC5E3 /* Classic.ColorsDemo.DetailView.swift in Sources */,
B5A543E724FB82BB000DC5E3 /* Classic.ColorsDemo.Filter.swift in Sources */,
B5A543F324FB84EC000DC5E3 /* Classic.ColorsDemo.ItemCell.swift in Sources */,
B5D6F209250E14AA00DF5D2F /* Advanced.EvolutionDemo.Migrator.swift in Sources */,
B5A543EF24FB84D1000DC5E3 /* Classic.ColorsDemo.LIstView.swift in Sources */,
B5A543ED24FB84BE000DC5E3 /* Classic.ColorsDemo.MainView.swift in Sources */,
B5A543DD24FB78F9000DC5E3 /* Classic.ColorsDemo.Palette.swift in Sources */,
B5A3919824E7E67000E7E8BD /* Modern.ColorsDemo.Filter.swift in Sources */,
B5D6F1FC250E0B1700DF5D2F /* Advanced.EvolutionDemo.V2.swift in Sources */,
B5A3919224E7E0C600E7E8BD /* Modern.ColorsDemo.Palette.swift in Sources */,
B5A3919E24E8EEB600E7E8BD /* Modern.ColorsDemo.SwiftUI.swift in Sources */,
B5A391A024E8F00A00E7E8BD /* Modern.ColorsDemo.UIKit.swift in Sources */,
@@ -703,7 +736,6 @@
B5A3916524E698C700E7E8BD /* Modern.PlacemarksDemo.Place.swift in Sources */,
B566C8EE24FA1EA3001134A1 /* Modern.PokedexDemo.Details.swift in Sources */,
B566C8EC24F9D694001134A1 /* Modern.PokedexDemo.ItemCell.swift in Sources */,
B5D6F210250E1E3200DF5D2F /* Advanced.EvolutionDemo.V1.xcdatamodeld in Sources */,
B566C8E824F9D406001134A1 /* Modern.PokedexDemo.ListView.swift in Sources */,
B566C8EA24F9D412001134A1 /* Modern.PokedexDemo.ListViewController.swift in Sources */,
B531EFED24EB7453005F247D /* Modern.PokedexDemo.MainView.swift in Sources */,
@@ -712,7 +744,6 @@
B5A3918624E7A54A00E7E8BD /* Modern.TimeZonesDemo.TimeZone.swift in Sources */,
B5A543EB24FB84AF000DC5E3 /* ⭐Classic.ColorsDemo.DetailViewController.swift in Sources */,
B5A543F124FB84DD000DC5E3 /* ⭐Classic.ColorsDemo.ListViewController.swift in Sources */,
B5D6F205250E0DD600DF5D2F /* Advanced.EvolutionDemo.V3.swift in Sources */,
B5A391A624E8F4EA00E7E8BD /* ⭐Modern.ColorsDemo.MainView.swift in Sources */,
B5A3916224E697BA00E7E8BD /* ⭐Modern.PlacemarksDemo.MainView.swift in Sources */,
B5A391B424E96C0A00E7E8BD /* ⭐Modern.PokedexDemo.Form.swift in Sources */,
@@ -721,6 +752,19 @@
B531EFEB24EB5ECD005F247D /* ⭐Modern.PokedexDemo.Service.swift in Sources */,
B5A391B924E96F8500E7E8BD /* ⭐Modern.PokedexDemo.Species.swift in Sources */,
B5A3918824E7A8F900E7E8BD /* ⭐Modern.TimeZonesDemo.MainView.swift in Sources */,
B5A5440725049480000DC5E3 /* Advanced.EvolutionDemo.V1.Creature.swift in Sources */,
B5931B4225124756007DA58E /* Advanced.EvolutionDemo.V1.FromV2.swift in Sources */,
B54D2F7E25119DCE004BEC7D /* Advanced.EvolutionDemo.V1.FromV2.xcmappingmodel in Sources */,
B5A5440925049489000DC5E3 /* Advanced.EvolutionDemo.V2.Creature.swift in Sources */,
B5931B442512480A007DA58E /* Advanced.EvolutionDemo.V2.FromV1.swift in Sources */,
B54D2F7A25119540004BEC7D /* Advanced.EvolutionDemo.V2.FromV1.xcmappingmodel in Sources */,
B54D2F7C251196B6004BEC7D /* Advanced.EvolutionDemo.V2.FromV1MigrationPolicy.swift in Sources */,
B5931B4C251249EE007DA58E /* Advanced.EvolutionDemo.V2.FromV3.swift in Sources */,
B5A5440B25049492000DC5E3 /* Advanced.EvolutionDemo.V3.Creature.swift in Sources */,
B5931B46251248B0007DA58E /* Advanced.EvolutionDemo.V3.FromV2.swift in Sources */,
B5931B4A25124993007DA58E /* Advanced.EvolutionDemo.V3.FromV4.swift in Sources */,
B5A5440D2504949C000DC5E3 /* Advanced.EvolutionDemo.V4.Creature.swift in Sources */,
B5931B4825124940007DA58E /* Advanced.EvolutionDemo.V4.FromV3.swift in Sources */,
B5A391AC24E9143B00E7E8BD /* Modern.ColorsDemo.UIKit.DetailView.swift in Sources */,
B5A391AA24E9104300E7E8BD /* Modern.ColorsDemo.UIKit.ItemCell.swift in Sources */,
B5A391A424E8F04300E7E8BD /* Modern.ColorsDemo.UIKit.ListView.swift in Sources */,
@@ -749,12 +793,14 @@
B5A3911324E5424E00E7E8BD /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
};
name = Debug;
};
B5A3911424E5424E00E7E8BD /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
};
name = Release;
};

View File

@@ -96,9 +96,10 @@ extension Menu {
Menu.ItemView(
title: "Evolution",
subtitle: "Migrating and reverse-migrating stores",
destination: { EmptyView() }
destination: {
Advanced.EvolutionDemo.MainView()
}
)
.disabled(true)
Menu.ItemView(
title: "Logger",
subtitle: "Implementing a custom logger",

View File

@@ -18,7 +18,13 @@ extension Advanced.EvolutionDemo {
protocol Advanced_EvolutionDemo_CreatureType: DynamicObject, CustomStringConvertible {
var dnaCode: Int64 { get }
var dnaCode: Int64 { get set }
static func dataSource(in dataStack: DataStack) -> Advanced.EvolutionDemo.CreaturesDataSource
static func count(in transaction: BaseDataTransaction) throws -> Int
static func create(in transaction: BaseDataTransaction) -> Self
func mutate(in transaction: BaseDataTransaction)
}

View File

@@ -0,0 +1,165 @@
//
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
import CoreStore
import Combine
// MARK: - Advanced.EvolutionDemo
extension Advanced.EvolutionDemo {
// MARK: - Advanced.EvolutionDemo.CreaturesDataSource
final class CreaturesDataSource: ObservableObject {
// MARK: Internal
init<T: NSManagedObject & Advanced.EvolutionDemo.CreatureType>(
listPublisher: ListPublisher<T>,
dataStack: DataStack
) {
self.numberOfItems = {
listPublisher.snapshot.numberOfItems
}
self.itemDescriptionAtIndex = { index in
listPublisher.snapshot[index].object?.description
}
self.addItems = { count in
dataStack.perform(
asynchronous: { transaction in
let nextDNACode = try transaction.fetchCount(From<T>())
for offset in 0 ..< count {
let object = transaction.create(Into<T>())
object.dnaCode = .init(nextDNACode + offset)
object.mutate(in: transaction)
}
},
completion: { _ in }
)
}
self.mutateItemAtIndex = { index in
let object = listPublisher.snapshot[index]
dataStack.perform(
asynchronous: { transaction in
object
.asEditable(in: transaction)?
.mutate(in: transaction)
},
completion: { _ in }
)
}
self.deleteAllItems = {
dataStack.perform(
asynchronous: { transaction in
try transaction.deleteAll(From<T>())
},
completion: { _ in }
)
}
listPublisher.addObserver(self) { [weak self] (listPublisher) in
self?.objectWillChange.send()
}
}
init<T: CoreStoreObject & Advanced.EvolutionDemo.CreatureType>(
listPublisher: ListPublisher<T>,
dataStack: DataStack
) {
self.numberOfItems = {
listPublisher.snapshot.numberOfItems
}
self.itemDescriptionAtIndex = { index in
listPublisher.snapshot[index].object?.description
}
self.addItems = { count in
dataStack.perform(
asynchronous: { transaction in
let nextDNACode = try transaction.fetchCount(From<T>())
for offset in 0 ..< count {
let object = transaction.create(Into<T>())
object.dnaCode = .init(nextDNACode + offset)
object.mutate(in: transaction)
}
},
completion: { _ in }
)
}
self.mutateItemAtIndex = { index in
let object = listPublisher.snapshot[index]
dataStack.perform(
asynchronous: { transaction in
object
.asEditable(in: transaction)?
.mutate(in: transaction)
},
completion: { _ in }
)
}
self.deleteAllItems = {
dataStack.perform(
asynchronous: { transaction in
try transaction.deleteAll(From<T>())
},
completion: { _ in }
)
}
listPublisher.addObserver(self) { [weak self] (listPublisher) in
self?.objectWillChange.send()
}
}
func numberOfCreatures() -> Int {
return self.numberOfItems()
}
func creatureDescription(at index: Int) -> String? {
return self.itemDescriptionAtIndex(index)
}
func mutate(at index: Int) {
self.mutateItemAtIndex(index)
}
func add(count: Int) {
self.addItems(count)
}
func clear() {
self.deleteAllItems()
}
// MARK: Private
private let numberOfItems: () -> Int
private let itemDescriptionAtIndex: (Int) -> String?
private let mutateItemAtIndex: (Int) -> Void
private let addItems: (Int) -> Void
private let deleteAllItems: () -> Void
}
}

View File

@@ -2,13 +2,17 @@
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
import CoreStore
// MARK: - AdvancedEvolutionDemo
extension Advanced.EvolutionDemo {
// MARK: - GeologicalPeriod
enum GeologicalPeriod: CaseIterable {
enum GeologicalPeriod: RawRepresentable, CaseIterable, Hashable, CustomStringConvertible {
// MARK: Internal
@@ -17,6 +21,11 @@ extension Advanced.EvolutionDemo {
case ageOfReptiles
case ageOfMammals
var version: ModelVersion {
return self.rawValue
}
var creatureType: Advanced.EvolutionDemo.CreatureType.Type {
switch self {
@@ -31,5 +40,60 @@ extension Advanced.EvolutionDemo {
return Advanced.EvolutionDemo.V4.Creature.self
}
}
// MARK: CustomStringConvertible
var description: String {
switch self {
case .ageOfInvertebrates:
return "Invertebrates"
case .ageOfFishes:
return "Fishes"
case .ageOfReptiles:
return "Reptiles"
case .ageOfMammals:
return "Mammals"
}
}
// MARK: RawRepresentable
typealias RawValue = ModelVersion
var rawValue: ModelVersion {
switch self {
case .ageOfInvertebrates:
return Advanced.EvolutionDemo.V1.name
case .ageOfFishes:
return Advanced.EvolutionDemo.V2.name
case .ageOfReptiles:
return Advanced.EvolutionDemo.V3.name
case .ageOfMammals:
return Advanced.EvolutionDemo.V4.name
}
}
init?(rawValue: ModelVersion) {
switch rawValue {
case Advanced.EvolutionDemo.V1.name:
self = .ageOfInvertebrates
case Advanced.EvolutionDemo.V2.name:
self = .ageOfFishes
case Advanced.EvolutionDemo.V3.name:
self = .ageOfReptiles
case Advanced.EvolutionDemo.V4.name:
self = .ageOfMammals
default:
return nil
}
}
}
}

View File

@@ -2,4 +2,74 @@
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
import Foundation
import SwiftUI
// MARK: - Advanced.EvolutionDemo
extension Advanced.EvolutionDemo {
// MARK: - Advanced.EvolutionDemo.ItemView
struct ItemView: View {
// MARK: Internal
init(description: String?, mutate: @escaping () -> Void) {
self.description = description
self.mutate = mutate
}
// MARK: View
var body: some View {
HStack {
Text(self.description ?? "")
.font(.footnote)
.foregroundColor(.primary)
Spacer()
Button(
action: self.mutate,
label: {
Text("Mutate")
.foregroundColor(.accentColor)
.fontWeight(.bold)
}
)
.buttonStyle(PlainButtonStyle())
}
.disabled(self.description == nil)
}
// MARK: FilePrivate
fileprivate let description: String?
fileprivate let mutate: () -> Void
}
}
#if DEBUG
struct _Demo_Advanced_EvolutionDemo_ItemView_Preview: PreviewProvider {
// MARK: PreviewProvider
static var previews: some View {
Advanced.EvolutionDemo.ItemView(
description: """
dnaCode: 123
numberOfLimbs: 4
hasVertebrae: true
hasHead: true
hasTail: true
habitat: land
hasWings: false
""",
mutate: {}
)
}
}
#endif

View File

@@ -2,4 +2,98 @@
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
import Foundation
import CoreStore
import SwiftUI
// MARK: - Advanced.EvolutionDemo
extension Advanced.EvolutionDemo {
// MARK: - Advanced.EvolutionDemo.ListView
struct ListView: View {
// MARK: View
var body: some View {
let dataSource = self.dataSource
return List {
ForEach(0 ..< dataSource.numberOfCreatures(), id: \.self) { (index) in
Advanced.EvolutionDemo.ItemView(
description: dataSource.creatureDescription(at: index),
mutate: {
dataSource.mutate(at: index)
}
)
}
}
.listStyle(PlainListStyle())
}
// MARK: Internal
init(
period: Advanced.EvolutionDemo.GeologicalPeriod,
dataStack: DataStack,
dataSource: Advanced.EvolutionDemo.CreaturesDataSource
) {
self.period = period
self.dataStack = dataStack
self.dataSource = dataSource
}
// MARK: Private
private let period: Advanced.EvolutionDemo.GeologicalPeriod
private let dataStack: DataStack
@ObservedObject
private var dataSource: Advanced.EvolutionDemo.CreaturesDataSource
}
}
#if DEBUG
struct _Demo_Advanced_EvolutionDemo_ListView_Preview: PreviewProvider {
// MARK: PreviewProvider
static var previews: some View {
let dataStack = DataStack(
CoreStoreSchema(
modelVersion: Advanced.EvolutionDemo.V4.name,
entities: [
Entity<Advanced.EvolutionDemo.V4.Creature>("Creature")
]
)
)
try! dataStack.addStorageAndWait(
SQLiteStore(fileName: "Advanced.EvolutionDemo.ListView.Preview.sqlite")
)
try! dataStack.perform(
synchronous: { transaction in
for dnaCode in 0 ..< 10 as Range<Int64> {
let object = transaction.create(Into<Advanced.EvolutionDemo.V4.Creature>())
object.dnaCode = dnaCode
object.mutate(in: transaction)
}
}
)
return Advanced.EvolutionDemo.ListView(
period: .ageOfMammals,
dataStack: dataStack,
dataSource: Advanced.EvolutionDemo.V4.Creature.dataSource(in: dataStack)
)
}
}
#endif

View File

@@ -2,4 +2,77 @@
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
import Foundation
import CoreStore
import SwiftUI
// MARK: - Advanced.EvolutionDemo
extension Advanced.EvolutionDemo {
// MARK: - Advanced.EvolutionDemo.MainView
struct MainView: View {
// MARK: View
var body: some View {
let migrator = self.migrator
let listView: AnyView
if let current = migrator.current {
listView = AnyView(
Advanced.EvolutionDemo.ListView(
period: current.period,
dataStack: current.dataStack,
dataSource: current.dataSource
)
)
}
else {
listView = AnyView(
Advanced.EvolutionDemo.ProgressView(progress: migrator.progress)
)
}
return VStack(spacing: 0) {
HStack(alignment: .center, spacing: 0) {
Text("Age of")
.padding(.trailing)
Picker(selection: self.$migrator.currentPeriod, label: EmptyView()) {
ForEach(Advanced.EvolutionDemo.GeologicalPeriod.allCases, id: \.self) { period in
Text(period.description).tag(period)
}
}
.pickerStyle(SegmentedPickerStyle())
}
.padding()
listView
.edgesIgnoringSafeArea(.vertical)
}
.navigationBarTitle("Evolution")
.disabled(migrator.isBusy || migrator.current == nil)
}
// MARK: Private
@ObservedObject
private var migrator: Advanced.EvolutionDemo.Migrator = .init()
}
}
#if DEBUG
struct _Demo_Advanced_EvolutionDemo_MainView_Preview: PreviewProvider {
// MARK: PreviewProvider
static var previews: some View {
Advanced.EvolutionDemo.MainView()
}
}
#endif

View File

@@ -14,34 +14,20 @@ extension Advanced.EvolutionDemo {
// MARK: - Advanced.EvolutionDemo.Migrator
final class Migrator: ObservableObject {
var currentPeriod: Advanced.EvolutionDemo.GeologicalPeriod? {
return self.current?.period
}
// MARK: Private
private var current: (period: Advanced.EvolutionDemo.GeologicalPeriod, dataStack: DataStack)? {
willSet {
self.objectWillChange.send()
}
}
private func findAndSetCurrentVersion() {
/**
Sample 1: Creating a complex `DataStack` that contains all schema histories. The `exactCurrentModelVersion` will specify the target version (if required), and `migrationChain` will provide the upgrade/downgrade progressive migration path.
*/
private func createDataStack(
exactCurrentModelVersion: ModelVersion?,
migrationChain: MigrationChain
) -> DataStack {
let xcodeV1ToV2ModelSchema = XcodeDataModelSchema.from(
modelName: "Advanced.EvolutionDemo.V1",
bundle: Bundle(for: Advanced.EvolutionDemo.V1.Creature.self),
migrationChain: [
Advanced.EvolutionDemo.V1.name,
Advanced.EvolutionDemo.V2.name
]
bundle: Bundle(for: Advanced.EvolutionDemo.V1.Creature.self)
)
let dataStack = DataStack(
return DataStack(
schemaHistory: SchemaHistory(
allSchema: xcodeV1ToV2ModelSchema.allSchema
+ [
@@ -58,26 +44,196 @@ extension Advanced.EvolutionDemo {
]
)
],
migrationChain: [
Advanced.EvolutionDemo.V1.name,
Advanced.EvolutionDemo.V2.name,
Advanced.EvolutionDemo.V3.name,
Advanced.EvolutionDemo.V4.name
]
migrationChain: migrationChain,
exactCurrentModelVersion: exactCurrentModelVersion
)
)
}
/**
Sample 2: Creating a complex `SQLiteStore` that contains all schema mappings for both upgrade and downgrade cases.
*/
private func accessSQLiteStore() -> SQLiteStore {
let upgradeMappings: [SchemaMappingProvider] = [
Advanced.EvolutionDemo.V2.FromV1.mapping,
Advanced.EvolutionDemo.V3.FromV2.mapping,
Advanced.EvolutionDemo.V4.FromV3.mapping
]
let downgradeMappings: [SchemaMappingProvider] = [
Advanced.EvolutionDemo.V3.FromV4.mapping,
Advanced.EvolutionDemo.V2.FromV3.mapping,
Advanced.EvolutionDemo.V1.FromV2.mapping,
]
return SQLiteStore(
fileName: "Advanced.EvolutionDemo.sqlite",
configuration: nil,
migrationMappingProviders: upgradeMappings + downgradeMappings,
localStorageOptions: []
)
}
// MARK: Internal
var currentPeriod: Advanced.EvolutionDemo.GeologicalPeriod = Advanced.EvolutionDemo.GeologicalPeriod.allCases.last! {
didSet {
self.selectModelVersion(self.currentPeriod)
}
}
private(set) var current: (
period: Advanced.EvolutionDemo.GeologicalPeriod,
dataStack: DataStack,
dataSource: Advanced.EvolutionDemo.CreaturesDataSource
)? {
willSet {
self.objectWillChange.send()
}
}
private(set) var isBusy: Bool = false
private(set) var progress: Progress?
init() {
self.synchronizeCurrentVersion()
}
private func selectModelVersion(_ period: GeologicalPeriod) {
guard period != self.current?.period else {
// MARK: Private
private func synchronizeCurrentVersion() {
// Since we are only interested in finding current version, we'll assume an upgrading `MigrationChain`
let dataStack = self.createDataStack(
exactCurrentModelVersion: nil,
migrationChain: MigrationChain(
Advanced.EvolutionDemo.GeologicalPeriod.allCases.map({ $0.version })
)
)
let migrations = try! dataStack.requiredMigrationsForStorage(
self.accessSQLiteStore()
)
if let period = migrations.first
.flatMap({ Advanced.EvolutionDemo.GeologicalPeriod(rawValue: $0.sourceVersion) }) {
self.selectModelVersion(period)
}
else {
self.selectModelVersion(self.currentPeriod)
}
}
private func selectModelVersion(_ period: Advanced.EvolutionDemo.GeologicalPeriod) {
let currentPeriod = self.current?.period
guard period != currentPeriod else {
return
}
self.objectWillChange.send()
self.isBusy = true
// explicitly trigger `NSPersistentStore` cleanup by deallocating the `DataStack`
self.current = nil
let migrationChain: MigrationChain
switch (currentPeriod?.version, period.version) {
case (nil, let newVersion):
migrationChain = [newVersion]
case (let currentVersion?, let newVersion):
let upgradeMigrationChain = Advanced.EvolutionDemo.GeologicalPeriod.allCases
.map({ $0.version })
let currentVersionIndex = upgradeMigrationChain.firstIndex(of: currentVersion)!
let newVersionIndex = upgradeMigrationChain.firstIndex(of: newVersion)!
migrationChain = MigrationChain(
currentVersionIndex > newVersionIndex
? upgradeMigrationChain.reversed()
: upgradeMigrationChain
)
}
let dataStack = self.createDataStack(
exactCurrentModelVersion: period.version,
migrationChain: migrationChain
)
let completion = { [weak self] () -> Void in
guard let self = self else {
return
}
self.objectWillChange.send()
defer {
self.isBusy = false
}
self.current = (
period: period,
dataStack: dataStack,
dataSource: period.creatureType.dataSource(in: dataStack)
)
self.currentPeriod = period
}
self.progress = dataStack.addStorage(
self.accessSQLiteStore(),
completion: { [weak self] result in
guard let self = self else {
return
}
guard case .success = result else {
self.objectWillChange.send()
self.isBusy = false
return
}
if self.progress == nil {
self.spawnCreatures(in: dataStack, period: period, completion: completion)
}
else {
completion()
}
}
)
}
private func spawnCreatures(
in dataStack: DataStack,
period: Advanced.EvolutionDemo.GeologicalPeriod,
completion: @escaping () -> Void
) {
dataStack.perform(
asynchronous: { (transaction) in
let creatureType = period.creatureType
for dnaCode in try creatureType.count(in: transaction) ..< 10000 {
let object = creatureType.create(in: transaction)
object.dnaCode = Int64(dnaCode)
object.mutate(in: transaction)
}
},
completion: { _ in completion() }
)
}

View File

@@ -0,0 +1,127 @@
//
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
import SwiftUI
// MARK: - Advanced.EvolutionDemo
extension Advanced.EvolutionDemo {
// MARK: - Advanced.EvolutionDemo.ProgressView
struct ProgressView: View {
// MARK: Internal
init(progress: Progress?) {
self.progressObserver = .init(progress)
}
// MARK: View
var body: some View {
guard self.progressObserver.isMigrating else {
return AnyView(
VStack(alignment: .center) {
Text("Preparing creatures...")
.padding()
Spacer()
}
.padding()
)
}
return AnyView(
VStack(alignment: .leading) {
Text("Migrating: \(self.progressObserver.localizedDescription)")
.font(.headline)
.padding([.top, .horizontal])
Text("Progressive step: \(self.progressObserver.localizedAdditionalDescription)")
.font(.subheadline)
.padding(.horizontal)
GeometryReader { geometry in
ZStack(alignment: .leading) {
Color.gray
.opacity(0.2)
.frame(width: geometry.size.width, height: 8)
.cornerRadius(4.0)
Color.blue
.frame(
width: geometry.size.width
* self.progressObserver.fractionCompleted,
height: 8
)
.cornerRadius(4.0)
}
}
.fixedSize(horizontal: false, vertical: true)
.padding()
Spacer()
}
.padding()
)
}
// MARK: FilePrivate
@ObservedObject
private var progressObserver: ProgressObserver
// MARK: - ProgressObserver
fileprivate final class ProgressObserver: ObservableObject {
private(set) var fractionCompleted: CGFloat = 0
private(set) var localizedDescription: String = ""
private(set) var localizedAdditionalDescription: String = ""
var isMigrating: Bool {
return self.progress != nil
}
init(_ progress: Progress?) {
self.progress = progress
progress?.setProgressHandler { [weak self] (progess) in
guard let self = self else {
return
}
self.objectWillChange.send()
self.fractionCompleted = CGFloat(progress?.fractionCompleted ?? 0)
self.localizedDescription = progress?.localizedDescription ?? ""
self.localizedAdditionalDescription = progress?.localizedAdditionalDescription ?? ""
}
}
// MARK: Private
private let progress: Progress?
}
}
}
#if DEBUG
struct _Demo_Advanced_EvolutionDemo_ProgressView_Preview: PreviewProvider {
// MARK: PreviewProvider
static var previews: some View {
let progress = Progress(totalUnitCount: 10)
progress.completedUnitCount = 3
return Advanced.EvolutionDemo.ProgressView(
progress: progress
)
}
}
#endif

View File

@@ -30,6 +30,31 @@ final class Advanced_EvolutionDemo_V1_Creature: NSManagedObject, Advanced.Evolut
// MARK: Advanced.EvolutionDemo.CreatureType
static func dataSource(in dataStack: DataStack) -> Advanced.EvolutionDemo.CreaturesDataSource {
return .init(
listPublisher: dataStack.publishList(
From<Advanced.EvolutionDemo.V1.Creature>()
.orderBy(.descending(\.dnaCode))
),
dataStack: dataStack
)
}
static func count(in transaction: BaseDataTransaction) throws -> Int {
return try transaction.fetchCount(
From<Advanced.EvolutionDemo.V1.Creature>()
)
}
static func create(in transaction: BaseDataTransaction) -> Advanced.EvolutionDemo.V1.Creature {
return transaction.create(
Into<Advanced.EvolutionDemo.V1.Creature>()
)
}
func mutate(in transaction: BaseDataTransaction) {

View File

@@ -0,0 +1,27 @@
//
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
import CoreStore
// MARK: - Advanced.EvolutionDemo.V1
extension Advanced.EvolutionDemo.V1 {
// MARK: - Advanced.EvolutionDemo.V1.FromV2
enum FromV2 {
// MARK: Internal
static var mapping: XcodeSchemaMappingProvider {
return XcodeSchemaMappingProvider(
from: Advanced.EvolutionDemo.V2.name,
to: Advanced.EvolutionDemo.V1.name,
mappingModelBundle: Bundle(for: Advanced.EvolutionDemo.V1.Creature.self)
)
}
}
}

View File

@@ -18,7 +18,7 @@ extension Advanced.EvolutionDemo {
// MARK: Internal
static let name: ModelVersion = "Advanced.Evolution.V1"
static let name: ModelVersion = "Advanced.EvolutionDemo.V1"
typealias Creature = Advanced_EvolutionDemo_V1_Creature
}

View File

@@ -42,6 +42,31 @@ final class Advanced_EvolutionDemo_V2_Creature: NSManagedObject, Advanced.Evolut
// MARK: Advanced.EvolutionDemo.CreatureType
static func dataSource(in dataStack: DataStack) -> Advanced.EvolutionDemo.CreaturesDataSource {
return .init(
listPublisher: dataStack.publishList(
From<Advanced.EvolutionDemo.V2.Creature>()
.orderBy(.descending(\.dnaCode))
),
dataStack: dataStack
)
}
static func count(in transaction: BaseDataTransaction) throws -> Int {
return try transaction.fetchCount(
From<Advanced.EvolutionDemo.V2.Creature>()
)
}
static func create(in transaction: BaseDataTransaction) -> Advanced.EvolutionDemo.V2.Creature {
return transaction.create(
Into<Advanced.EvolutionDemo.V2.Creature>()
)
}
func mutate(in transaction: BaseDataTransaction) {

View File

@@ -0,0 +1,27 @@
//
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
import CoreStore
// MARK: - Advanced.EvolutionDemo.V2
extension Advanced.EvolutionDemo.V2 {
// MARK: - Advanced.EvolutionDemo.V2.FromV1
enum FromV1 {
// MARK: Internal
static var mapping: XcodeSchemaMappingProvider {
return XcodeSchemaMappingProvider(
from: Advanced.EvolutionDemo.V1.name,
to: Advanced.EvolutionDemo.V2.name,
mappingModelBundle: Bundle(for: Advanced.EvolutionDemo.V1.Creature.self)
)
}
}
}

View File

@@ -0,0 +1,40 @@
//
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
import CoreData
import CoreStore
// MARK: - Advanced.EvolutionDemo.V2.FromV1MigrationPolicy
@objc(Advanced_EvolutionDemo_V2_FromV1MigrationPolicy)
final class Advanced_EvolutionDemo_V2_FromV1MigrationPolicy: NSEntityMigrationPolicy {
// MARK: NSEntityMigrationPolicy
override func createDestinationInstances(
forSource sInstance: NSManagedObject,
in mapping: NSEntityMapping,
manager: NSMigrationManager
) throws {
try super.createDestinationInstances(
forSource: sInstance,
in: mapping,
manager: manager
)
for dInstance in manager.destinationInstances(forEntityMappingName: mapping.name, sourceInstances: [sInstance]) {
dInstance.setValue(
Bool.random(),
forKey: #keyPath(Advanced.EvolutionDemo.V2.Creature.hasVertebrae)
)
dInstance.setValue(
Bool.random(),
forKey: #keyPath(Advanced.EvolutionDemo.V2.Creature.hasTail)
)
}
}
}

View File

@@ -0,0 +1,41 @@
//
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
import CoreStore
// MARK: - Advanced.EvolutionDemo.V2
extension Advanced.EvolutionDemo.V2 {
// MARK: - Advanced.EvolutionDemo.V2.FromV3
enum FromV3 {
// MARK: Internal
static var mapping: CustomSchemaMappingProvider {
return CustomSchemaMappingProvider(
from: Advanced.EvolutionDemo.V3.name,
to: Advanced.EvolutionDemo.V2.name,
entityMappings: [
.transformEntity(
sourceEntity: "Creature",
destinationEntity: "Creature",
transformer: { (source, createDestination) in
let destination = createDestination()
destination["dnaCode"] = source["dnaCode"]
destination["numberOfFlippers"] = source["numberOfLimbs"]
destination["hasVertebrae"] = source["hasVertebrae"]
destination["hasHead"] = source["hasHead"]
destination["hasTail"] = source["hasTail"]
}
)
]
)
}
}
}

View File

@@ -18,8 +18,10 @@ extension Advanced.EvolutionDemo {
// MARK: Internal
static let name: ModelVersion = "Advanced.Evolution.V2"
static let name: ModelVersion = "Advanced.EvolutionDemo.V2"
typealias Creature = Advanced_EvolutionDemo_V2_Creature
typealias FromV1MigrationPolicy = Advanced_EvolutionDemo_V2_FromV1MigrationPolicy
}
}

View File

@@ -64,6 +64,31 @@ extension Advanced.EvolutionDemo.V3 {
// MARK: Advanced.EvolutionDemo.CreatureType
static func dataSource(in dataStack: DataStack) -> Advanced.EvolutionDemo.CreaturesDataSource {
return .init(
listPublisher: dataStack.publishList(
From<Advanced.EvolutionDemo.V3.Creature>()
.orderBy(.descending(\.$dnaCode))
),
dataStack: dataStack
)
}
static func count(in transaction: BaseDataTransaction) throws -> Int {
return try transaction.fetchCount(
From<Advanced.EvolutionDemo.V3.Creature>()
)
}
static func create(in transaction: BaseDataTransaction) -> Advanced.EvolutionDemo.V3.Creature {
return transaction.create(
Into<Advanced.EvolutionDemo.V3.Creature>()
)
}
func mutate(in transaction: BaseDataTransaction) {

View File

@@ -0,0 +1,43 @@
//
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
import CoreStore
// MARK: - Advanced.EvolutionDemo.V3
extension Advanced.EvolutionDemo.V3 {
// MARK: - Advanced.EvolutionDemo.V3.FromV2
enum FromV2 {
// MARK: Internal
static var mapping: CustomSchemaMappingProvider {
return CustomSchemaMappingProvider(
from: Advanced.EvolutionDemo.V2.name,
to: Advanced.EvolutionDemo.V3.name,
entityMappings: [
.transformEntity(
sourceEntity: "Creature",
destinationEntity: "Creature",
transformer: { (source, createDestination) in
let destination = createDestination()
destination["dnaCode"] = source["dnaCode"]
destination["numberOfLimbs"] = source["numberOfFlippers"]
destination["hasVertebrae"] = source["hasVertebrae"]
destination["hasHead"] = source["hasHead"]
destination["hasTail"] = source["hasTail"]
destination["hasWings"] = Bool.random()
destination["habitat"] = Advanced.EvolutionDemo.V3.Creature.Habitat.allCases.randomElement()!.rawValue
}
)
]
)
}
}
}

View File

@@ -0,0 +1,33 @@
//
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
import CoreStore
// MARK: - Advanced.EvolutionDemo.V3
extension Advanced.EvolutionDemo.V3 {
// MARK: - Advanced.EvolutionDemo.V3.FromV4
enum FromV4 {
// MARK: Internal
static var mapping: CustomSchemaMappingProvider {
return CustomSchemaMappingProvider(
from: Advanced.EvolutionDemo.V4.name,
to: Advanced.EvolutionDemo.V3.name,
entityMappings: [
.transformEntity(
sourceEntity: "Creature",
destinationEntity: "Creature",
transformer: CustomSchemaMappingProvider.CustomMapping.inferredTransformation(_:_:)
)
]
)
}
}
}

View File

@@ -18,6 +18,6 @@ extension Advanced.EvolutionDemo {
// MARK: Internal
static let name: ModelVersion = "Advanced.Evolution.V3"
static let name: ModelVersion = "Advanced.EvolutionDemo.V3"
}
}

View File

@@ -60,6 +60,31 @@ extension Advanced.EvolutionDemo.V4 {
// MARK: Advanced.EvolutionDemo.CreatureType
static func dataSource(in dataStack: DataStack) -> Advanced.EvolutionDemo.CreaturesDataSource {
return .init(
listPublisher: dataStack.publishList(
From<Advanced.EvolutionDemo.V4.Creature>()
.orderBy(.descending(\.$dnaCode))
),
dataStack: dataStack
)
}
static func count(in transaction: BaseDataTransaction) throws -> Int {
return try transaction.fetchCount(
From<Advanced.EvolutionDemo.V4.Creature>()
)
}
static func create(in transaction: BaseDataTransaction) -> Advanced.EvolutionDemo.V4.Creature {
return transaction.create(
Into<Advanced.EvolutionDemo.V4.Creature>()
)
}
func mutate(in transaction: BaseDataTransaction) {

View File

@@ -0,0 +1,44 @@
//
// Demo
// Copyright © 2020 John Rommel Estropia, Inc. All rights reserved.
import CoreStore
// MARK: - Advanced.EvolutionDemo.V4
extension Advanced.EvolutionDemo.V4 {
// MARK: - Advanced.EvolutionDemo.V4.FromV3
enum FromV3 {
// MARK: Internal
static var mapping: CustomSchemaMappingProvider {
return CustomSchemaMappingProvider(
from: Advanced.EvolutionDemo.V3.name,
to: Advanced.EvolutionDemo.V4.name,
entityMappings: [
.transformEntity(
sourceEntity: "Creature",
destinationEntity: "Creature",
transformer: { (source, createDestination) in
let destination = createDestination()
destination.enumerateAttributes { (destinationAttribute, sourceAttribute) in
if let sourceAttribute = sourceAttribute {
destination[destinationAttribute] = source[sourceAttribute]
}
}
destination["isWarmBlooded"] = Bool.random()
}
)
]
)
}
}
}

View File

@@ -18,6 +18,6 @@ extension Advanced.EvolutionDemo {
// MARK: Internal
static let name: ModelVersion = "Advanced.Evolution.V4"
static let name: ModelVersion = "Advanced.EvolutionDemo.V4"
}
}

View File

@@ -9,7 +9,7 @@ extension Advanced {
// MARK: - Advanced.EvolutionDemo
/**
Sample execution of progressive migrations. This demo also supports backwards migration.
Sample execution of progressive migrations. This demo also shows how to do two-way migration chains (upgrades+downgrades), while allowing the app to use any version of the model.
*/
enum EvolutionDemo {}
}

View File

@@ -33,10 +33,6 @@ import CoreData
All CoreStore's utilities are designed around `DynamicObject` instances. `NSManagedObject` and `CoreStoreObject` instances all conform to `DynamicObject`.
*/
public protocol DynamicObject: AnyObject {
/**
The object ID for this instance
*/
typealias ObjectID = NSManagedObjectID
/**
Used internally by CoreStore. Do not call directly.
@@ -72,6 +68,11 @@ public protocol DynamicObject: AnyObject {
extension DynamicObject {
// MARK: Internal
/**
The object ID for this instance
*/
public typealias ObjectID = NSManagedObjectID
internal func runtimeType() -> Self.Type {