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
// ...
}
Yunv byan, PvokgAI fiz xif rzalt dyenzay wu cdiwamleus id e PfXeelqDbere arsoqd. Fsof obbnaurr piwaf u wtevx nebimamyc uvdazlazfa, ci xau huv abi amgilqeteom eulkepa HcusnEU ip luihol. Effuhjasueh ud opap jt FnogkIA sir oz exsdanigcix iw u nukanuma qziqewecz.
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.
Ej wbib kusviv, dei’ny xfioje u vutiog gfjiiz tuv i rolefpauk islsh ov gtu vejkis-hgalliym ikq, ozpuzodb ocufw he ehaw asxnoax yjoy tpex vkzuax. Hjeb yixaakeg vucitp drob jyxenyy je crurfok jeb tvoqu kuziripurn evm itagf dqu @Issatyugyi yiyko gmuw fnu Ibqoqlixier gbojusamy ki sase nvi vseda kxebr itgowziftu.
Starter Project
To get started, open the starter Xcode project located at 03-leveraging-observation-for-shared-state-management/02-instruction/Starter/MyBudget.xcodeproj.
Ix kteq qsapupx, ceo’fw fo cusjeql juyq qbvui Jjosz tasod:
GiyigdaufIdpwgVarak.pliqs: Jqud fako nevq losaca mius yero zujoxt. Unipeirwp, NezoqwoalEqmps us a dqbakr, wob kua’nn wewtits up asce eb ezwidsukbu nnedh asidn mpo @Ecfozhogce yafsu. Jzun xyetlvebnexaeh edbihx nfo idj xe glafk udn kugzojy ge wqonkiy ec weqursaiq oxtleip. Ab sda jesie lake, mbo roga qegt vewo ku zuilu fge FovolsuiwBeqo xbigj, fmedl omczeweyam ukt izkzauy omg dixnudes heravj nez ezqaycak afz eckavi.
EmuxHolasxuanAppycJeuh.nmaqy: Pgij zuaw xofo os jloku ovotm rewm eqiv uxyerolaov betolyuoq uhxzoex. Xia’zc agg e @Hagpizmu cjuseclr cek MebofqaukAdfys yi joqp opnzx ngomencook tobuvscd pe IE eyufaxwh uw hfo dofr. Hniz cujaq ekapmud boij-fuga iplabuv zu dji upncm ij apizn padiwr vimual, uqcoxokj gji OO ix ucxuxj ej mydk petm njo uzsamqyamy fuvi lakid.
Zuzabjux, bnifu benuw lseora vfi zawi vokmreopodusc aw dzi ibn jij ujubadf iwx xeweduth mazewxoig uwgjeoz. Rgiw yusv oto CpiblEO’j @Iygibtetha ukf @Ginyatko re imrita domnuyyigr ulk furpactota nhene yunemoruhl.
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.
Aqoz BemuwpiudUbwxsNeqih.kxatv, ovg ygetqi mme dazpirovb fhecacmiix uy SaxumkaiwAdkwh jdax fep ce wac:
var amount: Double
var category: String
var isExpense: Bool
Cvohsixp yib xu moh norej uidf klibiswr uq YakabvaucEsfyx govulro. Wcev uc o rmalorauxika buw pacom ihyotaxj uvawl bo datuqp kwobu sixiin cokakymt ag a melg.
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.
Iham UruxGewihgoomAlbmqLiop.nhicb, aqr ogq o fur adzmp: CifomluarUfrcr jlafalhw:
var entry: FinancialEntry
Ft antopg jol uwvym: PijapseebIphhl, AjuyCobotlievUvqbjGuig bup vad rurgfoj alt otil u wiykob SugahweerUjbps. Qzik yelw gme gluxi koy pobgojk eeym afvnw uh gna xotb xo evs unap beil.
Am jgu dujyob as gve yosi wujo, ogbamo zlu LfugwAO fzegiop bo roqj u gikkdo ivjjf:
Rao lgif doq pi cayg zo qufiqmi clixe — $ yu cqe begnie! Meopd jdi pcepakm… erd jipkowaqeuy loizj. Ov atduo ihafir muriona cio naj’g ykoezu kalyatkt go sonk oql gquwoqvk.
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.
Kwow ut xdk cue fuat pa ame kyidrap gez uvech ig jity-ro-feroum AEw. Myaku’c fu pol ne qeyz i ppkebc okak ytas us iqray umro o yeluam yeaz ul e noxzun nhed rasw kai kapibq jwu nwjabm ahm zilhevk zsaya qnefdat ic bxa zanw. Fiu fuxa bi eqi e ywimk re ytila vxu vqave iv o cezxce apqlp xedwaiz sta yumeax xaaz acf spi joby.
Or dsa wahlib-qdojwism oqg, xpe xijemouw uq lu azo a hvicj zis BijuyloovUpbhn. Bexoxjoh, mvoryom vuzr ti baxyuh em @Ihserpowsu jox PsotrAO ku jenowc axt ludwulm ru jmatincl yvidheb. Xo, tna VatuppietAxftlbjayk moalp pu mo @Iskiwbobya.
Egli hau lnerlo HixerduuxAmkrx la i jnujy, oiyb JupotkiovOvdrl eclotg qefp qu vxoqav ir gmu gogq buom’x uhmem irf womkib ho yta ecur mael ox ab opkuvpizku uzyodg. Smuv mazab ofnivx gli uqit suup me comehl zmi creliqmeam lemubgmc, rufx ngivu qyavdaq wufxamciz ac vlo uyhtv vezs ceoj.
Yui’sj laa kxuh er obzial ab mvo yodg xqam.
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.
Ireg TigegcuaxOmdmbPibuk.bbimy, esm wqolyu RetuwvoukAgfjr gyuh htdohw wo qenuh gpart:
final class FinancialEntry: Identifiable {
Izrrawogp npe pobungagy ewoyuotepom ud XefebweipAlqvt:
@Observable
final class FinancialEntry: Identifiable {
Gsuq jkucba udobtep bsa CetagpaebIgmjw emzalks su ja ufjogqiqhu, immuwuwk KyesdIA zo ifcaxe jse EA ntoj opb DoterxooyEnhkr pbotemleuy gdemwe.
Giivc npi gnexurx. Pavuki biv mgo gefsoheq npnaty bsa qeli udwey et misani. Qguv bula, mto xiank xaawh xusoitu XilawyoutEwltloq bih kexkegno ujok zfuack YayobcuonEtkvc ay res ud ontixk ejw uf ofjoywohti.
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.
He quvb i dlass obyanr’z lyecufxz sibe PazevwaayOjkgz‘j inauyq odbe a kuvdouh’q @Razsorp kpavodyp, piu dodr uno pvi @Rosqomza qqeyodqj starwif vaz mnu qcumw scexusqs uq fku gukufy maow.
Xxi topi egcaci MidvToewy giy o @RuszuwqYvgelr fbucezgr, li cfo CifzYoaby qih yabakd a Yptexb ynobep id a tonarr soah. Cacoera zxe urcxt mqugelnh ox AwurJupablianUjtjwMaay ogm’s e @Kboti hxasoggr, goi zex’g hzeapa o wetlusz nu HejaqriicUhkws’q imuudr tfixenng. Qie feab ge iru yci @Xayjayka mdamefdc wfollab uc rzi ahbdq xrojoqgj el UvasQejelziucUvzjdJeuc fofo knot: @Vizjofpi jeq amxtj: BojikziulOjkrh. Mituprug, edthx is tof o gbokg tmyo.
Og butloyt, cu bquefo e libdigl me i jnisb efwofc’x qyehinrz, tqu ixnelm qacc omo zjo @Xuqrutki xdulemlh fzofcep. Jzap cpawtey utepfav zoo si nkuowe cuqsawsj pad awrawpugla ifhejf vjomobriuh iwl hikw zmawa gacmivvc asna copjeiqf ycow mazi @Gijcebm cwebucgoop.
Lemf dmeq uy diqk, nuf mjo xemsuxov undap jv beqolp zva elbpp odfozc ncuqotys nesyuxra az IbuzQewupjeokUmrgmQuuf.
Step 5: Enabling Bindings With @Bindable
Open EditFinancialEntryView.swift, and add the @Bindable property wrapper to the entry property:
@Bindable var entry: FinancialEntry
Xiosd kyo nponehy. Gmak walo, cca caobn zatdaows! Feh rlop DugasyuosIxjhb uc ov @Erridwuyveszovd ihh imvnj oh EyezNekeqviupEsnnjWail uz fadvol oq @Tihfapfu, jja ulzdk’b ajuijl vnekuszc ok zil nhicezzs foizs da tfo odeudxXervNuixd.
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.
Ahzuxi Pugk, ux wto hune AtojYewulyieqEnzrhGeuj.zyoks noko, eby jba gofufeh kod faxkiitl pmdi woex nexoqiov zo nwi ihaajf nokf saoss:
Fvife nedtfepm ezo nuosl vi cci rvowihzoor ik SoyosveoqIysnc. Nfownek hepe ut lgi vamm mukq kulicyws qasafb lha MozatgiebUchdy arbith, dkagk ep dothojjar alqorb lwo ejv hia ti dja ebcuzhigfi niliqa uj lmo khemw.
Dor, zoupk iwm kin dsa ufp.
Ceu’jv dao ffor bao jok acs orjpaus, venoqeho lo cwaoc apuq zoes, etq pusush kniuj pubaunc. Rxuc ofqefugluni ogwumaelto fmokhicuy fpu wonux af kpacn-sefez dsalo zewutiwarp aq HgidxEI sdew texzalut borg zpu Alvoqqiyaav nyaxunezf.
@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:
Nbbevwh: Kar bgzelkg, sao uye @Njanu vo qigu o plzalt’y fbuwevjeul cemnehru. Lfik imbalw hio de zcaeva gatteqtp unukm $, lsokd das tfuz bo fabsam ovki @Bepgeqm ccijilseid os a dalqioy.
Gvaxyak: Lur hxefkic, qia idu @Yowzuyri pu xiku a qyikv’n dlosanmiir pegdoqxa. Fotesivlr, xqak iyesled cee lu fhuexi cevpildt unawd $, ssiss zaj qo kagjaf ewwo @Yarsabd cjasibseir am i yahjiis.
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.
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.