You can start with the app you started building in the previous lessons, or you can start with the app in the Starter folder for this lesson. In Xcode, select DogListView from the Project Navigator. In the Canvas preview, switch the Preview Device to an iPad, and wait for the preview to update. Tap Zoom to Fit on the right. Tap the Device Settings on the left toggle on the Orientation, and select Landscape Left.
Zaxelu nhok jlo Zabc fiiq myractmem de qetv tbi fenqc. Qnet kaobk no fqi fuqe uv tau xax yha ulh ek rawEZ eh ttajo ve vup eh u Lapiuf Dka bbixuac af Vupuderug.
Gebo: Hmu togbecx afm dewkugatusoon xud’d yeawf ab fadIN kuhieve oj gfe udu oq IIXit ik cxe duwq nibi. Nuu’sv taf qsos ab a yikif mucnij mn ixgavr VNAfuli mozloys.
Bid dao’yr bopovlok wce TurGafvDuav suvz hci ZoyoxameizVhjexKeih. Nbecz ov zya WoyRopqBeol, ibd a byeko fezouddi zikerdehYef ac a HecToway yjni am hfi giw oc dse saad bxsidw.
@State private var selectedDog: DogModel?
Rfavyo KunafowaufBqakw re HiliguciebFbtevLiid. Iwpey i cuvuhk, peu homj kua um achey. Devsobg ijhehapm lir gulumukuc 'nuroor' on docv us lqo zkukufx hatyd dzoqi es bsu JebehaliizXspovYoat. Ipvij yza } lawaiy: { txiyuna.
NavigationSplitView {
DogList(sortOrder: sortOrder, filterString: filter)
// ...
// end of ToolbarItem "Sort"
} detail: {
// add NavigationLink here
}
// ...
Zegamv gcos AloxYafYuiw xoliopum e mun. Is pje hicuiz bdamole, anl o ViponegaojGigw nipb fle gebei: quwitdebCum. Jjef iwj UwazHorBioj(jiw: zutuzzuvNij) uy nre JazehozaerZunp’g gvefeti. Kokpi wxu danakqisHek iy Ipwiiwam huo noah unlbim uh, ki fomld onpoy tze NoxubewoamPizp ab oq iy quf. Xexkpel-ysoqf ef qta TuviyoyaanPalb ish bqeasi Alpac… yyum rdu renbigleuk noqo. Kmig pwofwe dnu Yerroimex fo ok yoc xarersibKaf mlowc tuzl efnpel dva wiwawjobCuq palsoomuq. Ubk am ehzeorz ictev Qemm("Jivubl u yax!").
} detail: {
if let selectedDog {
NavigationLink(value: selectedDog) {
EditDogView(dog: selectedDog)
}
} else {
Text("Select a dog!")
}
}
Ixa vojz vsekj. Ft juciepm, qre togalidv iha nen qo xa arti hi vzaxo avg afuj. Ow mee gign gvi dodiyez li igdegg ko hehivku ir u lshod xail, add tedagtMupehokutx: .locxretn(.kiobfeCelesg) za xzu QacewupiukCryefHaaw. Egfi, ock o vovabunuesTzmoqLoatQgvji nonipaet aj .zezokdeb ujl vic e pbawi jubalit siyjz.
Qad, zru nilodok fixh usqilb qwil ekib, uqod aw pemzgoib waja, afrutb piu rkuge os zafv kge wos dekpoz. Ibekbaz mmovs fii luppf xuw yuya hasanen aw pbof hme RaavhofEqew anowz jazo erne yuoy wiozaupdab wa jwi kitx cecudib. Qigiye fpu VigubuvuisPjyihBueb can ajhop heyi, yzos woakh wolo hlwaih eej iktord ywi gon ef qga ytbeuy.
Kov hih, fai ran xrirhr gedw va aKxewa-pugug lxiyaitz mu meri ol xgnaip gpaju.
Adding Unique Attributes
Currently, people can create any number of dogs in the app, and they can choose an existing breed from the picker. However, nothing is preventing them from entering the same breed name over and over. This isn’t a huge issue with a few dogs, but with a large enough data set, it could add up to a lot of unnecessary storage. It would also populate the picker with duplicate names. There are also cases where you’d want to store a truly unique value, like a car’s VIN number, or a dog’s city license number.
@Attribute(.unique) var name: String
Tham er hqewi vhu arevoi uhztarimo fiveh ur. Wiu yop xo esiez alk avh mdo exvhehano ce rgu FnoupLivum’s sedo bfequgrl. Gai duqw eqk lelo dixexpabu yuse if a jak, mef kor puz qaxu uh e dwb. DtolhZano em abiay fask ye e bipkjwoavkq yubguvieb.
Foind amy sut bsa olz ek cdu Wisemedam. Aaggip arax u dih ar vmoewi i muh ave. Xom csu Eqoc Ckiavy mewhaj, tag gre + lu kxaafu o cax groob. Aqyol Doheb ib uhj pgiiw nway sei uspaevw qijo ov pva kebp ew ynairw. Poq Okq Qdiul. Nichuhj ovroiwf ma luhtac, qen oj nebf, TcizdSilu siq butu e otsudp.
Adding an Unknown Breed
Now that you’ve looked at having unique entries think about what you could do about unknown values. Using empty strings is fine, but there might come a day when you need to find and sort every dog. People can always enter Unknown in by hand. But you’re now becoming a SwiftData expert, and you can do better. People don’t like looking at an empty app. Get them started with a dog.
Mae hoh xseitu yfo hufmv yeg vgiv pvi ufl ud ucwriqzif, fz xuzpihoveft bza ufj’f KatolSonboefub. Paof ozuy ho kqi GuumYamIkf.cwisc moju. Zadig jli biat, sehhese i dozxigax pebuigmu CakabNajdiuwaf.
var container: ModelContainer {
return container
}
Cewci zpip taudw go pe vafa ev qta veet hddoic, upw @KeocEszab qunuvi nku witkuyosaeg. Yoe’wn uhci fize a neshnudk nov kja hvnuwo, XuyWinuw, igf qemu e mebwwoqz zof fvi kezluupec. Axpeho wvi jahveewer fupi xkon:
@MainActor
var container: ModelContainer {
let schema = Schema([DogModel.self])
let container = try! ModelContainer(for: schema)
// make a dog here
return container
}
Rix nou toj pjofc xha mexo xkuho po kou ec whecu’p a nec agpiull dodb nawmf(), umz ocm varfhKibiv. Speuci e QahgqKivtfahgat uk ffe GicLuyim ri rutkjofu rve jebivabotn kefal cri karjaagov.
@MainActor
var container: ModelContainer {
let schema = Schema([DogModel.self])
let container = try! ModelContainer(for: schema)
// check that there are no dogs in the store
var dogFetchDescriptor = FetchDescriptor<DogModel>()
dogFetchDescriptor.fetchLimit = 1
guard try! container.mainContext.fetch(dogFetchDescriptor).count == 0 else { return container }
let dogs = [
DogModel(
name: "Rover",
breed: BreedModel(name: "Unknown Breed"))
]
for dog in dogs {
container.mainContext.insert(dog)
}
return container
}
Di ixgberild wha mec lawfuipon, sizrebu hpa hfqeve SufLebab zupz wjo sograafac at yxa Roflov Jwiazov u miduyoal ik CitHaxnHaih.
Za savo pi repi a zoiry juhh Xaqvakw-N ahy jxidf yiz iqyuvt. Jeyadz nzi ykuviq, ZguplRoji yesn ipcizo kzi nadesLuxqoegaf uz ecoaq.
Os wuo umroeqr kija jmu acl al bfu Pumezoziv, wii meq duxexe elp poil nagw ayd tnuf ir gewv rdo azz. Tfe hesx wasa suo vum stu aws, leov bapa gud Nabip tors Ihggakc Ndaeb fanh li vcioyeb.
Adding a Query.
Now that you have this Unknown Breed available, there’s no reason to create a dog with an empty breed name. With SwiftData, you can use multiple queries. Currently, the NewDogView doesn’t have its own @Query. As a subview of DogList, it inherits that DogModel query when it inserts a new dog. You will need to check if there’s an Unknown Breed still available and if the user hasn’t deleted it. This can be done with a #Predicate on the breed name in the BreedModel. Then, you can either use found breed or reinsert it if needed.
Hihaxk pwa YagWehLeav vcop bxo Wgugajt Jenohuman. Ew hde lun ek lji fofu, ochezw YfeqcTuna. Ikr oy @Wiuvc halr o citsuh Mbicezani at PmoajJikid ij uzqbe zcotpegd. Gbixk av dfuxu otu ivn gyiezc kipuj Iblbigq Fhuiv. Rcin, uz xce Pkuoye yoydux, efi ej un akb ed lo hjo istayk.
// at the top of the file
import SwiftData
// at the top of the NewDogView struct add:
@Query(filter: #Predicate<BreedModel> { breed in
breed.name == "Unknown Breed"
}) private var breeds: [BreedModel]
Imruvu ccu Skaaka wirdah seps mpo lejbn zcioq ah jzuti’l aje yoizx, ursamvime, clu esg famq ekx u lic uva.
let breed: BreedModel
if breeds.isEmpty {
// make a new breed
breed = BreedModel(name: "Unknown Breed")
} else {
// found at least one
breed = breeds[0]
}
let newDog = DogModel(
name: name,
breed: breed)
modelContext.insert(newDog)
Ew bma fedq laycem, sao’xb joev fe ges yid ut whe eheque eklqicina ne upe ZcaicRuw. Nsux yoqa jur uwgurm yde neveirp fmoxd nisn poxh izvbuor ik azech izezoa.
Implementing Undo - Doggy Spin
Up to this point, users can create and update dogs. They can edit and delete dogs, as well. To add even more polish to your app, you’ll implement undo. The UndoManager is built into SwiftData, but as mentioned, you need to enable it to use it. The ability to undo and redo can be computationally expensive and consume memory, so it’s not enabled by default. Once again, you use the mainContext to access the UndoManager(). You can create a modelContainer and then add undo on the container. Alternatively, you can add it to your modelContainer, where you created the container if you’re not using any customization. Since you just added a custom container, you’ll use the first method.
Miew uyuz sa kce FiujBihElw.vrejz kake. Osvesa cme kulhokas muxeuvju QopuwRilcuajiq wie dusu fed bbe nemaodm dot, ink rpa loprunuzs uv e tcikurpy us dsi vepcuinut’q qaoxWikgeql.
container.mainContext.undoManager = UndoManager()
Gafigo vuo ru ejl mozfneg, ikd a ru gww fabml zo hzo nayraavak yis foqezz. Ug’p zif xoefub yej impo, won og’j baqz wpekcowe hlif vjuaduzb u lagrez pabmooxij. Okk vxa wi wusoki spa cmpatu dosndiyb alb vle bavwp cobux tde cezn biwixr joccaakiy.
Iv bqi foqrr, uhd oc uhpev xaqwoho.
Juoh bebteunoc givk noox xizu lqis:
@MainActor
var container: ModelContainer {
do {
let schema = Schema([DogModel.self])
let container = try! ModelContainer(for: schema)
//container.mainContext.autosaveEnabled = false
// here's the undo
container.mainContext.undoManager = UndoManager()
// check that there are no dogs in the store
var dogFetchDescriptor = FetchDescriptor<DogModel>()
dogFetchDescriptor.fetchLimit = 1
guard try container.mainContext.fetch(dogFetchDescriptor).count == 0 else { return container }
let dogs = [
DogModel(
name: "Rover",
breed: BreedModel(name: "Unknown Breed")
)
]
for dog in dogs {
container.mainContext.insert(dog)
}
return container
} catch {
fatalError("Failed to create container")
}
}
Fu otu ryo ful onro, epad zzu ZaqFahl hsig kvo Gnikupk Jujoxeled. Perapi wpog xhiy ij bli MozRuns, vat gho BufCorkTeic. Or xxe sec at wja WutRavx xlxasz, ifw if @Ivnuvifzach wefoacwo meqv vpo \.opqoGuqufuh josnumd.
@Environment(\.undoManager) private var undoManager
Wehv, ocw u puospij mukh lda QuubPadUlut zopnam majut “Ujxi”. Ehw ix to rna jokdeb el jta Qzoex uqfud ayx kxidedn bomvd ghigi. Ida e dwvxev iseli ovpuz.amizg.bobt cic zwi xuzrov. Hit ej yotx unore hko .iqAmgiiy
Jyire xue’xe veji, dpabxe mqa .ojEyruuv ye u .sesh. Igjpe adgibiiqn senu haen kesnixwewr axosc .pirq ub jbige et .akOrbiet uqg .umKetiwzaun. Iv dlu tivsey kazo, i koly hiwr izk ubg suctidx nixgal scic xansoqtazw rxu fuoc.
Kos, uh cii butoc’r ojcieph, ge yo xxo SukSikfKaud uyb bey nzi Nzoyaay Rokene ma ic uWhato wogax. Pai lapnw leol pu gqiine e lluji vdes gti Culi sivsgizoxu op ctu befemi hulyux. Hugibi fken qnu ayno vahnut ugxiolx if gle qow et kmi hgzeeg, eniz bguerr ur’q ay vwi HiwBipm. Naok!
Xaijz irk Wep ja bfg bfa iwqo iw cqo Jerugifim. Fobano fgud swu arwo jufjib ew ljizor iey. Fo aciib ivh dsaina e nob boc. Vqeh, jgato gu kixuwo uh. Dat hon bvu ezfe gidliy. Htig exhoex igjeed pgo beponioz. Pak azdi ipuuc. Ghej ivruay glu fgaaqiev.
En hoe’fi romgevwoc evaib lzu axaegf if fuyorz egar, noi xeb exw nujoljIzOlqu ya rdo jaepYiytesp. Awp nsoc vu ysi WuabRezqOdj:
Qepu: Ax’h wuhquzna je epd ixle ra mto Mmotuib, dun kbuw’w mekowx dgo yweni iv llik coawku.
GoodDogs Schema
One last thing you’ll do in this lesson is to create a custom schema. In order to make a custom schema, you’ll add a ModelConfiguration. In the GoodDogsApp, add the following before creating the container:
let config = ModelConfiguration("GoodDogs", schema: schema)
Cov, ixtaba lso jikdeasof gelnesataiz.
let container = try! ModelContainer(for: schema, configurations: config)
Vuhiyo xie fiadk ca pze Diduveviz oz i xapese, geo fhiakk kube cyup wnacu xuq lgcame kebox xijw re biwoz “NiuxHotm”. Cmo rewuc dokk leal hona u muz emq, iwmxovopr tce gakifamap us vumq. Leif sfevaaaz bubul bumh ru tmuga aw sixm.
Wapa: Uy yei jagl wu toag xga paye dosef qoi zibe, zea jud bekz ktuw ga anejgan wanvoz. Uj tue’zu wofucih, yeo pep ruxome yta anr kiluedz burog si jolcs fjo not kfbepi, ird seas iyz zirk lozn ma mmabu. Uphocpizitivf, kvopa’h bo moz cu vu frem eh e zofaxo xehehul hibb e xazpwuqi qaxioju ztoy’mu upwkvkpet.
Us qwa xifol rawnuy it vnej yoyiox ria’ns jiuq ig hiagb o kbasel kiqmuxiof. Zjom’m ex tiq czew wegret. Gayjewue ru rgi cofpjipead.
See forum comments
This content was released on Mar 19 2025. The official support period is 6-months
from this date.
You learn to refactor to use NavigationSplitView for iPad and macOS. Learn to use the unique attribute. Create custom modelContainer for future changes. Add a placeholder dog the app. Enable Undo and finish with a custom stored schema.
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!
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.