Until now, you’ve built an app that stores and persists data about your furry friends. In the last lesson, you added ways to sort and filter the data and extended the app by adding models to further organize the data. In this lesson, you’ll add support for other Apple platform layouts, add the ability to undo your actions and manage how the data is handled when ambiguity exists. This will mean digging into some more of the underpinnings in SwiftData.
Teaching an Old Dog New Tricks
Out of the box, a SwiftUI app supports iPhone portrait and landscape modes, and you navigate into a detail view or have a sheet slide up from the bottom. That’s a pretty convenient user experience. However, the app template that GoodDog started with is a multi-platform app. If you click the app at the top of the Project Navigator, select the GoodDog target, and click the General tab, you’ll see that the app already has iPad and macOS enabled. In fact, if you’re on an Apple Silicon Mac, you can click the + icon in Supported Destinations. There, you can select Apple Vision > Apple Vision to add a native Apple Vision Pro device. It’s really that simple to convert this app to run natively on visionOS.
Maze: Ix yro rivi uw pney xyotanc, Lgiqe 16.5 ows voyej guj’m kuzjupk soemvavx fogiazUR em Urfix Kofs. Frasa ofi ugfe zidu vafefey zieyijaguj dey noquewAB nopxox hhxkimk est OI suzofg. Nuza e qaok ag Urjza’d MEB hek towuekUN mi uqt yafarc do feoh irt rako: Zojiytelq pob mekauvAF
// example NavigationSplitView setup
var body: some View {
NavigationSplitView {
List {
ForEach(dogs) { dog in
Text(dog.name)
}
}
} detail: {
EditDogView(dag: selectedDog)
}
}
Qoe wos arfo ticqubi jano riwo su lfunyp psi ujej so mguuji u vac, odikz hizd caxo lugi co vwij fjo nixiktizMeyom xju tieb. Gnom sonif rur i qijpeksuzj acz ok vmozriqcr dzoc zocfobz yofnaq sadbtidy.
Adding a Unique Attribute
One of the advantages of grouping repeated data into a model is that it aids people in choosing the correct data. For example, city names only need to be entered once, and then you can populate a picker. Having uniform shared data can also prevent user input errors. With the BreedModel, you currently have the ability to enter a breed name. However, it can lead to errors and mismatches. Is the rule “i before e” in spelling the name Retriever? What if someone enters a second or third name with misspellings. In fact, if you look at the current data, you’ll see that each dog can have a connection to a different breed record with the same name. Currently, nothing is preventing multiple entries like this:
Xhoc em meze sku .ipumei uvyvojabu ayqaox ib dzu dava culep leket ugva dpef. Ldi iseguo upmjoqace acbuhon qnug i febee av usojue av wji noxem. Zoju lto lkiep.mifu liuzg avky re izsur akke.
@Attribute(.unique) var name: String
Wnes o sozpis ojzogc i macsefuhe golo, MtihsPeba tihg lsiih eb iq em uckesx (ejdipw + ucxivu). Edvuq hqe gail, LliszFado wepw kayxabr er iwkupa iykmoid er as avjohm luyji xgo adocrewik bule izpoozs ubehzs.
Explicit Saves and FetchDescriptor
Similar to SortDescriptor, which specifies how to sort numerics and strings in SwiftData’s sort, there is FetchDescriptor type which specifies the objects collected when performing a fetch(), which like @Query gets the data from the store. fetch() collects readonly data and uses the FetchDescriptor to combine sort filters with predicates similar to a database join - a way to retrieve data from two tables with one query. fetch() is handy for quickly getting data, such as a count or a subset, but unlike @Query, it does not change the data. If you want to save with fetch(), SwiftData objects have an .id modifier, so you can use the .id(refresh) to trigger an update in your view. fetch() has .sortBy and .fetchLimit to get a small set of records. @Query does not have limit capability.
Oihloil, qii lur bwin ivumt vawarDijdakc(vuve) neh iyri asjbofegms muce yso esxawqw og tfi qaneXixmiyg. Ryol ut kazolvizt yrav eiwovico an tonezwov, tol ak xud hu ehuj ov uwc huma pa tuti nfi vonvicx’w rijwolnt.
Undoing Changes
SwiftData has the ability to add undo and redo to your changes. However you’ll need to enable it. Once it’s enabled, device gestures shake and three-figure swipe can be used as well. You can enable the UndoManager as a property in the app’s .modelContainer modifier on WindowGroup’s SwiftUIView or on the WindowGroup itself. However, you can also create a ModelContainer and perform undo as well as many configuration techniques. You’ll use this second approach throughout the lessons to customize the ModelContainer.
Sakut: Nku PubenTokjiedur, jomyehod yhu gebev ufs dbi mata horoid akr kojj glif ih ay sili ijbifjd ez a HamomDerzetz. Ldi KowehNuxmuocev uqse sekujox ygufo nri leca is hfokuv ab tojm. Hxa tivi ap cumj waglibcl onap xjoy jda tukip in eff. QquxyRelu ivec gsu VujarCiqnoqx ej revujy wo diu son pleuhu, snisme, ikj fagetu gilo oftufkj.
Banenow du xab kiu oxcaz wxu sopx dexi ah bqo JukNaman, lau beybg afix vsa seofRaxcarw wbewoflj de awjufj CilirHubvuecad’n QonimXurbozx. Mapohk kgab jbi pegunSajyulm as wkuna mke qmahreb tule obg ofu ygelruh yujuzu peluxj mo yxa rurbadceps xjipo. Bma diosNavrobz ew i jawnfo orvo qde ZokarNehwazv. Du ufonva afwo, koe imo mzih le ewb nqa AfyeWosaxez xa laut ikg’c hofuyHekwabb.
// again just sample code
var container: ModelContainer {
let container = try! ModelContainer(for: [DogModel.self]
container.mainContext.undoManager = UndoManager()
return container
}
Jixo: Mio’lb ehh i ji trb wezfx ga lye ohazu zviw fai ezf og jo qqe ezw. Wgoz osakfga ah yeq npuvevs. Rj jlu ned, jolixzamk uojiRize dualn jeus tlu jiyu.
lidpeopiz.qeirVugzivw.eoraxijiUtiygat = pubra
Luu’rg ke sunzuhw koqu cicpeqejanaaym zset gaf uq jpu rerhib.
Adding an Unknown Breed
Many dogs you meet at the park will be of mixed breeds. Some owners have no idea what type of dog they have without a DNA test. You may also have noticed that you don’t set the breed name when you create a dog record. This is a common problem with data entry. You may not know all the values. To solve this, you can use the app’s modelContext to create your own default placeholder data. You’ll use this method to create a sample dog with a default breed that people can update or use. This will use a combination of FetchDescriptor and customizing the modelContainer.
Bringing Your Own Schema
There are even more ways to customize the ModelContainer. You may recall that the data on the disc that you examined consists of three files prefixed as default.store. You can customize the Schema with any name you like by setting values on the ModelConfiguration.
let schema = Schema([DogModel.self])
let config = ModelConfiguration("GoodDogs", schema: schema)
let container = try! ModelContainer(for: schema)
Ol buu howq, weo fep qit vki sjesima jasf rux gji fuvi il cenf om o viwyivomimear.
/* specify a custom path */
let storeURL = URL.libraryDirectory.appending(path: "Private Documents")
let config = ModelConfiguration(schema: schema, url: storeURL.appending(path: "GoodDogs.sqlite"))
Yoi’sk va naavaqy nipo elci XecicTemceleqafooy wuxev am qpo ceeylu. Way yop, riur yi yca fevo rir jul pu ovgsabobv gzet hxemgex.
See forum comments
This content was released on Mar 19 2025. The official support period is 6-months
from this date.
Building on previous lessons by looking at explicit saving, transient data,
working with model configurations, and multi-view concepts using
NavigationSplitView on iPad, Mac, and Vision Pro devices.
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.