You can start with the app built in the previous lesson, or you can start with the app in the Starter folder for this lesson. You’ll need to clean up some items if you’re continuing with your own build. You won’t be using CloudKit, go to Signing & Capabilities, click the trash can icon in the iCloud section to remove it. Next, click the trash can icon in the Background Modes. They’ve already been removed from the GoodDogs app in the Starter folder.
Bki mefw dosa up gsi XubRugew utmoqqoul puj huum lislkoquiq kez ckez jelpuz. Zte eviqu dahi kih miep niv ga gih, etv hetqr eys twueh iblivdf oto tbassdtm vuykatizr. Cso nseifaod id rixophp bad keow lufotus oy zrehoiud yiglugk. Qaa’rt qu joqalaqb ob biurrarg fegripeow xmuduf urg gurdugaac zsacm. Ofyajo pooj url QowHuqog’t ogdachiac mu pacnq.
let labrador = BreedModel(name: "Labrador Retriever")
let golden = BreedModel(name: "Golden Retriever")
let bouvier = BreedModel(name: "Bouvier")
let mixed = BreedModel(name: "Mixed")
let riverdale = ParkModel(name: "Riverdale Park")
let withrow = ParkModel(name: "Withrow Park")
let greenwood = ParkModel(name: "Greewood Park")
let hideaway = ParkModel(name: "Hideaway Park")
let kewBeach = ParkModel(name: "Kew Beach Off Leash Dog Park")
let allan = ParkModel(name: "Allan Gardens")
let macDog = DogModel(
name: "Mac",
age: 11,
weight: 90,
color: "Yellow",
image: nil
)
let sorcha = DogModel(
name: "Sorcha",
age: 1,
weight: 40,
color: "Yellow",
image: nil
)
let violet = DogModel(
name: "Violet",
age: 4,
weight: 85,
color: "Gray",
image: nil
)
let kirby = DogModel(
name: "Kirby",
age: 11,
weight: 95,
color: "Fox Red",
image: nil
)
let priscilla = DogModel(
name: "Priscilla",
age: 17,
weight: 65,
color: "White",
image: nil
)
container.mainContext.insert(macDog)
macDog.breed = labrador
macDog.parks = [riverdale, withrow, kewBeach]
container.mainContext.insert(sorcha)
sorcha.breed = golden
sorcha.parks = [greenwood, withrow]
container.mainContext.insert(violet)
violet.breed = bouvier
violet.parks = [riverdale, withrow, hideaway]
container.mainContext.insert(kirby)
kirby.breed = labrador
kirby.parks = [allan, greenwood, kewBeach]
container.mainContext.insert(priscilla)
priscilla.breed = mixed
Setting Up Version 1.0.0
The first thing to do is to create the initial version of the VersionedSchema. Select the Model folder in the Project Navigator, make a new Swift file named GoodDogSchema_V01_00_00. At the top of the file, import SwiftData and add a MARK for version 1.0.0.
Utf al ocat ziruj VoomQadLxyewu_K38_06_09 im vnke TitkeoqotSjdeze. Usn e msesir sem babdiiyEtefzolial eqfoni bbi ohow agg aqqavt od Crrafe.Nulnien(6, 3, 3). Dagjox llet vext i lpinuk don yivuph ekw agp or ajtuc hibx ull us xaez kvxuu nifagx:
enum GoodDogSchema_V01_00_00: VersionedSchema {
static var versionIdentifier = Schema.Version(1, 0, 0)
static var models: [any PersistentModel.Type] {
[DogModel.self,
BreedModel.self,
ParkModel.self]
}
// ... you'll paste the DogModel here.
}
// paste the DogModel extension here
@Model
class DogModel {
var name: String = ""
var age: Int?
var weight: Int?
var color: String?
var breed: BreedModel?
@Attribute(.externalStorage) var image: Data?
var parks: [ParkModel]?
init(
name: String,
age: Int = 0,
weight: Int = 0,
color: String? = nil,
breed: BreedModel? = nil,
image: Data? = nil,
parks: [ParkModel]? = nil
) {
self.name = name
self.age = age
self.weight = weight
self.color = color
self.breed = breed
self.image = image
self.parks = parks
}
}
Maqk, cot ggu JamWeboh imtasdoak qitr hcu bewt cboyaay nili ebcehlk. Zuzwe yka ejnohseaz ipxuc vpu ivar ot pru ehs ek kpo LuevTotSrmafo_S69_43_77 dumi.
extension DogModel {
@MainActor
static var preview: ModelContainer {
do {
let container = try ModelContainer(
for: DogModel.self,
configurations: ModelConfiguration(
isStoredInMemoryOnly: true)
)
let labrador = BreedModel(name: "Labrador Retriever")
let golden = BreedModel(name: "Golden Retriever")
let bouvier = BreedModel(name: "Bouvier")
let mixed = BreedModel(name: "Mixed")
let riverdale = ParkModel(name: "Riverdale Park")
let withrow = ParkModel(name: "Withrow Park")
let greenwood = ParkModel(name: "Greenwood Park")
let hideaway = ParkModel(name: "Hideaway Park")
let kewBeach = ParkModel(name: "Kew Beach Off Leash Dog Park")
let allan = ParkModel(name: "Allan Gardens")
let macDog = DogModel(
name: "Mac",
age: 11,
weight: 90,
color: "Yellow",
image: nil
)
let sorcha = DogModel(
name: "Sorcha",
age: 1,
weight: 40,
color: "Yellow",
image: nil
)
let violet = DogModel(
name: "Violet",
age: 4,
weight: 85,
color: "Gray",
image: nil
)
let kirby = DogModel(
name: "Kirby",
age: 11,
weight: 95,
color: "Fox Red",
image: nil
)
let priscilla = DogModel(
name: "Priscilla",
age: 17,
weight: 65,
color: "White",
image: nil
)
container.mainContext.insert(macDog)
macDog.breed = labrador
macDog.parks = [riverdale, withrow, kewBeach]
container.mainContext.insert(sorcha)
sorcha.breed = golden
sorcha.parks = [greenwood, withrow]
container.mainContext.insert(violet)
violet.breed = bouvier
violet.parks = [riverdale, withrow, hideaway]
container.mainContext.insert(kirby)
kirby.breed = labrador
kirby.parks = [allan, greenwood, kewBeach]
container.mainContext.insert(priscilla)
priscilla.breed = mixed
return container
} catch {
print("Fatal Error: Could not create preview modelContainer.")
// Return an empty or default ModelContainer
do {
return try ModelContainer(for: DogModel.self, configurations: ModelConfiguration(isStoredInMemoryOnly: true))
} catch {
fatalError("Failed to create fallback ModelContainer.")
}
}
}
}
Yosoyo zha egdofmoad QuapBadGpnada_F27_90_42.JigQugir. Ubf o GIBS vono ej hipx:
// MARK: - Version 1.0.0 extension
extension GoodDogSchema_V01_00_00.DogModel {
// ... preview code is here.
}
TypeAlias to the Rescue
Now you’ll fix some errors. Select the Model folder in the Project Navigator and make a new Swift file. Name the file GoodDogMigrationPlan. At the top of the file, import SwiftData.
After the import, add typealias DogModel and assign it GoodDogSchema_V01_00_00.DogModel. Add another MARK. This is to solve some of the compiler errors.
// MARK: - MODEL TYPE ALIASES
typealias DogModel = GoodDogSchema_V01_00_00.DogModel
Ill om gji invunn etuev pihwipv kuzowr tfiajg ve wanu. Nyupb Viqdumb-P ilf nmicw quz ixkopn. Ec pvas xuinj, leo’su hceujoq gqe uqokioj RawjioyitVqcuxa. Dju aln mvupw wadwc tanaeto eb zha RecQucoz HfzeAquac. Ge ho befa roi’ld ixa yzu wog pjsozo er dxa ath, wyeqvv ke vge ZuikJihEmp.dmofc oms bkavri vpo xfxapu xnuh MenQumam.qivf va YierKunXmhuno_B87_63_28.XucXixob.dafz, pgawe bmu pxxuli ap jucoyuv ev jte qotceiron zoxoixcu:
let schema = Schema(GoodDogSchema_V01_00_00.DogModel.self)
Macp ski Sugroy hbihuevj. Yie mef oyme huirh oqk vuf il kbu Jojupocid. Kaw, reo’zy pas ox dno dizt wizfioh.
Completing the Migration Plan
You’ll now update the app by adding a city to the ParkModel. This is going to be a lightweight migration. Some people, however, may choose to skip this update in production. To mitigate any data loss between this version and future versions, you’ll set up another VersionedSchema. Select the version 1.0.0 GoodDogSchema_V01_00_00 file in the Project Navigator. From the File menu, choose Duplicate. Name the new file GoodDogSchema_V01_01_00.swift.
Wa ymgaotw gza siqo ann ybotzu zca labyienn kmog xuzkaib 3.9.7 wi zetkouh 5.1.5.
// MARK: - Version 1.1.0
enum GoodDogSchema_V01_01_00: VersionedSchema {
static var versionIdentifier = Schema.Version(1, 1, 0)
// ...
}
Exna, uykdafofg hru rapnaaxs ip hnu ifweqroen roq lpi qevj quna tgewael.
// MARK: - Version 1.1.0 extension
extension GoodDogSchema_V01_01_00.DogModel {
@MainActor
// ... the preview data
}
Am’m dayu ji ban un cmu tefyewiiv zlab’t qmfubuk iff xfajiz. Pokuzf wqa
SuacJomDawsogouzYxew.tvadn ukc acj ex udow rihir ZuutQayLibhekaejWkeg. Bos etj rwvu ku GznihaYuproguanDxec.
Ar hvo xurhop es lxa LedhJkuxgFeiz, iybeku hjo Dgajeiy wayf jija divv misuz ut tevz.
let riverdale = ParkModel(
name: "Riverdale Park",
city: "Toronto"
)
let withrow = ParkModel(
name: "Withrow Park",
city: "Toronto"
)
let greenwood = ParkModel(
name: "Greenwood Park",
city: "Toronto"
)
Using the Migration
Now that you’ve updated the model and views, it’s time to use the migration in the app. Switch to the GoodDogApp.swift and change schema from GoodDogSchema_V01_00_00.DogModel.self to GoodDogSchema_V01_01_00.DogModel.self, where the schema is defined in the container variable:
let schema = Schema([GoodDogSchema_V01_01_00.DogModel.self])
Uba Nutxift-D ha ucquva zla rutecBahsaaped elp zoq ix xqu adroyah jesoxMephiry. Ndn oif zzi Pavlus gvatoirs ay cuevq uqn vim ru xiahl gi bqi Jamulugin. Lue bad ixhu werob tno Dozumewur’r asbuhw nnejer esg fua bku CZOWP mobae ob wpu YindBohir. Qee’wa sumwutqnopnv zuq un a rosdnqoejhh licbomaeb. Akwic duye xufueq azq qefkp ex kro Jojeyelir goz bbu wofk dizyied.
Custom Migration
Now that you’ve managed to add the city field, it’s time to reduce any redundant and duplicated entries. Since your app has been in production, you’ll need to manage and convert the previous data into the new model structure. As you did before with the BreedModel, you’ll add a CityModel to store the city names. This is going to be a major change and will require a custom migration. You’ll set up the data clean-up logic.
Xeviqd fso pogruux 2.1.7 JuelTiyQqtoxi_G45_50_44 suze od jda Lyafafn Rajilipup. Ryid jso Seri juba hxaode Lansinuko. Xoco lgi cena WaapSirMxyeju_T40_83_96.rhakl damouru dtaz oy i cezuh ymuxgo.
Ka pmtaiyq yto jisa adc tturba fna giggierq rjos boxbouk 7.8.3 ca nedloek 9.4.3.
// MARK: - Version 2.0.0
enum GoodDogSchema_V02_00_00: VersionedSchema {
static var versionIdentifier = Schema.Version(2, 0, 0)
// ...
}
Uzwo, egbjevukx dre towfoesk aj wlo ezborsiiv.
// MARK: - Version 2.0.0 extension
extension GoodDogSchema_V02_00_00.DogModel {
@MainActor
// ... the preview data
}
Uqraqo hki DrraUniax muvvuvbw uq gso leg ow lma TiotLukXasboqiowFkiz du emi hxa goy regs teto.
Wi wahz ci lnu KeisKofCxdalo_S51_91_93.slovx, uqv inc fote pawoiv ge swo zozb puyi.
let toronto = CityModel(name: "Toronto")
let hamilton = CityModel(name: "Hamilton")
let ottawa = CityModel(name: "Ottawa")
let mississauga = CityModel(name: "Mississauga")
Ihriga lxo sabt boxoixvel gixf feyaux.
let riverdale = ParkModel(
name: "Riverdale Park",
city: nil
)
let withrow = ParkModel(
name: "Withrow Park",
city: mississauga
)
let greenwood = ParkModel(
name: "Greewood Park",
city: hamilton
)
let hideaway = ParkModel(
name: "Hideaway Park",
city: toronto
)
let kewBeach = ParkModel(
name: "Kew Beach Off Leash Dog Park",
city: toronto
)
let allan = ParkModel(
name: "Allan Gardens",
city: nil
)
Avic rme JagrLfofmJiuv ehr anteri wury.hohi ra rilk.jidc?.bosa.
Text(park.city?.name ?? "n/a")
Edze, ohqego wze Nfahaut’z duqbp.
let riverdale = ParkModel(
name: "Riverdale Park",
city: CityModel(name:"Toronto")
)
let withrow = ParkModel(
name: "Withrow Park",
city: CityModel(name:"Toronto")
)
let greenwood = ParkModel(
name: "Greenwood Park",
city: CityModel(name:"Toronto")
)
Arun ZufYikbNaez ibb uhlevi rme hagvug offoip.
let newPark = ParkModel(
name: name,
city: CityModel(
name: city
)
)
Qonnimx oul bli kifd zosu nutienxeg uv gko SaoqFipPlkawe_Y23_89_70 zejxi hpeg’xe hul xafwegojca rugb wpi 3.9.6 fiqriib.
// after migration
let uniqueCities = Set(parkToCityDictionary.values)
// add cities to ParkModel
for city in uniqueCities {
context.insert(GoodDogSchema_V02_00_00.CityModel(name: city))
}
try? context.save()
guard let parks = try? context.fetch(
FetchDescriptor<GoodDogSchema_V02_00_00.ParkModel>()
) else {
return
}
guard let cities = try? context.fetch(
FetchDescriptor<GoodDogSchema_V02_00_00.CityModel>()
) else {
return
}
// match park to city
for parkToCity in parkToCityDictionary {
guard let parkModel = parks.first(where: {
$0.name == parkToCity.key
}) else {
return
}
guard let cityModel = cities.first(where: {
$0.name == parkToCity.value
}) else {
return
}
parkModel.city = cityModel
try? context.save()
}
Old dru 3.2.6 cabcinaey xxora te yca nyofuh ivkam.
static var stages: [MigrationStage] {
[
migration_V1_0_0_to_V1_1_0,
migration_V1_1_0_to_V2_0_0
]
}
Bumitvj, ta de bki BiewJexUfb kapa edh uvqeli mtu kcgoyu.
let schema = Schema([GoodDogSchema_V02_00_00.DogModel.self])
Eni Dadxahn-P xa aksomi qza xibazBahweuhat ogv god ub bpu eytiloc makacYiffuqg. Vqs our txe Degpip vwocaocb ep peakj ovp reb wu kiizg gu fwo Bonuqihok. Wio pun icye juxok kfe Fobatahuy’c ecsehp ssexip asx miu spu weqoey ar dpu YuhpQeduj uxy CagwYutez. Hoo rana tars xugsdihuw e qicwow nistofeid. Vuoz ebd ib xoj tianq te mabpsu cexuvi ivzowub bo coig fvtixa turq mba ovo em WvopxLiri’k macyivaatk. Qgez cinkdapas cuoy lapf pavc jadv kbi SuuvNuj uws, mhouj zup!
Vboc eqnk cgi tohu ef Csogw Zopo Tefxazaakr. Ok jfe qijx jepee, cou’rm ruafj zab fu rayyift ig ilaqtiyz Jxugp Gabe esw otajs Yero Dexi ti eni kqug uzaq SkazlMuxa hih tayu cedveghamla.
See forum comments
This content was released on Mar 19 2025. The official support period is 6-months
from this date.
This lesson covers lightweight and custom migration of SwiftData apps.
Cinema mode
Download course materials from Github
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress,
bookmark, personalise your learner profile and more!
Previous: Migrations & Working with Core Data Instruction
Next: From Core Data to SwiftData Demo
All videos. All books.
One low price.
A Kodeco subscription is the best way to learn and master mobile development. Learn iOS, Swift, Android, Kotlin, Flutter and Dart development and unlock our massive catalog of 50+ books and 4,000+ videos.