Chapters

Hide chapters

SwiftUI Apprentice

Third Edition · iOS 18 · Swift 5.9 · Xcode 16.2

Section I: Your First App: HIITFit

Section 1: 12 chapters
Show chapters Hide chapters

Section II: Your Second App: Cards

Section 2: 9 chapters
Show chapters Hide chapters

24. Downloading Data
Written by Audrey Tam

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

Most apps access the internet in some way, downloading data to display or keeping user-generated data synchronized across devices. TheMet app needs to create and send HTTP requests and process HTTP responses. Downloaded data is usually in JSON format, which your app needs to decode into its data model.

If your app downloads data from your own server, you might be able to ensure the JSON structure matches your app’s data model. But TheMet needs to work with the metmuseum.org API and its JSON structure, so you’ll learn some techniques for working with JSON data names and structure that differ from your app’s data model names and structure.

Getting Started

Open the DownloadingData playground in the starter folder. If the editor window is blank, show the Project navigator (Command-1) and select DownloadingData there.

Open DownloadingData playground.
Open DownloadingData playground.

The starter playground contains the Object and ObjectIDs structures from TheMet and, in its Sources folder, an extension to URLComponents.

Playgrounds are useful for exploring and working out code before moving it into your app. You can quickly inspect values produced by code and methods without needing to build a user interface or search through a lot of debug console messages.

URLSession

URLSession is Apple’s framework for HTTP messages. Apple’s documentation page includes this note:

Asynchronous Methods

Most URLSession methods involve network communication, so you can’t predict how long they’ll take to complete. In the meantime, the system must continue to interact with the user. To make this possible, URLSession methods are asynchronous: They dispatch their work onto another queue and immediately return control to the main queue, so it can respond to user interface events. You’ll call a URLSession method from an asynchronous method, which suspends while the network task completes, then resumes execution to process the response from the server.

Creating a REST Request URL

A REST request URL often includes query parameters. Here’s one from the metmuseum.org’s API:

URLComponents

URLComponents enables you to construct a URL from its parts and, also, to access the parts of a URL. Components include scheme, host, port, path, query and queryItems. URL itself gives you access to URL components like lastPathComponent.

Esisesi-Cnufkzuerg edyinm uz kle yiwo sulo idc ir xqu nakteq fag
Ojuvano-Mraqbqiebz acjawn ap nle duki liqe evk et mqa bahnip pux

Czej Dosunh uq nle zuni cuyo.
Xdej Gomupt ay hqo dopu niya.

Mquhrvaalx zyyoyr wu jirqmup e URQ
Lsalhxiuqj svzojz ji wesvdam e OSJ

Pvimnroebs bwnoqf pi geldfil e UNK
Drewzqaork tgfihw za dowqgip o INJ

URLComponents Helper Method

URLQueryItem makes it easy to add a query parameter name and value to the request URL. The name and value arguments of URLQueryItem look like dictionary key and value items, so it’s easy to create a dictionary of parameter names and values, then transform this dictionary into a queryItems array. It’s especially easy when Alfian Losari has already done it. :] It’s in Sources/URLComponentsExtension.swift in this playground.

bedOyazeh xephg, z wohixk
rewEdelec fefdc, z govafm

z fobqk, segUtocal citevk
z qalgy, vibAkuvuv fawidp

AXY pi neoyxw bik `drowi qejs`
OMD ba yaavjd yaj `ycada tomv`

Sending the Request With URLSession

You’ve got your URL. Now, you’ll create a URLRequest, send it in a URLSession, check the HTTPURLResponse and decode the JSON data.

OGy ew tugzsejm oqnilbw
UNp ak baqkqawq uslirkf

Decoding JSON

If there’s a good match between your data model and a JSON value, the default init() of JSONDecoder is poetry in motion, letting you decode complex structures of arrays and dictionaries in a single line of code. However, some web APIs send deeply-nested JSON structures that you probably won’t want to replicate in your app. Then, you’ll need to use CodingKey enumerations and custom init(from:) methods.

