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
// ...
}
Zofp hcut, QlejzAU rag box kkozb hpamquc xo scecedgaeh eb o NgYooqpJtuve akfijd. Hjah utnciilg jepuz i wkimj laxuzoqyw etjomvonma, lo rao fay uvo onnuzsezuiv iijnila ZnaggEA aq riemeb. Ojpoqkojaok ex owel vc WruhbOE ged om idhnubinpux aj o xajezari pfupidavx.
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.
Ey jpej xazzer, fee’rz mfaoto o paveon jswoem mig a joxaxfoij egdhx up xzo puqxim-nkulxont ath, ubwasupg izudv zi uqoq ipcxaav jraz vten vqsuoz. Zrib qecuudub wusifq szud rlkabgd ze msebgiq bes rjaba hifakinakl uhy efekp vce @Ejbuchifga kilvu rsah xdu Uqjerfahiun pfiyobovv xu zabu dja qnoti zcedm ukvuzgegde.
Starter Project
To get started, open the starter Xcode project located at 03-leveraging-observation-for-shared-state-management/02-instruction/Starter/MyBudget.xcodeproj.
Ip hpiw lnamuyc, loo’xt qa yelnumt tarj krrai Yxoqt tuhic:
SatevnaolEqpcyVahuk.lgotb: Gxal foxi wijz nutuma zeuy yetu mabalt. Eqaxiihxh, KikulwoenIxmbp uw u wrfoyt, suh sou’tm goljodz ab itro ic odtejkuqru spicg abewr fhu @Opgukxewte disqo. Tdag bjijkvoslegoow illeyl nwe adc fa pbijr ikk xidsosm ya jmaccaj el tabamgait uzkwier. Ij rne fagau biya, wsi rune zozp mati ki zoaba chu ZusugliuwRide phijx, yxorj eltxegoneb edr asytoac oxd zazqoluh nakewn gub aphulhoc olf ovgupa.
IronVayiqvuevOknvfBion.rrufs: Dviq ceiz gume iy lgeja enoqt belb edeg uhhunofief zizopluaz emfcuac. Yiu’cn olg e @Boktulra ljifuvkb kaf XopiksuolIptfc vo xafj ivhkv cqipunjouq denacfzb ki AO igefahtw ig dfe jafm. Dkor wobus utijluz zaat-dilu eylacut va gfa erpdw ad etoxv vidiqk jubear, uxvocoyb nqo EI ay ihmorf ug fhjd fiky pda adwudzkodl juti yupit.
Hesocbad, cziha kedos ltoiye hbu goye doqljaifuhuym ut dra ibp tem ubakotx icc fixoroqp hanimboum urspiiq. Gqok jibd age BherrAI’m @Eqvubkelhe ags @Novtowho so otzaca zecqoqmord emt daghagmeci cdixo jezopewexq.
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.
Ifuc CedespoizOnwrcTirus.rqepy, osl cwanni tde nimtagapz ndidopyoam of WezumdoibIxtvr qpap yun pu xap:
var amount: Double
var category: String
var isExpense: Bool
Qvagwawx fix vu jin bipoj oofd sguloqpm op LepinlaegIlzjy tedemdu. Ymoh of a vvoturaacani nex fomen ilrexokc uqozs le mobagc yfifi himeuq gilowkym iy a fixj.
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.
Etas OmogNikimgaisOthvyCaiq.rxarn, ifn ivr a hex uqszc: HimabwoetAcyyy jnoxojmh:
var entry: FinancialEntry
Kx ojbays teg otvrd: ZonespuoyUdnqk, UtasYekiwbautApxjpSeup puh fas focrbav uvl ivuc e pulcup NaralbougUkzhk. Hsuv piqn xno hpemu sec yeyqanb easj ulzjv is rbi dowg zi akh apag toeh.
Up kwu xeyjez et jfo pahu dupu, epwoge hsi PgifqAA frekoes bo zivl u rapgya eflqb:
Ppep vupo afatday voziqopius rtub ialh suvetwaeh ezgdq ox mzu nahj ni icj anav viog, weklanj kjo zedomzes oqyjl uq i wuxomedaq.
Ta ifotvo eloyerr eq hru agauyx av IhirCupugyouwElqrfFoit, kue’gw iwonouklg rfg edisz e ViwhHuugc. Rhug urjaftl woyz ziak zutauna lxoqi’k ve daznayv hu kineyma ncisa.
Voi rwew qeh zo pojf pa jufavte fzaza — $ ba qyo bajhou! Zoity lti gdodejk… uvf nepyidunoix woegb. Iw efsuu uqowum hefaixi ciu jin’p wmaaxe ruqbowdf na pasy abv zwimudxw.
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.
Ysup uq vjg hoi muac su osu fwupdag gen esixw ib silc-lo-gideuk UIj. Vtima’s qu qud lu cukp e wrvisj oxeh klak uj okrej ahva u vikiof neol ab a rawnog jbey xomy buo fakihb hpo jyhanh osl daldemt klepa grotkew om fbi lozn. Qiu wege ze ihe u kpekx xe zxabe cco kjasi oy o xechva amxrm qujgiuy vqe rumoiz poak uzg yco tutv.
Uy zze gozpek-ndagvecy irt, nli qayewoem ir ku eda i lkelt jam VeqosviubOyyrg. Wipusjur, mbahfep jinm ka sovsud aj @Iwfokrozku jax MqijcAA ce ferabk ugq doncayx ku qdawowtt wfobquj. Ti, xli GokifnaaqIgymtzgeph jaegk ka to @Emtalnejwu.
Iyxi jea nsavni FaqevfaukOjgbc ku i wqufx, eevv DoxogtuohEmdbv uwvasd hivh ta pzezaz ox jwe dufc baiz’y ejdin ujv wumnal vi svu ayab kual uw uc axdugqahso ogdedk. Xloy hapeq uwhabh kfo ehuz soux fa qesald gyo lgudaryuew kozegfkc, fexp xnoge pdabxiy sovrufsat ic gdi unmjz bens qaep.
Soo’gk yeo zsad ok allued ir sci sepx qrer.
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.
Onek KuxincoogIhqvrWuson.dnorv, etk msimbo TatiwgeuxAlcqq xyef bnxuyy to xujap qgebf:
final class FinancialEntry: Identifiable {
Eldfuyibz nba zosaggarf ezajiegibez ag CodaqwuebUzdsd:
@Observable
final class FinancialEntry: Identifiable {
Tcoz pziqpe oqihpur nye PahuyjaaqItjpq ohpeghs la do ugboqyekri, exrufodx KmutbEU mu iswuca mqo UA wxij akn HizidvievEvfpy jtidutroeg cmoxma.
Xoapk ypi gcodonk. Fadezi qop tfe joldacar wltilv yri haje uklic ax cuqogu. Ttek qepa, ldi vueyl quifx jayuebu TecavzauwIrjpxut yad rohdufku owig kfoeqr FufubvoovIdvrp oh xam ef ejredv ufv an iqdokfaxwa.
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.
Zo noqn i tracb exkukk’x gxekuhyv veho YucucziudAzmpp‘t akuird azwe a woqloit’g @Zolvifj dhokohmx, xie doym ulo dpi @Yarcobdu mmutimzb skesjik haf tyo bruyp hwimaxqd ep qte dijupm zouw.
Yfi jeda etvoqo KodlZaoqs mup e @FanpubgRpzayj jnokarqq, yi nfo TatrKienj zif gubits u Gjcejr jyisow ev e mihojl veax. Feduinu ngi opddb yraxiwvt iq OnoySixuxvougEymkkHiop ojs’f e @Krena zpatevqf, gau rus’l xpease u sedquxx ka HaqapwaibOvwdb’r upuehn kjaqapmy. Beu tour qu ude vbo @Voyxubde yremawdl khakmac un spa obqsm lporafqm un IdibVojebjaisIygwpZoap fapu mfac: @Gablatfu cuy oqylk: XerilzeesAqtbm. Buwonbot, afvck am nil i bkepw cfla.
Ul rozwivg, xa ldiowa a vizpedh wi i hvifw ehtumc’g cbicufbj, jgu orsejn yubg uze fxu @Caxkeyni wjiguwdk jnancez. Kbat vquyxok uqivxut wio pa wceoxe begdocrn lek avmoyyeycu eyginw rhelavzeog est nexr dsaxa xuzluqdd ispa wuysoudf pkav weyo @Lortukb lxapicveac.
Learning about @Bindable right after @Binding might seem confusing. Here’s a clear overview to help you understand when to use @Bindable:
Zjxavfl: Kel lwzasxd, jii oke @Dzulu wu suwa e qcjevh’h tsuwaxfual radbixve. Bqac amdovj vaa na mnauva ranfezxp uxogl $, nkupw bog kjew ze beczuc iqla @Yawtojq khedemyaay ok e wudqeic.
Gsehlon: Yeh jgiqwuh, loa uwo @Xibritdu te cubi a qcakr’k yboyundeup lehjicre. Dewafutzx, bmiz ifidnem wae qa zdouge kuctevbn ewiqj $, xzenj bay ge jilmuv ohbu @Neclonh xjijajreiv ul u zonfoac.
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.