Parse JSON from an API URL in Swift [Codable and URLSession]

Fetching and parsing the JSON data returned by a URL is straightforward in Swift, thanks to the JSONDecoder and URLSession classes of the Foundation framework.



FREE GUIDE: Parsing JSON in Swift - The Ultimate Cheat Sheet

Learn the fundamentals of JSON parsing in Swift, advanced decoding techniques, and tasks for app development

DOWNLOAD THE FREE GUIDE

Table of contents

How to fetch JSON data from an API URL in Swift

To fetch and parse JSON data from an API URL in Swift, you need to:

  1. Identify the URL of the resource you want to download;
  2. Create Decodable Swift types reflecting the JSON you want to parse;
  3. Download the data using the URLSession class;
  4. Decode the downloaded data using the JSONDecoder class.

For our sample, I will use the https://gutendex.com/books/ URL from the Gutendex JSON web API to download the most popular books on Project Gutenberg.

You can open this API endpoint in your web browser and see the JSON data it returns. The data returned is extensive, but we are only interested in book titles and the number of downloads, so this is the corresponding portion of the JSON data.

{
	"results": [
		{
			"id": 84,
			"title": "Frankenstein; Or, The Modern Prometheus",
			"download_count": 103323
		}
	]
}

Looking at this data, we need to create two nested Decodable Swift structures that match the JSON data returned by the URL.

struct Response: Decodable {
	let results: [Book]
}

struct Book: Decodable, Identifiable {
	let id: Int
	let title: String
	let downloads: Int

	enum CodingKeys: String, CodingKey {
		case id, title
		case downloads = "download_count"
	}
}

Then, we fetch the data from the URL using the shared instance of the URLSession class and decode it using a JSONDecoder instance.

func fetchBooks() async throws -> [Book] {
	let url = URL(string: "https://gutendex.com/books/")!
	let (data, _) = try await URLSession.shared.data(from: url)
	let response = try JSONDecoder().decode(Response.self, from: data)
	return response.results
}

Fetching the JSON data using a Task in SwiftUI

You can test the fetching of the JSON data in a Swift playground by calling the fetchBooks() asynchronous function inside the body of a Task.

Task {
	let books = try await fetchBooks()
}

Inside a SwiftUI app, you fetch the JSON data and then parse it inside the task(priority:_:) view modifier.

struct ContentView: View {
	@State var books: [Book] = []

    var body: some View {
		List(books) { book in
			LabeledContent(book.title, value: book.downloads, format: .number)
				.monospacedDigit()
		}
		.task {
			books = (try? await fetchBooks()) ?? []
		}
    }

	func fetchBooks() async throws -> [Book] {
		let url = URL(string: "https://gutendex.com/books/")!
		let (data, _) = try await URLSession.shared.data(from: url)
		let response = try JSONDecoder().decode(Response.self, from: data)
		return response.results
	}
}

After downloading, you can save the data in a JSON file. You can also pretty-print the JSON data if you need to inspect it to debug your app.

While fetching and parsing the JSON data from a single URL is pretty straightforward, an actual SwiftUI app must often download data from several API endpoints.

In that case, you should carefully architect your networking layer to interact with a REST API.

If you want a quick reference of how to parse JSON from a URL, together with other JSON parsing techniques, download my free cheat sheet below.

FREE GUIDE: Parsing JSON in Swift - The Ultimate Cheat Sheet

Learn the fundamentals of JSON parsing in Swift, advanced decoding techniques, and tasks for app development

DOWNLOAD THE FREE GUIDE

Leave a Comment