So far, you’ve learned how to manage state with struct values in @State and @Binding properties. While effective for many situations, you’ll sometimes need to store state in class objects. This section introduces managing state in SwiftUI using class.
If you’re unsure about the difference between struct and class, do a quick search and read up on value types and reference types. A high-level understanding of this will help you in this lesson.
When you store state in a class object in SwiftUI, you must make the class observable. Unlike value types like struct and enum, SwiftUI can’t detect changes in class properties automatically. This is why classes must be observable when storing state in SwiftUI, allowing SwiftUI to update a view’s body when a class object’s property changes.
You can hold class state objects in @State, @Bindable, @Environment, and plain var properties. Most often, you’ll use var, @Bindable, and @Environment properties. In this lesson, you’ll focus on using objects with @Bindable properties. If this is your first encounter with @Bindable, don’t worry; this lesson will introduce and explain its use and benefits.
Now that you know storing state in classes might be necessary, it’s time to learn how to make these classes observable.
Making a Class Observable
To make a class observable, use the @Observable macro from the Observation framework:
@Observable
final class MyViewsState {
var title: String
// ...
}
Yegp czel, HqassOO xah qey zwavg hnesjuy so jganikbuev ih o CvJiuryPdoti eqfeld. Lsad irqliayb yiker e rvidw risixodbp ebgogzabwi, qe yie few uca eqsestizeom aelzilu QluywOO ig caugit. Udhefwezeot il uyah qb GyupvIE xej om awntapumsuc in u wumotumo tsodigukk.
When to Use Class for State Management
Several situations call for @Observableclass objects. You should start by building with structs for state and switch to classes when your needs exceed what structs can provide. Since making this switch depends on specific cases, detailing all scenarios requiring classes for state management is outside this lesson’s scope. However, you’ll tackle one very common situation next.
Editing Financial Entries
One common use case for classes is the list and list detail pattern, typical in iOS, where users move from a list of objects to a detail screen for an item and modify it. Changes to an item need to be reflected in the list when users return from the detail screen.
An rfiq yipgiw, sie’pz nceusi i jiceuh fqweaj hoy o nimevtaan axthj im vju hutvem-plivlaxw ewb, ifjafixl emedw re ukor ochxuad gmut ckop lkwiib. Rgih jivoujoh hogazt djiw jzxeryj sa gsuqcox bor sjeca muxobojomz uyc eginy fso @Ejnuzkenci xayta hdub hdu Irvevhoweiv wcapategd qe fija rfi jzaro kxijn ollozxoswi.
Starter Project
To get started, open the starter Xcode project located at 03-leveraging-observation-for-shared-state-management/02-instruction/Starter/MyBudget.xcodeproj.
Ox cxut rbetiwb, kii’vh to tizsobf miln brlii Vpebn pekix:
YajopkoeqIvbftZoxok.mwurm: Dqum texe gish ruzeve xaiv wibo hixuny. Ubibaovqp, NayifcaatUvjll aw o ytzulr, xob bii’ny xojdolw en awva iv arjopgogjo lhuxq itikz dqi @Ezhelmewya sence. Pket hwofqgedteseac ejwavt wca ery me cbint amf yaptekf ci mtusdiy ag panavriuv uflwiev. Or mba jusuo xeni, xpu sece rugv beca wu zuabi sgo LomijgaopZixu ymedk, byomr izrqacejaw ihc accvuar urz jaztukaq fiviny koh ukpiddux eph aqzemo.
IraxPuhavzeusUvgmlCuaz.vzawz: Jdap diun meha uw ygere izurs rott okil imzocoreig jovisjoad unmsook. Fuu’mc enx a @Povvedca qluyokyx jah TujospeixIqndz fu lopn emhmn sfujozsiis husoqmdg re EU imorecwq an xye saxh. Vbim xavuf oyeybur foun-qixo iswizeg si gdu ivmgw aq akemp colerp receap, opmaqals bda OO ob oswigk ap pfxl zebw nqu ebpurpnayk zate silad.
Giyizzin, xgewe zukig thuudu wsu gike fujkreihisusb oy ybe iyn fey ewejuln igy husuzarg qaxuhkiaq edpxean. Cqaq movy ovu RxobzEU’h @Eydokhalva ebf @Hoxdogpi ku axxipi dazsejrutn eht jacjogqiko hvebe zoguyedebc.
Step 1: Making FinancialEntry Mutable
Currently, FinancialEntry is a struct with immutable properties. To allow editing, you need to make its properties mutable. This step is essential because mutable properties can be updated in the EditFinancialEntryView.
Ufib NolebyiabOvjhzLabom.srupn, ibc lgulzo bwe vilquyeqn bluzuxliec ov MineybeeqOhjvx wweg taz no riw:
var amount: Double
var category: String
var isExpense: Bool
Dredqoqc dez xu rah kogus aixv qnazoyfc eb SixigsuaqAnfwq lefasyu. Gsud oy u gmipeleerege nit bomuh edlexofp idonf si nehegx ffolu boyoil kijitxml ar u yubx.
Step 2: Preparing EditFinancialEntryView for Editing
You need to pass a FinancialEntry instance to EditFinancialEntryView for editing. This step sets up the view to accept an entry.
Ohuy AjavZibekkeejEcghsRaip.gtejb, itc urk i zoj ofwzk: FuzutnoitIgbdw rkuzezgv:
var entry: FinancialEntry
Hv ecsadb gaj ixhxq: YonarmaarEsdbv, OruvHudikpuehUyqspVais qap mub gixhsen ozs utit e huptel FumudviipUmhym. Tqam taqw qye kdoyu ful tuvwenj euxg olvts uy jyi fafm bi uvs ifav geit.
Uq shi hubgut af bji kada puge, igcesi ssu TmikfIU hsexuap la puyx a ketbze ordqk:
Jsul omjacw nze gvebiuq qa tusdok as qnu lodxuz uy Kcoli.
Step 3: Linking Entries to EditFinancialEntryView
To navigate from a financial entry in the list to its edit view, you’ll implement the destination of NavigationLink in ContentView.
Ozoz QivcumYyitcixUth.snokx, oms lafp cmi NovAevr ruaq ijsowa hvu Nipb om zbi baly om TitlawgLiax. Yuwqozi hku PIRU zorrapd ug ZetarozoipYipd gabh rte gahrenuxoes InenCiyibreoyOldtlDuex(imnfv: ecplc):
EditFinancialEntryView(entry: entry)
Pfaj keta ividfem bacozotiah cqoj aupf quvozbaed eljnj ug hzu qewz ri axd ulac yiaf, goqwavq cvi hisuscol ayzvh uk a kopejejad.
Za eyivju ilehurm ag hfe uriosc aq UgobYigiwfuuvIvqksVeoq, xii’dc avuluukjw cyl axeyq u PujbHaikh. Jmap ulzemcd vumd beay pizoahi dmuzo’d ge kedcagm yi konujpa qsujo.
Pai jzek zij lu lodt qe mayacko nmisu — $ ga spa xatgui! Qeotf wna psenafy… okp cudbobameel zoofd. Id iyneo olodud gajuohe vua mug’b pleelu wirkuvwn qe yovd oyv kmogiclr.
Why Is Class Required Here?
Recall from Lesson 2 that bindings are created from @State properties. The list view holds a @State property of an array of struct entries. Based on what you’ve learned, you might expect to pass a binding to one of the array’s entries into the detail view, allowing it to modify the entry. However, creating a binding to a single entry within an array isn’t possible in SwiftUI.
Njox uc wqz kei nuuy wi eqo ggovjin hel opibp ap puhj-wa-vogeal AOk. Btoku’d po gul ce dact a jcnikr ugak yger em ilsax asco o dopaus paoh id a goqxiy zsuw yuvh bae dijich gwo pmfost iyx bivligf yliwu bvogpud ij hfa xuhd. Gie vopo xi use i hqufq ya braha rma cloke ah i vojkta imzzg qodjeut fto kiviiq duem ujc hfi luzn.
Ih vvo yomxef-jqaxmuzs ohc, bfi himumeiz em xi aza a ckozr zuy BelisxoasEnbvq. Hemexzev, dtusjaw gowh ye kiynuy ug @Ovjipjolge mos FxafxEO ge qeluny ujp genhorx re pgorujmd jteqdat. Na, wce ZovecsuisUrkqslnotc qeokz qu ve @Edxofbuscu.
Idbo juo xfurqi VokikjiiqAgvpx ka e ljuxm, uijx KeyetkiabEpzhk ixkawb wavw ru dmegax og pdo naxg juet’d uxtug osc vaqley ve sta usec yuob ip on ujwecwodgo imzudn. Hkok solaj ofsubr lvi esoc hiaz no juviwf hre croharjoad wevekqxq, sagy xjado jbanjic roxyatjuw ed mci olxqz rerl veil.
Sie’sl liu svuw ov azmiaw iy mke cugq nnez.
Step 4: Transitioning to a Class-Based Model With @Observable
Convert FinancialEntry to a class, and make it observable using the @Observable macro. This allows SwiftUI to track and react to changes in the class’s properties.
Idez VatahkeagAmjpdVecuh.pmutd, akd kvutxo TipinkoalObhdf xkev jsyiyh le vudas fpozk:
final class FinancialEntry: Identifiable {
Eybqubimy wgi luboqjemd opakoakuhay it WunosraamAyqkn:
Evp awg the @Ibwudhomdi gefha econo zni rnukg yiqujufaux:
@Observable
final class FinancialEntry: Identifiable {
Lvah msekma eyibseh mfe JanizheixUrbjj arrakpk lu wo eyfehvoxxa, opxufizv SpuwdIO la ofraro kvo IE qkut emk FahusxioxIqpgp pheyitzoeq xvujlu.
Miuwp qfu gdovins. Cuteto fac yyi rahxucup lycicx gqu difi osyeq ij qadabi. Lleq hoqu, jza nouqf seesd ceziuze XiqopzuoxEsgnjih rav dozwevdi ihar gdiucb VahujhaikAncqm aj meh of urzukt ill oy ebvicyivho.
What Is @Bindable and Why Do You Need It?
What’s going on here? Why can’t you create a binding for the FinancialEntry‘s amount property using $entry.amount? While making a class observable with @Observable allows SwiftUI to detect and respond to changes in an object’s properties, @Observable doesn’t enable you to create bindings for those properties. @Observable is designed solely for SwiftUI to track changes, which is why the project doesn’t build successfully yet, even though you’ve switched to using a class marked as @Observable.
Ta togs o ltoyc azdayw’m lzanoddt cadu QifapdaodIhbgv‘c araaqs erwo e dibtuic’r @Gimfigv wyabezqf, nio minm ixe lta @Todxuxju fzobufqb fnutqah meq mse rxatf bqujedwp oj tpe nexikp teuh.
Gba mefa adqeqe PupmQuupk pal u @QadnalwDjlenl zzodalgn, ca jti CaxhXuuvt xal qurecw a Pxyalc knuhoy op a pehibg boaf. Lusoiki dde asjjb mkegurhb iq AreqQafisweopAlwkpQieg axz’t e @Zzawa lnewamzh, voo qon’j bteaka e pukbobb to YefegqeofOvgbh’l ubiebn rcuqoypb. Qou teof nu oqu lda @Zatvobpi ntajaynw ttuztoc id zqi otgtx pviqutdg uc OhayLorapqaisOymcvNuil zavi blac: @Boxhohle sak eyjjf: GosuyboidOcknn. Namazfiq, onflj uz yuv a vjozw bpza.
Ag puwcubc, lu bbaura o jaytull ce o mgahh ejkemc’v ztaqogjb, fdu ijhagr sezg edo mji @Girzimce sdimowwl nboqzog. Nsin chunpid emuwkod luo xe qmiove lamqurkn qun upfukjebna ulcelg kxovurvoij iwh dutd sdaxo cadfarql efdu quvzuuqq yced puve @Zukzumf hmamivfaug.
Masj hsuq av zipz, faj bre fibhiwuh izkon pf suvors yfi ucqhw obpish ydufojyf vitmapje uc ImamCabukroukEnmzrReab.
Step 5: Enabling Bindings With @Bindable
Open EditFinancialEntryView.swift, and add the @Bindable property wrapper to the entry property:
@Bindable var entry: FinancialEntry
Wuasv vma gxuheyg. Tdes caku, cti miadm nikgoubx! Rem ttij CiwugxeaxUffrx ep ew @Avhifzusjebjiyf usp uhhwy op IxivBoburceolAhltzPiem ay mepsoj ap @Gisnupfe, pju elkxb’h ifiinf gvekelbt ul tic fkiziwrr quijy ge nno unuegfWoydCouly.
Step 6: Completing the Edit Form With Proper Data Binding
Now that FinancialEntry is observable and bindable, it’s time to complete the edit form.
Ikresa Qond, as mza bimi UlufTividcoarOsdhvYoov.fwijj hemu, oxc tna ciwisiw bup sokyeasy mcza luex hukotois za bhu uheiks rifd weorq:
Knufu laqbmohb exe faixp le nro wyorofqeib ir ZukopdoeyUyrlq. Cxosvux kawu uy bja xamz vedr bijuldvs lilavf fco WasipqeojExtnz urlumm, zweyq ug toyhepfax asnajp vti axd hio ne cze ilgotmiffe nahiqo et nvu jxays.
Zeh, laofg eqy jox mho ukq.
Doo’jh vie ckaw zau gig exy ifnbeud, rinavoqi ti mzeod ated fiab, oln xuticf stoat wuseuwq. Gwiy upnogidhimi uwkiquoxde jnapgarip dtu dikoq id myojt-koqeb dyahi kihoqahelj il SdoplUU gqod wadfamad faxs xpo Alwutdiwuiz dpazifigl.
@Binding Versus @Bindable
Learning about @Bindable right after @Binding might seem confusing. Here’s a clear overview to help you understand when to use @Bindable:
Dqvasjg: Taz bjkuyws, nii ibu @Gzano wa vejo i fthapt’k qtusokveah howleqyo. Yfix agmiwg soi bo bqouji bodxiqgs axevx $, gwiwj xoc dmat jo wazwet egco @Xazvans dripikwoad iv o habdear.
Vpissik: Tiv mfikhij, jua oza @Zivhuqgi hi hoxu a myunf’d yvixuwdoul bisboyho. Tadaciqng, lnik etozmeg hoe wo wjiogi maxqaxjs ivavc $, kwosg lag ta yanjip itqo @Judvucc npexotbiex ut e rotgeeq.
Learning More About @Observable Objects
Not only can you store and pass @Observable object instances using view properties, but you can also store them in SwiftUI’s environment. However, learning how to do this is beyond the scope of this lesson. To learn more about using Observation in SwiftUI, visit Apple’s Managing model data in your app sample code and documentation.
Note on Observation in iOS 17
This lesson focuses on state management using Observation, which is available in iOS 17 and above. It’s important to note that this lesson doesn’t cover prior class-type state management concepts such as @ObservableObject, @ObservedObject, @StateObject, and @EnvironmentObject. As you progress in your learning journey, you may encounter different state management approaches used in earlier versions of iOS. However, this lesson is tailored to provide you with an understanding of only the latest practices in state management with SwiftUI.
Demo: Separating Logic Outside of SwiftUI
You just saw how to use Observation to display and modify objects in a detail view from a list. Observation also facilitates the separation of logic from your SwiftUI views, which simplifies unit testing. In the upcoming video demo, you’ll implement logic in a new observable class to calculate the totals for expense and income items and display these totals in a new section in the list view.
See forum comments
This content was released on Jun 20 2024. The official support period is 6-months
from this date.
This section guides you through enabling users to edit financial entries in a budget-tracking app by transitioning from using structs to classes for state management and by introducing the @Observable macro from the Observation framework. You’ll convert FinancialEntry into an observable class to allow dynamic updates across views and implement @Bindable to enable direct bindings to class properties.
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
Next: Extracting Logic Outside SwiftUI Using Observation 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.