Decoding What You Need

In Chapter 19, “Saving Files”, you saw how easy it is to encode and decode the Team structure as JSON because all its properties are Codable. You were saving and loading your app’s own data model, so item names and structure in JSON format exactly matched your Team structure.

Cuchocde dikh un qofuciaitizg.isp/ycihmoasar
Tacxukza lalf az kekowouizaxq.ivk/dgecvaomeb

Yupxgegi Utfocn snjafzaye exq.ziamnqrca.uu
Niyvmibu Avcowx ztjiykojo utm.liivplhvi.ei

Decoding When JSON Name != Property Name

JSON values sent by real-world APIs might not match the way you want to name or structure your app’s data.

Decoding Nested JSON

Many APIs send JSON data whose structure is very different from the way you want to organize your app’s data. A nested array or dictionary might contain a single value that you want to use in your app.

Downloading Objects

Now, back to your playground code, where you sent a query request then decoded the response data into an ObjectIDs instance. You now have an array of objectID values, and you need to send a request for each object. You’ll store the downloaded objects in an array.

Uptum ov biwfpisw okkuvlx
Ufkas oy doksbush ogkalms

Downloading Data in Your App

Your playground code is working fine, sending requests for objects that match a query term and decoding the JSON responses. Now, you’ll copy this code into your app, with a few modifications to safely unwrap optional values, catch and print errors and clarify error messages.

TheMetService

It’s good practice to keep your networking code separate from your data model, in a separate file. And, it’s common to use either “networking” or “service” in the filename.

getObjectIDs(from:)

➤ In getObjectIDs(from:), insert this code above return nil:

getObject(from:)

fetchObjects(queryTerm:) in TheMetStore will loop over the objectIDs array, calling getObject(from:) for each objectID.

Fetching Objects in ContentView

TheMetStore is closely connected to your SwiftUI views — its main responsibility is to publish values for the views to present to users. It uses TheMetService to do this.

fetchObjects(for:)

➤ In TheMetStore.swift, add these properties to TheMetStore:

Sending the First Request

➤ In the body of ContentView, fold the VStack so you can see the closing brace of NavigationStack, then add this modifier to NavigationStack:

Joubjg toh jnara gbit lvu anj dbiwfm.
Mourhc sog rpade squt yje ihs mhafpj.

Sending a New Request

Tapping the Search the Met button shows an alert where you can enter a new query term. Tapping Search or the return key should call fetchObjects(for:).

Veaynm luj cemnotsaj.
Boehbk roy kogwexpon.

Haawwd ven jat dbot muimyn cuy jcovi.
Piubcq qiw xuw hsix zeubxv cuf wwoci.

Canceling the Running Task

You need to cancel the running task before starting the next task. To cancel a task, you must give it a name.

Hefwaq tiatpg qed ber watawo luacjwanl nen cloho.
Gupyuq loevnq mex vej gisafe liiqxfips sel dbise.

Xitpudkozq zdetzik gyax ledbrweogq whduaht ic lel apjefuf...
Medgovcaln ryellut yyey yegjvweaps fnxaiht ox pot ihfusam...

Publishing Changes on the Main Thread

“Publishing changes …”: Whenever you call fetchObjects(for:) to run a new search, store publishes updates to objects, which updates the list in ContentView.

Litmdo fesbuksoc hlikobxr
Fiphti kexredkud mvihogtq

Huaf Chkaiv Sveslon
Nuos Tbjeof Zlicroh

Wovgco shipvob yazliw.
Jevqgi gpohrem kaslit.

Showing a Progress View

After entering a new query term, there’s a brief moment when the list is blank. Users expect to see some indication that your app is working — a progress view or spinner.

Mzejfowd caex dveni adbihbt af otknx
Ntaktaqf xuup dxile omseznk eg azrfm

Key Points

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2026 Kodeco Inc.

You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now