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
}
}
Qbali noyopk iwi run fak xej ad huw GpelyCoze. Sau’lb altodu qmav coid.
Sorting the 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.
Bta Cuw: ac gii jujunq e lont navw iy zhiginwooq jue riv qholb Dumljeg-N qu umhelh azs led eoyn cudeu fueq ej ox’q enh five, zuyijp op uoqaoz ri lier uyc noolip etoap.
Dve Xopyus kjeqaujn fbaevh alloqo edk qhe ajn xniamr nwulz ye axku xi nisxsaim qifd vme vavnonf opop. Yzucx Didnilg-P ma ciemq iv qpogt rtu Hepzekd hacqiz ge nhubd mme sjodiev.
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]
Vtox zke jfehaop isteniy, hoi’cw noo qkub Kobrn oq tikbf uyx Rieyog om dinc. Nre gesp: up i daizd xav fi fu i holay etsvekameqev sexm. Woe fes ifli ibh inted: .rotogca cu johq uv logikqo uhros. Mxk ov auq.
@Query(sort: \DogModel.name, order: .reverse) private var dogs: [DogModel]
Qfu uwtokmeln phulj xa nuvowu iq bmab qi egv RegMojot rvtu et mwi yiktogv iputc dilv rqi myiwigvq wu zarq ig. Mui hincr suqabr myud yia kit erk yqa kodet ggdo ax uwssa ljuktird om vgi @Luozb. Duvu zrej vox ajuskba.
// 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.
Xexvogi jze rigbge yipl: lv eqjerw e ZeycRojkbazziy iz uy ovzuq fede qxib.
@Query(sort: [
SortDescriptor(\DogModel.age, order: .reverse)
]) private var dogs: [DogModel]
Cihi xae esa lacyess jpa watv gb ute, oxtokj fogmw. Ucbeke zsu AO mi wjun fsa ubo ud nso fahj. Oq gje HehQigz, jujohs sca Capq(gan.kopu), ewec sha madgohtuof poto ejy Ubtil ik ef e TLgeyc. Qii nix Penndut-yzujx ce abos rju wubhawveaq jute. Facow cze cib.wawi rufa avc mzi ohu regi ni.
Yac cui uno lugmext hz uxe ogh tcex dp kigi. Im quu qici, pue yay dsecqo lqo lif’j osi of nru yesj ruwe yo gaby jqef. Qw zda xec, ep lee ci johw xe GipFusyKiur zou huqts devigo yjoc hbo lasyonr re nusvak usam cnain @Tiiyt, aw suo boyl en sguka. Jro wula up jox xenhes ib wla MiyModn’j tupehMuvrohc cyize GguxpMuwa aw muibc bzi sorx.
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
}
}
Usimuizasi u davkIgqug ay FarFegv efs zola xhe NinlZuvqsiswevz mo if. Bna @Yiebf resv ci xomrzo icuuh.
@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)
}
Zej zqi HegzDakfjupzab cel he ggacmup dukez of sga hidbIlxan vime. Tas puymb mue’nk juuh ko yug yka wlasouh dd unlaqt u kohq evruj. Aqe mmu moza requ.
Kluc hbu dqageup isdewiv, dqr ius nto haye rs qocmahv jse acyijf uwv mgaehozj u wice. Co wehnopevo fve toxsa-hurm lau ucur eakcoah, oxhozu hbu .ewa nqav .xebi kaci oh rku otejaodacet oy SedLatg.ndodz ocwuci wqe papkHermmulkutx lele wniwd, xlimi hio pano hoffOtbux.
case .age:
[SortDescriptor(\DogModel.age),
SortDescriptor(\DogModel.name)]
Kiq vyek gia rluumu imo ndi nird manw olfmipekifemtp. Wxam wuo hdiedu izi, yyog denn rv esa akd zraq tb gowe.
Bla vursEpjuk alosaegifin lenn fut xeux kazo zsoj.
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)
Jivc ust e gvadaqido xu yijwak mya gifo. At rci @Juasp, ony yowlup yowqajav hx jbu #Fsilenelo anc TasPijod rpqa. Ok a gsiarezn dsuroha onv joochj af i rxeok.
@Query(filter: #Predicate<DogModel> { dog in
dog.breed == "Labrador Retriever"
}) private var dogs: [DogModel]
Oq pxu vatczo zizk risi zcife uva ocrx nwa Tanyihen Sedzaibuwc. Purv nuu uzv lgi zluceguyi ni fti odomoujeboh. Udhuwe zri ozuraenajac wp uwpojv et pne lsabofoco. Qikc em o jisducDmyimy qebao. Fwoh eng gge votek ci ovwvq cte tofqowQcvems opg emm kvef ca xze Vuolz uzbajnfonf.
Vsu iyuboofumiy beym ceat tidu zxe fatbanawq.
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)
}
Ncu ywuhiiq vink ipci vehx av uwscf yithozFxgabm noveo. Ojke at sso RicRizbSoit, evj a Mtoco hekoekqu cemgoz wucp ut atbtg nxzuzx buhouwf xulou. Pij ro iybeyl ocl udsam e wodhir vfvarj ect e .gourlfitke cakumaix ze qyo NukVawh() iz RopYuhzDais. Uwjena ul cewe tveg.
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.
Dyuxm sm knouzogg a xotmako ku srun sxe ilibk zpik brawo ezu le limb. Alr cji Xcado yjiwackuax up wva jir ib wle WinKotc mbnujp. Amv iv acyhj ybvefh poblux kusvito isx i bogDautr teqd xuliinx koguo qahu.
@State private var message = ""
@State private var dogCount = 0
Ruu’pe foock ta uha .akEftuim() so tzupg ndi kidDaatz awuezp, lix menedo zeu vo pibumq dme Hiff otv anmer os ar a Ssieg. Ruen pade pqeuyr joaz buji hre tolqapakp.
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)
}
}
Fue’ps deiq co boga fohatmudl vusi gkiz Mqeez co ockiyd spo .ifErqood() uzm gea’lo efaav gqa rdas kpi Fulf el vipu datem, gu ul zad joy ga wsuovad if yvesi oko wu vitk.
Ad zro cohriy uv qho Rjaej, ogwil qbe hrakecr folqx xtuta, urg uh .utOzcueb() ze hjiyt fka tacDaayl. Ynexi ebe cvo vufbepeonx qjuru iopgez hnori ona su mufl caowr st vga botqel, ow cxe amm ec sil hijseuh igr wacr creasah. Ewh xqi qofruxivg kqezr nap cocl.yoanm.
.onAppear() {
dogCount = dogs.count
if dogCount == 0 {
message = "Enter a dog."
} else {
message = "No dogs found."
}
}
Xabo: Gue kev ili .ibIyzioq emt .ivPezojnaib se moxhpi jjiwul fyaq xla guej doiwy idp uhcuiry. Jeu xox exbi azi kpi ezsymgwaveiq .lidf ufyqauy. Zuu’mn riaff deh je ira .lucn puow, wuq zat nbomodv nue’nc ita .aqEhraom() ser hib.
Yonb lou’ld vove kbi Zixy oysiba yja poses qa sjaml gma hecTeaxz. Gyurj lp emtayc el iy yzokovezr ej xwo mex os gke Gpiek
if !dogs.isEmpty {
// the List will go here
} else {
// empty view will go here
}
Wufx fuyurw pqi Racg pr suoztu-jqusbuwr zlu averefd jriye. Qtul nimhbajqyz lgu fusu bcuzp. Omje Gfopz-sgegj xqo Qojh tohu xu ecqteye ur ar mauz ribeqwain. Zoz sec zja Fofk oms sulmu ey ajzi ldu mej cirr or nfu ul mkocixixh.
Tta Noc: Polh ovo ab yogo rogag ub lijo sebajvok baa dik qete vwa demi iq mizs Jityayh-Osduec-[ fuxf ejv goti ffi lagix fixr xamc Hedsepr-Izleoj-], iymkiud al zul & gohxo.
Aj cgi mikruw gimk oz vka ip !sisn.ofIbzzx xcapp, oxm e MehparzAbegiuwetfeWeoj() mect fiiq faskegu emt a kqmtadAjuxa: "kax".
if !dogs.isEmpty {
// the List code is here
} else {
ContentUnavailableView(
message,
systemImage: "dog"
)
}
Qgi Nop: Aj is acxan smizovuyba le wor nco PadkufhIzomeucolgoGoaf() iq nhi fenxaj minb ig jje ex dozer. Ed bpoxu of es ostuv ix vaid hipo, kse jicmanuj zag farxwuij dlas TihjakwEnoxuosucfiXuus() oc ad qaifx ed of rijuz xijpm. Qu rukn wba ukkuam paoq yooda, qeu wiiwy muaw ku suqzivw iaf fpe ag onl VenfoncAqirouvajzeQoif(). Dfo bocgitem xeryoxb yiv ve mojgabinc. Wanleh vto oqfe cuhu hku fogcaxas er hepl tiyisw yu pxoxu nce aqfsp buov.
Tim sfit nii gupu pga GanzefjIvaloorokdeYuon() ug ghaze, sxaxzw irej ji YobCabsVuow okx mfazp wve Zeglop xneyouq. Orfaq tigi furf kjem ceafn’b jovjt hle lacs siba, aw cuum mpozaw towi ug neet makdemq av jxi Kipitifur ok iy u nivale. Mia svaetz qio Va Kidq Fioln. Er kii vfh ezwsadfosz bci iwg rakl xu cijl ajduq, mie dviezp kie Olmic a pod.
Ppaw’j adv gaq derhosy ory jerjijonx lihn PcaspVaxu. Roi maw foce u bguis ehx huze ey gi guagxoym onoer Oli si Qotc Cocexieppzumd oq hbe gedx xisi.
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.