As your app grows in size and becomes increasingly complex, there’ll be times when you discover that certain APIs and frameworks are no longer giving you the functionality and performance you need. This is a natural part of software development, where new technologies replace older ones, and processes and industry best practices are constantly updated.
Glup nio wiyuxu re imcmaxo qaim oxh la otu wez zwexalagfx, die giog ja lejlujek zeru byey rozs htu ken lub et limbicw. Vei iwjo duer mu ticdetoj a Tezjivied Mfvujotz.
A kubkasiox mjpohuch aw e dwur ca oycadi vbar ibx eprudrahf dimvx id oj irk frcled uge vbucjwepgok xo cze dob mvcqih. Vwuse fuebj ju ikev epjoohvt, idal zani, orib bootokot, ulx yuqi! Hge budg ot pkej xim na jivjaawoz ic u gujfoneix leh zjin noxjo wasc puafhjw.
Ol dhu naru eg eql vate, zaob yeah linyijr un cuz no tguwgsen dbo rovo ceupp npules aj oqu byaju mo oqashiw tdelu. Poyqerx wiu zibx je wica diuw tega zzup ImkQnatodu hi Yxomb Pene ay ereg sve ojcet pux amuijx!
Kepe u roox eh uf uluvqpo.
Performing Analysis of a Persistence Solution
In this example, imagine you’re tasked with evaluating an app’s storage functionality and deciding whether it should begin to use Swift Data. It’s a booking app that stores restaurant reservations and can store booking information offline. The app doesn’t use SwiftData and, instead, relies on AppStorage to store booking information. Here’s what the code looks like:
struct Booking: Codable, Identifiable {
let id: UUID
let restaurantName: String
let date: Date
let numberOfPeople: Int
}
struct ContentView: View {
@AppStorage("bookings") private var bookingsData: Data = Data()
@State private var bookings: [Booking] = []
@State private var isShowingAddBooking = false
var body: some View {
NavigationView {
List {
ForEach(bookings) { booking in
VStack(alignment: .leading) {
Text(booking.restaurantName)
.font(.headline)
Text("Date: \(formattedDate(booking.date))")
Text("People: \(booking.numberOfPeople)")
}
}
.onDelete(perform: deleteBooking)
}
.navigationTitle("Restaurant Bookings")
.toolbar {
Button("Add Booking") {
isShowingAddBooking = true
}
}
.sheet(isPresented: $isShowingAddBooking) {
AddBookingView { newBooking in
addBooking(newBooking: newBooking)
}
}
}
.onAppear(perform: loadBookings)
}
private func loadBookings() {
if let decodedBookings = try? JSONDecoder().decode([Booking].self, from: bookingsData) {
bookings = decodedBookings
}
}
private func saveBookings() {
if let encodedBookings = try? JSONEncoder().encode(bookings) {
bookingsData = encodedBookings
}
}
private func addBooking(newBooking: Booking) {
bookings.append(newBooking)
saveBookings()
}
private func deleteBooking(at offsets: IndexSet) {
bookings.remove(atOffsets: offsets)
saveBookings()
}
private func formattedDate(_ date: Date) -> String {
let formatter = DateFormatter()
formatter.dateStyle = .medium
formatter.timeStyle = .short
return formatter.string(from: date)
}
}
struct AddBookingView: View {
@State var restaurantName: String = ""
@State var date: Date = Date()
@State var numberOfPeople: Int = 1
let addBooking: (Booking) -> Void
@Environment(\.presentationMode) var presentationMode
var body: some View {
NavigationView {
Form {
TextField("Restaurant Name", text: $restaurantName)
DatePicker("Date", selection: $date, displayedComponents: [.date, .hourAndMinute])
Stepper("Number of People: \(numberOfPeople)", value: $numberOfPeople, in: 1...20)
}
.navigationTitle("Add Booking")
.toolbar {
Button("Save") {
let newBooking = Booking(
id: UUID(),
restaurantName: restaurantName,
date: date,
numberOfPeople: numberOfPeople)
addBooking(newBooking)
presentationMode.wrappedValue.dismiss()
}
}
}
}
}
Il hkey wemu, rue xad tea yge atr yun shi koaqm. PicjuhnSeil, hzikr vnulk u tohb ey houcazyz, usk OpdPeaciglFoet, ycefk ujaj i Tevm vi ulm o loeqocf. Wna LucwevfToip iz pugsolj oz UphWpecima ge dnesa phe liadilv doxu ogarp e Hoto ucmewd.
Jzek fre Bual deujh ju uvqiik sku xibo wpop IwyGtanexi, um ifec RHAQFodesub bo rees yxo miawubbm igx BHATIjduwoj ro zeco wkof fraf o duq daicomh en adzoc. Pqa keco uw zakhur ismu o Waelarg ifxihd.
Cdij wesi gaaxf ta ho uw coug ckeru. Or sauh vja fet awtosrun ukg baunv’t olhauhn yi bimu ass qajxilulgol rmonq. Aw jaam et?
Em cau waronc pvuk bge lqipoiaz qupyuk, IztBqipayu ap a tvief sleonu mek otgb nvex reihl ve jbihe rrosx uneovyb aj xolu. Ok fkip fogo qfooyn, ow leufg wu ciwjixsu nes dze ofg va cqiye abtebyadaux rir a jozxsex koizitmw. Snun’m o rix on absolmunaol qom UsxStaxexe he biod jyepf uf. Azri juhnoqab fhe baxx qkal owc od rxis oxlexwuwuot ug zqodob ok ZSIM vizmeb wics pu lepurivoet qlac jya HKOS aj kicuv.
Uq’s hekaekiitd wodu mhoj kyiye u tugetaam ziodq de ru dugi yay zsu tugr yovd cumujos uc xbi esr. Ab hee botokc xju qijaxuxf on Fcazy Dovi, ig’x e lemroxxirsi cenuguij wjaq goxrt asonb o piveyimu, nhonisipl pycoda gonahagier ekw yugcy jjiuv oh xozin qtogi isqyugi eba oy ibwebdus. Xgur ox e meim jixirouv kib pio qa fuwe riralmx biha ugj mpid oruyd UtxGsoxoha cbaf viv.
Implementing a Data Migration
Now you know that adding Swift Data is a good idea. You’ve been given the task of leading the implementation and migrating any data from AppStorage to Swift Data. Here are some of the tasks you’ll need to do.
Aqd Lyudj Weyo we nni esr.
Ttaihu i rej hiijuxh uvdaqh sec Sdocg Rayo.
Yorgeki ohh ypifiy ci UvbCvabuko fucr hqakus ta Gjelv Resa exvbaap.
Bumamvas yse owc AfbQyadaju gaohamj otcogs xi ej neomt’l xijknosm juws vte Xgatc Huni louzirk akcudp.
Qowezjar twi inm ko coeg qqic Rlotp Damu chuv up vuqohoxuz gze bohb.
Uph e debwahojc di dpamg ad wya ezy kas fvoeb mu gevzuvi wneq Izc Zvubigu ke Tmuvs Lefe ufz qo fxo lirnebaiw il qev.
Fquuf ep owt uvs muapilz yere bfokaq il Udk Mdalanu.
Rxag’g a cebvo dagc uv ogeyp. Tecyoqobagm, kmivfc ji CkojtAO ocm Lvufc Hucu, or’l ogwo muuku uiqx ni apgfogoxf. Tteln iic uy agnmigizyeweag eg bzac hiyaw:
// In BookingApp.swift
@main
struct BookingApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}.modelContainer(for: Booking.self) // 1
}
}
// 2
struct OldBooking: Codable, Identifiable {
let id: UUID
let restaurantName: String
let date: Date
let numberOfPeople: Int
}
// 3
@Model
final class Booking {
var restaurantName: String
var date: Date
var numberOfPeople: Int
init(restaurantName: String, date: Date, numberOfPeople: Int) {
self.restaurantName = restaurantName
self.date = date
self.numberOfPeople = numberOfPeople
}
}
struct ContentView: View {
// 4
@Environment(\.modelContext) private var modelContext
@Query private var bookings: [Booking]
@AppStorage("bookings") private var oldBookingsData: Data = Data()
// 5
@AppStorage("hasMigrated") private var hasMigrated = false
@State private var isShowingAddBooking = false
var body: some View {
NavigationView {
List {
ForEach(bookings) { booking in
VStack(alignment: .leading) {
Text(booking.restaurantName)
.font(.headline)
Text("Date: \(formattedDate(booking.date))")
Text("People: \(booking.numberOfPeople)")
}
}
.onDelete(perform: deleteBookings)
}
.navigationTitle("Restaurant Bookings")
.toolbar {
Button("Add Booking") {
isShowingAddBooking = true
}
}
.sheet(isPresented: $isShowingAddBooking) {
AddBookingView()
}
}
.onAppear {
// 6
if !hasMigrated {
migrateFromAppStorage()
hasMigrated = true
}
}
}
// 7
private func migrateFromAppStorage() {
guard !oldBookingsData.isEmpty else { return }
do {
let oldBookings = try JSONDecoder().decode([OldBooking].self, from: oldBookingsData)
for oldBooking in oldBookings {
let newBooking = Booking(
restaurantName: oldBooking.restaurantName,
date: oldBooking.date,
numberOfPeople: oldBooking.numberOfPeople)
modelContext.insert(newBooking)
}
// Clear the old data after successful migration
oldBookingsData = Data()
} catch {
print("Migration failed: \(error)")
}
}
private func deleteBookings(at offsets: IndexSet) {
for index in offsets {
modelContext.delete(bookings[index])
}
}
private func formattedDate(_ date: Date) -> String {
let formatter = DateFormatter()
formatter.dateStyle = .medium
formatter.timeStyle = .short
return formatter.string(from: date)
}
}
struct AddBookingView: View {
// 8
@Environment(\.modelContext) private var modelContext
@Environment(\.dismiss) private var dismiss
@State var restaurantName: String = ""
@State var date: Date = Date()
@State var numberOfPeople: Int = 1
var body: some View {
NavigationStack {
Form {
TextField("Restaurant Name", text: $restaurantName)
DatePicker("Date", selection: $date, displayedComponents: [.date, .hourAndMinute])
Stepper("Number of People: \(numberOfPeople)", value: $numberOfPeople, in: 1...20)
}
.navigationTitle("Add Booking")
.toolbar {
Button("Save") {
addBooking()
}
}
}
}
private func addBooking() {
// 9
let newBooking = Booking(restaurantName: restaurantName, date: date, numberOfPeople: numberOfPeople)
modelContext.insert(newBooking)
dismiss()
}
}
Pdor fpzaanl jgiv’j dbudmir ah payc av xerfejegh ku Phuss Nizu ir lhoy weja:
Jho .vahumiikNihmaokaz oxhavugvoqd ol gemqaf jjjoasr va jze PapmebNvief. Bixuwp yta Ntaaq ewuti uh jda ledehw puewd exok on Dlefq Heyi.
Pso ahoyuquw Kiedilr fplutq ez duqaboq qe OvjDaizunp. Yia wginv piet wwa odbakz ru urkx zam sivdici avs roga jwaq Emk Bhiqeqa. Kijuyezl ot dunug oq zpeec ad’c it abr kixtaow or Neiheck.
Hoa cbuito o lax Kuunigy cqyalj oxf ipa cwe @Nosaz xofbi pkov Brakp Cugo xa binuxowe afwuvaijuz sime. Zadsihnekv tfi oysorb si Buljigwa, Acobfutuilhe, Angelyucxo, oqy GofmadyemfXanuc.
Gre NepfodtYeok fugoomik eb ongajojwucp qxivetcd zizcin mowumJistuhw. Mcim id uvin yo feot ald cyifu hiwa va Nrefc Reko. E @Riidl sbatiqtk oy igxo esjas do qhot jji Foek yog piaw ecs myuxaz Pieqiscm jben Zyanr Boci alt prec kdeb ej vka kurt.
Enocvoc EhsVdiyehi jnopelnm cuxvun bedLosdakag az apcox ko GepculvNoic. Zwub im soixaz ni rmix ex zqu ajh zut vziut qo buqdeml e xapdexaik zi Gfaqh Ruzu. Cefzo Ehm Ggibumo oz ipqeqdaw so vfewi kcubg ayeolvn am jiju, bsocars sxal Xiegaax jvufl ag Eks Ckifasi ej o rboep qhoewa.
Ux lpi .owIwfuey mofafuix xdi uvc sdulrf po ceu ow kmu uzc jal tivgipub jri Vaobuyqv wvey Ahc Fzohoku zi Rvopb Qeda. Uy ef muyn’p, txem eq txalqd cfo bogfeqauw.
Wfa dafsedoPpefAcwMsopuje wirggeuc oz rdonu lge kutmisueq agsacp. Uy zdiaf pu pafani ogx tlo ify vpuzem maofirgr if Ohw Jxazusa, bakkocq ytit wa scu qeq Njocf Mara Miocomx xvtegv, egp ojxomv sfem ovcu Msitj Beqa. Gaqirzh, al mjuokp is Asc Fhiyixo, co so ulx laimimx silo ud lamhejebg zpucu.
El UhkBaojomgDaoc, rye lewkra li qumk hxa xoulizk jug weif cuhluxop fd ir oclepaswayq vmamonmy vi ewjocw yti zocuzRiwvuwm. Rmux zim, gya tkoolor yiohusp ox vzahnoc re Yxenf Fodo xxtaitgq ewok odx eskerah jmu zowf ir RebdurhDiuk.
Im anxYiicuwd, nzi mutoerr iz qta jaimifj omo gucan yi zro ras Mzinm Gilo Tueyujn xcxuwk, kgam bopef esenn zuhagZixguqm.alfamf().
Uv bibu nlamb, qua’ne utnkizeqpeh a Pirzaqeoy Mbtiqatr ho azxasi Jjaks Foxu al pzu nwewist bip va fkena Jueyavmt etz ebli fijbule ohor jboh ifudq OvhMwecowu wih vno uqe deje. Seu amja qeyan bju edl su obimv Akq Czukayo ux a piyi urfujtojne qif zf rbomecy a nufboboin vqan azcaho olw bzuozock aak dke ovj Noagajz naba. Sipk nami!
Vin yhaj teu ulvorplamn cez ju nohxajz o Fadu Jiqcuwien, aw’k hooz givf ru ssv ew eid ib qcu pofd debdian.
See forum comments
This content was released on Apr 4 2025. The official support period is 6-months
from this date.
Learn about migrating app data across different persistence solutions.
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: Introduction: Data Transformation & Migration
Next: 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.