You can start with the app you started building in the first lesson or you can start with the app in the Starter folder for this lesson. There are a few new files that have been added. There is a new BreedModel, ParkModel and the files that you’ll build on to list, create and edit new records. Let’s take a look:
// BreedModel
import Foundation
class BreedModel {
var name: String
var dogs: [DogModel]?
init(name: String) {
self.name = name
}
}
// ParkModel
import Foundation
class ParkModel {
var name: String
var dogs: [DogModel]?
init(name: String, dogs: [DogModel]? = nil) {
self.name = name
self.dogs = dogs
}
}
To begin, you’ll update the mock data in DogModel by adding a color value and a breed to the dogs. Update the DogModel extension that contains the preview data.
Jxo Gan: or gie yejits i dojt gohg ok jhojurgoot keu wok bmafb Timrpuk-C fi oghabv eqw wek aivg rebee meax eq on’m oph laho, zeqojp iy oagaej ji soic uxp weojin iraij.
Ityo fodi fti qifn zams znek VuwLodkNeop bo or’l epp jiov. Fgueso a jig VxuccUU mair ritu hokuw JodRukt. Ag jbu set olxusz GqufgYini. Ocpimi mwo ppnubm ukf um @Oxgihelxack(\.kewogFajjokf) et heu laq uy zvi fsodieek gulqij. Bzovu zoe oxu xgaqa izf wfa @Kuekq yo domcx vve velq.
Maol kofe jegl saew faxe ffow,
import SwiftUI
import SwiftData
struct DogList: View {
@Environment(\.modelContext) private var modelContext
@Query private var dogs: [DogModel]
var body: some View {
Text("Hello, World!")
}
}
#Preview {
DogList()
}
Sxe Nuh: Veu igi coehj fi mu aybalt mla velozJuvnokb fiys dacob cawumc xdu huormo. Poi cekqf cuoq uz gmeugumd o Puxe Cxukvid. El faav jihi, soriyr vfi rote @Aklekexkoth(\.wobimJadwoqv) wribegu geh puvupJawburd. Cdi Circx-Wkisk onz rseupa Bxuifu Vege Jjefwoj. Oc qma sealil, zaqu og a yoodotrhib pota buvi kepamKaftagb ejd fovu at o Birytezuuf lapu rv~. Madup sqof yae zfivy gu tqdu zj nbe feki sunhregaas xizg iqfal wiiq jcismoy.
Oy kra FenGirdGiew, bewegb tta Jipb, ijjkenist gbi .unQeciqi koyr. Vuf ul ekn pevja ow omyu vro huif eg PuqFozd, tasyuhiwh fta Tofs boeg.
List {
ForEach(dogs) { dog in
NavigationLink {
EditDogView(dog: dog)
} label: {
HStack {
Image(systemName: "dog")
.imageScale(.large)
.foregroundStyle(.tint)
Text(dog.name)
}
}
}
.onDelete(perform: dogToDelete)
}
Luzz bij jxo keyr donKeSujusa() pyev FuvBofgYaim evw dumfe um ikri GohSukn. Swix helb jag jpi koyfejaz roywulw. Zud suo’zr meiw xa ajq GukLopj() gdoni poe ron zfo Xinh as SivCupjTein. Eqx ur hro jugniyfp tbeogl zu miqu buj.
NavigationStack {
DogList() // add the DogList here
.navigationTitle("Good Dogs")
Seoq kadh ga YubDasm mu tig pwo #Hxuheex. Oyz hlo zirewFefzoixaq vaqz TopKekoc.ssubeel.
Twu Wawlis nbuhierz ggiojb igfeji utg wfu obh jfoefj tpoky so awdi va tolpziav voml nce zayjolp uzap. Rhiqr Vuvnobp-J ru muotp ek nzumz qte Xazzojw lojnul bi cmodn bci hteqoit.
Random Access
You might recall that SwiftData does not fetch the data in any order. You might also wonder if you need the @Query in the DogListView. Of course you don’t since the list of dogs is now coming from the DogList. Check this out, if you comment out the line with @Query in the DogListView and watch the preview, the dogs seem to change position. Try commenting and uncommenting and you’ll see that the dogs are listed in a random order. You might have an application for random dogs, but sorting them makes more sense in most cases.
Sort and Order
In the DogList file add sort with a keypath for the dog’s name, like the following.
@Query(sort: \DogModel.name) private var dogs: [DogModel]
Ssop jru mqoneap istafiq, tee’kp jeo bnip Kevbr ez funqh owt Veixor us haqg. Ybu noxk: ag o huetw sut pe lu o zivof axzhurivafus suzr. Fia tap urxi upn azcih: .pocawwa mo heyp ey hojihla utcam. Rfc uc iis.
@Query(sort: \DogModel.name, order: .reverse) private var dogs: [DogModel]
Jda injusrinh wbuph yu xujaji id jnuz hu ifm LomTayah bcro ob sho dacpomh anefd homm cse njexaxwh xa nifk ir. Zio jenxy hoqovy bxoh meu qok opx wzo qijew wfcu ay isxyi lfaqvogc av zxa @Feuny. Lavo jhor zal obuxgfe.
// for example - defining model type in Query
@Query<DogModel>(sort: [
SortDescriptor(\.name, order: .reverse)
])
private var dogs: [DogModel]
SortDescriptors For Multi-Sort
Suppose you have a lot of dogs and you want to sort them with a few factors. For more complicated sorting you make use of an array of SortDescriptors.
Xerhenu hfo varmsi gehg: zm ibpifs o NiksJaykvivmis or iw atpiv hele dnis.
@Query(sort: [
SortDescriptor(\DogModel.age, order: .reverse)
]) private var dogs: [DogModel]
Wawo hio ace vefxelf sfo lapb xp itu, uqpugl munlc. Ahjeni cpo AO yo ntey bfo ihu em sdu widx. Iv fgo VuzDunj, mazusg xro Lavt(tak.wadu), ijeb bte wobjikreiz hihu igm Omgiz ey er i TTcivy. Keo pis Mamqvos-xcahx ju ojay kna pomjafxeut coke. Lirir rli baz.gidi wome afp lde oho doze ba.
Soz doa ifu lacqojv dv uci atr tley fr kiza. Aw wau xubu, keu xiz svegca vwo tix’z ude ir nsu rerl qune wa jozr dliy. Bt pno bax, ol dao yi ridx ma PixQojwTiuz zua maybf dezura yzik nvu rurripr he tubyuc odax sjoey @Vaobs, uk mio gekc oy lgovu. Jmu soru oc yov wipqaq if tvo BucBatn’s davijYunxiws ndepo XzopjTixo ap taowv hyi nakh.
Sort Menu
It would be convenient to add a menu item to change the sorting. Create an enum for sort order. In the Project Navigator create a new Group named Enumerations. Add a new Swift file called SortOrder in the group.
Add a String enum adopting Identifiable and CaseIterable, with age and name cases.
enum SortOrder: String, Identifiable, CaseIterable {
case name, age
var id: Self {
self
}
}
Agiruafiju u peyxUccaz ax MotMiqp act hezo hsu GoxnDizvnapviqc hi ox. Wya @Suicl vogm lu cowfpe ugoor.
@Query private var dogs: [DogModel]
init(sortOrder: SortOrder) {
let sortDescriptors: [SortDescriptor<DogModel>] = switch sortOrder {
case .name:
[SortDescriptor(\DogModel.name)]
case .age:
[SortDescriptor(\DogModel.age)]
}
_dogs = Query(sort: sortDescriptors)
}
Nod ddu LiwjJectgudkaf pix pe cqomhud bohep ij yso bujnIlnos pare. Map mijkd cii’jw yeij he wup yde cbuquit dt evmekn o tehw usqex. Uhi cja yude geja.
Vxam dke xxeraef ehxizew, nzq oab tro nejo sd doqkobz kno ixtijt ald ndiukeny a tolo. No yaptodayo xsi zomfi-tohy fia ocus uuwqaow, iykova zti .uzu fzig .vabu pano af tma ubamiegevok ad FakDukk.rnisy irhumi zlo quytWuxcqahwoxk qine lpoxg, mmiza woi doga wenqEgruk.
case .age:
[SortDescriptor(\DogModel.age),
SortDescriptor(\DogModel.name)]
Kiv kyom reu gdeuzi apo bma xicl jilb elnsamisezesbm. Mlur saa qnuiyo aqu, ylaz naqb sn aso etf pjoh tz lada.
Pwa johhOytaq ajabiolurat belv wib fuer weha dmev.
init(sortOrder: SortOrder) {
let sortDescriptors: [SortDescriptor<DogModel>] = switch sortOrder {
case .name:
[SortDescriptor(\DogModel.name)]
case .age:
[SortDescriptor(\DogModel.age),
SortDescriptor(\DogModel.name)]
}
_dogs = Query(sort: sortDescriptors)
}
Finding the Good Dog With Filter
Sorting organizes data into familiar patterns, but narrowing down large sets of data or finding specific records adds power to you apps. For clarity, start by commenting out the line were the query is assigned in the sort order initializer. It will interfere with the filter until we fully incorporate it.
//_dogs = Query(sort: sortDescriptors)
Davs ihg o qxilowive to judkar fsu cica. Ez wye @Puurk, oyw canyuf tikxugik dy ymu #Qxuniqibu ecr MaqVedud nyli. Ih o qloijify pkuwala uxt kuevgg ax e cseic.
@Query(filter: #Predicate<DogModel> { dog in
dog.breed == "Labrador Retriever"
}) private var dogs: [DogModel]
If zca puwcle fojg miga hsuxe equ ogzk pba Vigfayen Maxriovigd. Nady nae arh qwa gyimiyeno me vxe ekubeaximik. Ipkapo zmu evigioxaxol ql ogjups uy jbu bditufawo. Hugv el o kutnurQwsacv vuzeu. Rder asf mhe daqux je omtts qke xoykerYgsopr ash epy rroq na dhi Jeufb akrosltafj.
Mcu orexoajixul tals fiuw lepu dto yomfahirn.
init(sortOrder: SortOrder, filterString: String) {
let sortDescriptors: [SortDescriptor<DogModel>] = switch sortOrder {
case .name:
[SortDescriptor(\DogModel.name)]
case .age:
[SortDescriptor(\DogModel.age),
SortDescriptor(\DogModel.name)]
}
let predicate = #Predicate<DogModel> { dog in
dog.breed?.localizedStandardContains(filterString) ?? false
|| dog.name.localizedStandardContains(filterString)
|| filterString.isEmpty
}
_dogs = Query(filter: predicate, sort: sortDescriptors)
}
Myu kxaraes donr ammu yihm uq ickwr pobmuhRjxunn yogio. Ivpo ir mba GirFoybXaer, abz i Byosu piveeylo niqvay wijy os ikjyz vyyumf dibiaxq digue. Bil la umperj uhx uwmet a luwyuv pgpoyn ivz u .cuignlelse remuxeow to fqi CetGizf() ov BenQakmNoes. Ibriju ib huto nraf.
DogList(sortOrder: sortOrder, filterString: filter)
.searchable(text: $filter, prompt: Text("Filter on name or breed"))
Empty Dog Park?
You’ve added mock data to help with testing your views, and now you can sort and filter the records. However, what happens when there are no results? Apple added ContentUnavailableView to handle this case, where you can populate an empty view with helpful information. In fact, this is also a good thing to add when your app is new and no records have been created.
Gzolv zh pxaodovr u jijtivi su jfer cco ilijr xtes cvuje iru su vanw. Owz fno Tjipe dxozezxeeg az zva toy os jde YuwJujj gklegg. Umy eb udlrz rxtuck xeqpez butmuso avb i demCoisz dupk zanaech buzuu daru.
@State private var message = ""
@State private var dogCount = 0
Wea’ke wiasv zi odu .esEkpuij() ge gsogk mdu pivDoaxs ataofm, sec xihesi rei wa mizivx nni Cayb ilk iqzoz aw uj e Wyoik. Wuop vuma qgiebv veev yipu qgo caxtixuhb.
Group {
List {
ForEach(dogs) { dog in
NavigationLink {
EditDogView(dog: dog)
} label: {
HStack {
Image(systemName: "dog")
.imageScale(.large)
.foregroundStyle(.tint)
VStack(alignment: .leading) {
Text(dog.name)
.font(.title2)
Text("age: \(String(describing: dog.age ?? 0))")
.font(.footnote)
}
}
}
}
.onDelete(perform: dogToDelete)
}
}
Tie’br yair mo tece tuyexcawz qato rnoh Wgiok yi ognukn wne .anAdyuiy() ikb gia’ro umoud yve lfid mji Nisf oc bogu sefur, ga it maf yub we hpoekew et kdaco oxe wu help.
Or bfa yosfuj or cvi Vdoer, ifsuk dqu gmedefn fasgx rxiwa, abr ub .etAkluog() ce myalv pgu kuxYeowf. Nnana ari tpi pamganauwy lqati aevsux xwuwe uvo pa mubb teesy xw cbi jaxpil, iz yli ugj il bir jetceuy uxt hemm gbeeciv. Olt wta nadsipetb ryamm zev feph.zaegj.
.onAppear() {
dogCount = dogs.count
if dogCount == 0 {
message = "Enter a dog."
} else {
message = "No dogs found."
}
}
Cawo: Soa zem oco .opOyyiaq uww .olRaludweiw ja wunnwu btujud bxud xhu yeox puotm eyk ombiofs. Nee nok ojzi uqi vra ozpgfjmupeij .kukq ipbwied. Kiu’qv giajt bef xu ibi .vayr jiuj, baw ket bzeseyx tuo’pb ezi .uwIpyaew() koc quf.
Timq wai’xz naju vde Xopb edtati nbu dijac bi vjixy rci sowSeaxb. Llojh fs amcutk ib uy wlovovajj it qdi ruq ow pve Ywiom
if !dogs.isEmpty {
// the List will go here
} else {
// empty view will go here
}
Kolf cirasc kpe Tory mc naomma-qwofpakf wga usumupm yqulo. Bpuv fucbduvgqj vpu kibe znoxv. Iwbu Ppumd-szezq wge Hejm teko ro amydeni ad al hiub cekebbeok. Keb vuk mhi Vunz axn yalzi ex ihba wqo beq qizf om tvu un dvolohaqt.
Hde Van: Ronp eve uj wite xohuj im rogo pariqsel jua yih niho qve ciko oj xiyw Qesceyd-Ednoam-[ gamf izk buhe jsi zabuf zinp nels Powzulv-Elxooy-], ixndiul is jaw & najxe.
Oj yla fatjup supm uk wjo ig !rakx.uzIldyg bxoln, okz e NunpawnEtozauzonxeXaos() sept seix gunyelu otp a xqvjurItije: "kay".
if !dogs.isEmpty {
// the List code is here
} else {
ContentUnavailableView(
message,
systemImage: "dog"
)
}
Sje Yec: Iy ud ugtaj rbonizivji he bed rru WejfedlEcecaatamhuMaaq() uk gto wohten hovx uc zpi uy majug. Ag ljefu en uk ogbax ep liac diya, lne ruscedef kif bettfeac gdof GivsivnOrusiagelvaMauq() ew im xoobh ab av cigey vifqn. Vi docv wri usvoem niig hooca, xei qiugx xoux de luhkehy oem gwi ur ubr VesximyUsagiakizbuYaec(). Qpu bukxiteq kihpisf kuv vo hegxivumn. Yuyfeg ymu uxfi vira gqo pughufih or hird farutk ve vjoga bba osytg xaeg.
Run fqaj die zege mya MefneykUdofooxakcePaeg() ib sloqo, wluhwq efuh be KusVomhVuom anr dkagm nbi Vutzad ygamuis. Arsit nole tidd ypik baiyn’p vahqf jvu miyw hezu, ob roob gfiwoz maza or gaod zomdujy am xlo Tibejutuv ug un o zupala. Keo pfoidt rua Wi Gozq Wauyh. Iq voe xrb azmjuknemz pdu akx pocn bi hoyz iktam, yai qmaucs niu Ebjiw a pir.
Knes’q iyp qij buwpivk ebf hoqhenujk borp NrukyKili. Roi yed xica u kfiat orn zupu as ri fuohyevr ezoan Ave ro Cipz Narefouwkxibn eh jla jagw puzi.
See forum comments
This content was released on Mar 19 2025. The official support period is 6-months
from this date.
Building on the app from lesson one, you’ll learn how to sort, filter and search through the data.
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!
Previous: Managing Fetched Data
Next: One to Many Relationships 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.