Parsing JSON in Swift: The Complete Guide [With Examples]

Parsing JSON data is fundamental to any iOS app that performs remote REST API calls.

Thanks to the Codable protocols introduced in Swift 4, Swift has a native and idiomatic way to parse JSON data.

Paired with the JSONDecoder class, the Decodable protocol allows straightforward JSON decoding in a few lines of code and more sophisticated techniques to handle all the possible data formats and edge cases.

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

Chapter 1

How to parse JSON data in Swift

To parse JSON data in Swift, follow these two straightforward steps:

  1. Create one or more Decodable Swift types that match the structure of the JSON data;
  2. Decode the data using an instance of the JSONDecoder class.

In this chapter:

Creating decodable types that match the structure of your JSON data

Let’s take, as an example, the following JSON data I took from the Rijksmuseum API, representing the Winter Landscape with Ice Skaters painting made by Hendrick Avercamp.

{
	"objectNumber": "SK-A-1718",
	"title": "Winter Landscape with Ice Skaters",
	"plaqueDescriptionEnglish": "Hendrick Avercamp turned the winter landscape into a subject in its own right. A typical feature of his early work is the high horizon. This enabled Avercamp to focus on the dozens of figures on the ice. He showed all kinds of uncouth details in this bird’s-eye view, including couples making love and going to the toilet and in the distance, in the middle, a man urinating. ",
	"principalMaker": "Hendrick Avercamp"
}

All we need to parse this data is to declare a Decodable Swift structure with stored properties corresponding to the members in the JSON object.

struct Painting: Decodable {
	let objectNumber: String
	let title: String
	let plaqueDescriptionEnglish: String
	let principalMaker: String
}

Writing Swift code to match the structure of your data can be tedious. To speed up the process, you can use quicktype, which generates complex Swift code for decoding JSON data.

Decoding JSON data using a JSONDecoder instance

Once you have your Decodable type, parsing the JSON data is as easy as feeding it to a JSONDecoder instance.

import Foundation

let json = """
{
	"objectNumber": "SK-A-1718",
	"title": "Winter Landscape with Ice Skaters",
	"plaqueDescriptionEnglish": "\r\nHendrick Avercamp turned the winter landscape into a subject in its own right. A typical feature of his early work is the high horizon. This enabled Avercamp to focus on the dozens of figures on the ice. He showed all kinds of uncouth details in this bird’s-eye view, including couples making love and going to the toilet and in the distance, in the middle, a man urinating. ",
	"principalMaker": "Hendrick Avercamp"
}
""".data(using: .utf8)!

let painting = try JSONDecoder().decode(Painting.self, from: json)

You can test your decoding code by running it inside a Swift Playground. If the parsing does not succeed, the JSONDecoder will throw an error you can read in the playground’s debug area.


Further reading:

Decoding nested JSON objects using nested Swift types

To parse nested JSON objects, you create Swift types that match the JSON data’s nesting structure.

For example, let’s take the following JSON data representing the results from searching the top landscape paintings of the Rijksmuseum.

{
	"elapsedMilliseconds":0,
	"count":26712,
	"artObjects":[
		{
			"objectNumber":"SK-A-1718",
			"title":"Winter Landscape with Ice Skaters",
			"principalOrFirstMaker":"Hendrick Avercamp"
		}
	]
}

For simplicity, I included only one object in the artObjects array, but the API usually returns ten results by default.

A Decodable Swift type can be decoded automatically if all its properties have types that conform to the’ Decodable’ protocol.

Looking at the Decodable documentation, you can see a list of some conforming types, although that is not comprehensive. These include:

  • Simple Swift types like Bool, Double, Int, String, Date, and URL;
  • Collection types like Array, Dictionary, and Set.

We can easily decode the above data by creating two nested Swift types.

struct Result: Decodable {
	let artObjects: [ArtObject]
}

struct ArtObject: Decodable {
	let objectNumber: String
	let title: String
	let principalOrFirstMaker: String
}

Decoding JSON objects with optional keys and null values

In your Decodable Swift type, you only need to create stored properties for the JSON object members you want to parse. Any other field present in the data will be ignored during the decoding.

The Optional type is also listed in the Decodable conforming types. If a member of a JSON object is null or missing, you can make the corresponding property of your Swift type optional.

For example, the makers of a painting in the Rijksmuseum API are represented by a JSON object containing their names and places of birth and death.

{
	"name":"Hendrick Avercamp",
	"placeOfBirth": "Amsterdam",
	"placeOfDeath": "Kampen"
}

However, this information is not available for all makers, so it might not be present in the returned data. So, we must make them optional in the corresponding Swift structure.

struct Maker: Decodable {
	let name: String
	let placeOfBirth: String?
	let placeOfDeath: String?
}
Chapter 2

Advanced techniques to parse complex JSON data

Parsing JSON data is, unfortunately, seldom straightforward. The JSON data we get from our remote API often reflects the internal data representation of the server of origin and the API developer’s decisions.

However, an app’s model types should match its business logic according to its user interface, following the dependency inversion principle

An app’s business logic often differs from data representations from outside sources, so we need a way to bridge the two when decoding.


In this chapter:

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

Using coding keys to select and rename the members of a JSON object

Often, the stored properties of your Swift types will have names that differ from the member names of a JSON object. The member names in the data might not fit your app’s business logic or might not follow the Swift API Design Guidelines.

This often happens because JSON data uses snake case, which is common in web development, instead of camel case, which is Swift’s convention.

If that’s the case, you only need to set the keyDecodingStrategy property of the JSONDecoder to .convertFromSnakeCase. No extra steps are required in your Swift types.

However, you will often need to rename a stored property. In that case, you must add coding keys to your decodable types.

For example, we can use coding keys to rename the objectNumber and principalOrFirstMaker properties of the ArtObject structure to something more idiomatic.

struct ArtObject: Decodable {
	let id: String
	let title: String
	let author: String

	enum CodingKeys: String, CodingKey {
		case id = "objectNumber"
		case title
		case author = "principalOrFirstMaker"
	}
}

Xcode’s autocompletion can automatically generate the coding keys for a Swift type. Start typing cod… in the editor, and the autocompletion suggestion will appear.

Coding keys can also be used to exclude stored properties in your Swift type that are not present in the JSON data.


Further reading:

Decoding the various date formats of JSON data

JSON data often contains dates in different formats. Thankfully, the JSONDecoder class can be configured using different date decoding strategies, handling any date format.

In the JSON data for a painting coming from the Rijksmuseum API, the makers of a painting are listed with their places and dates of birth and death.

Sometimes, the dates included the year, month, and day, while others only included the year, as in the following example.

{
	"objectNumber": "SK-C-109",
	"title":"Italian Landscape with a Draughtsman",
	"principalMakers": [
		{
			"name": "Jan Both",
			"placeOfBirth": "Utrecht",
			"dateOfBirth": "1622",
			"dateOfDeath": "1652-08-09",
			"placeOfDeath": "Utrecht"
		}
	],
	"plaqueDescriptionEnglish": "An artist has found a place to sketch by a wood, near a waterfall – perhaps it is Both himself. Travellers with heavy-laden mules are walking towards a plain bathed in Italian sun. Both was clearly fascinated by the Mediterranean light and stayed in Rome for a long time. Back in the Netherlands, he continued to paint Italianate landscapes in the warm glow of the sun."
}

Decoding dates does not require adding any specific code to a Decodable type. You just need to create stored properties with the Date type, which also conforms to Decodable.

struct Painting: Decodable {
	let id: String
	let title: String
	let description: String
	let makers: [Maker]

	enum CodingKeys: String, CodingKey {
		case id = "objectNumber"
		case title
		case description = "plaqueDescriptionEnglish"
		case makers = "principalMakers"
	}
}

struct Maker: Decodable {
	let name: String
	let placeOfBirth: String
	let placeOfDeath: String
	let dateOfBirth: Date
	let dateOfDeath: Date
}

The configuration happens instead at the level of the JSONDecoder instance you use for the decoding. Since dates can have different formats in our example, we need to use a custom date decoding strategy.

let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .custom { decoder in
	let container = try decoder.singleValueContainer()
	let dateString = try container.decode(String.self)
	let formatter = DateFormatter()
	formatter.dateFormat = "yyyy-MM-dd"
	if let date = formatter.date(from: dateString) {
		return date
	}
	formatter.dateFormat = "yyyy"
	return formatter.date(from: dateString)!
}
let painting = try decoder.decode(Painting.self, from: json)

Further reading:

Flattening nested JSON objects

Another reason the structure of JSON data often does not match an app’s business logic is that some information is contained inside unnecessarily nested objects.

For example, the results of searching for the top landscape paintings in the Rijksmuseum API have the URL of the header image for an art object nested inside another JSON object.

{
	"objectNumber":"SK-A-1718",
	"title":"Winter Landscape with Ice Skaters",
	"principalOrFirstMaker":"Hendrick Avercamp",
	"headerImage":{
		"url":"https://lh3.googleusercontent.com/3R_8eX8o6uJNcIgqdMqCqJH8fPufvWZTopDEGKQgfVexy4pBCKr8C3sW3QH-KWgiuarBQxjRyuF8Xi4B4APUiv8q23v1Lk2xQA8pIg2U=s0"
	}
}

While we could easily decode the headerImage member by creating a nested Swift structure, as we have seen above, this would unnecessarily complicate our app’s model. This, in turn, would influence all the app’s code that interacts with the ArtObject type.

We can provide a custom decoding initializer to control the decoding process in such cases. We can decode the headerImage object using a nested decoding container without creating a custom Swift type.

struct ArtObject: Decodable {
	let id: String
	let title: String
	let author: String
	let imageURL: URL

	enum CodingKeys: String, CodingKey {
		case id = "objectNumber"
		case title
		case author = "principalOrFirstMaker"
		case image = "headerImage"
	}

	enum ImageCodingKeys: CodingKey {
		case url
	}

	init(from decoder: any Decoder) throws {
		let container = try decoder.container(keyedBy: CodingKeys.self)
		id = try container.decode(String.self, forKey: .id)
		title = try container.decode(String.self, forKey: .title)
		author = try container.decode(String.self, forKey: .author)
		let imageContainer = try container.nestedContainer(
			keyedBy: ImageCodingKeys.self,
			forKey: .image
		)
		imageURL = try imageContainer.decode(URL.self, forKey: .url)
	}
}

Notice that once you provide a decoding initializer, you must explicitly decode each stored property.

Xcode’s autocompletion can generate the coding keys and the custom decoding initializer for a Decodable type in one sweep.  Start typing ini… in the editor, and the autocompletion suggestion will appear.


Further reading:

Processing the content of JSON arrays

Sometimes, you must also flatten the JSON objects inside an array.

For example, the JSON data of a painting from the Rijksmuseum API contains an array of the painting’s most prominent colors, represented by JSON objects with a color’s hex value and percentage.

{
    "objectNumber": "SK-A-1718",
    "title": "Winter Landscape with Ice Skaters",
    "plaqueDescriptionEnglish": "Hendrick Avercamp turned...",
    "principalMakers": [
        {
            "name":"Hendrick Avercamp",
            "dateOfBirth":"1585",
            "dateOfDeath":"1634",
            "placeOfDeath":"Kampen"
        }
    ],
    "webImage": {
        "url": "https://lh3.googleusercontent.com/1pT..."
    },
    "normalizedColors": [
        {
            "percentage":37,
            "hex":"#D2B48C"
        },
        {
            "percentage":20,
            "hex":" #696969"
        },
        {
            "percentage":18,
            "hex":" #F5F5DC"
        }
    ],
}

Again, as seen above, we could decode the objects in the array by creating custom Swift types and decode the nested array.

However, these objects would again unnecessarily complicate our app’s model. If we only want an array of hex values, we can process the array using a nested unkeyed container.

struct Painting: Decodable {
	let id: String
	let title: String
	let description: String
	let imageURL: URL
	let makers: [Maker]
	let colors: [String]

	enum CodingKeys: String, CodingKey {
		case id = "objectNumber"
		case title
		case description = "plaqueDescriptionEnglish"
		case imageURL = "webImage"
		case makers = "principalMakers"
		case colors = "normalizedColors"
	}

	enum ColorKeys: CodingKey {
		case hex
	}

	enum ImageKeys: CodingKey {
		case url
	}

	init(from decoder: any Decoder) throws {
		let container = try decoder.container(keyedBy: CodingKeys.self)
		id = try container.decode(String.self, forKey: .id)
		title = try container.decode(String.self, forKey: .title)
		description = try container.decode(String.self, forKey: .description)
		makers = try container.decode([Maker].self, forKey: .makers)
		let imageContainer = try container.nestedContainer(
			keyedBy: ImageKeys.self,
			forKey: .imageURL
		)
		imageURL = try imageContainer.decode(URL.self, forKey: .url)
		var colorsContainer = try container.nestedUnkeyedContainer(forKey: .colors)
		var colors: [String] = []
		while !colorsContainer.isAtEnd {
			let colorContainer = try colorsContainer.nestedContainer(keyedBy: ColorKeys.self)
			let color = try colorContainer.decode(String.self, forKey: .hex)
			colors.append(color)
		}
		self.colors = colors
	}
}

The decoding code above combines all the techniques we have seen until now.

  • It uses coding keys to rename the members of the JSON object;
  • It flattens the webImage object using a nested keyed container;
  • It flattens the contents of the normalizedColors array using a nested unkeyed container.


Further reading:

Chapter 3

Building a SwiftUI app that downloads and parses JSON data

Parsing JSON is only a single piece of an overall SwiftUI app structure. When building an app that fetches and parses JSON data, there are several other moving parts you need to consider.

You can download the Xcode project on GitHub.


In this chapter:

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

Fetching sample model data from a REST API

One of the first steps in developing a SwiftUI app that uses JSON is fetching sample data to build and test your decoding code.

Most of your JSON data will come from a REST API. Many APIs show examples of the data they return in their documentation. You can often open a resource’s URL directly in your browser to see what data it returns.

Some APIs require a personal API key before you can request data. The Rijksmuseum API I use in this article requires you to add your API key to any resource URL. You can get one by creating a free Rijksstudio account.

When an API requires authentication or extra HTTP parameters, your browser is insufficient, and you need a more sophisticated solution.

Running cURL is a standard option, and some API documentation gives commands you can copy and paste directly into the terminal. Graphical user interface clients like Postman or Bruno are generally a more convenient solution.

REST APIs often strip all the white space to save bandwidth, making JSON data hard to read for humans. If that’s the case, pass it through an online JSON formatter.


Further reading:

Saving preview data in the app’s main bundle 

After grabbing some test data from your API, you can save it in a .json file in the Preview Content folder of your Xcode project.

In our example, I created Result.json and Painting.json files containing the data for a search of the top landscape paintings in the Rijksmuseum and the details of a specific painting.

You often need to use a specifically configured instance of JSONDecoder in several places in your project. It’s convenient to store such an instance in a static property inside an extension.

extension JSONDecoder {
	static let rijksMuseumDecoder: JSONDecoder = {
		let decoder = JSONDecoder()
		decoder.dateDecodingStrategy = .custom { decoder in
			let container = try decoder.singleValueContainer()
			let dateString = try container.decode(String.self)
			let formatter = DateFormatter()
			formatter.dateFormat = "yyyy-MM-dd"
			if let date = formatter.date(from: dateString) {
				return date
			}
			formatter.dateFormat = "yyyy"
			return formatter.date(from: dateString)!
		}
		return decoder
	}()
}

When you need to read several files with different content, extending the Bundle class with a generic method is also convenient.

	func decode(resource: String, withExtension extension: String) -> T {
		let url = url(forResource: resource, withExtension: `extension`)!
		let data = try! Data(contentsOf: url)
		return try! JSONDecoder.rijksMuseumDecoder.decode(T.self, from: data)
	}
}

Finally, we can create some test data we can use in our SwiftUI previews.

extension Result {
	static let preview: Self = Bundle.main.decode(resource: "Result",withExtension: "json")
}

extension Painting {
	static let preview: Self = Bundle.main.decode(resource: "Painting",withExtension: "json")
}

Further reading:

Creating basic SwiftUI views that consume JSON data

Now that we have our JSON test data converted into Swift values for our previews, we can start building the user interface for our app.

We can start by building a view for the entries in the list of top landscape paintings.

struct LandscapeCard: View {
	let artObject: ArtObject

	var body: some View {
		VStack(alignment: .leading) {
			LoadingImage(url: artObject.imageURL)
			Group {
				Text(artObject.title)
					.font(.headline)
					.padding(.top, 8.0)
				Text(artObject.author)
					.foregroundStyle(.secondary)
					.padding(.bottom, 16.0)
			}
			.padding(.horizontal)
		}
		.background(Color.gray.quinary)
		.clipShape(RoundedRectangle(cornerRadius: 16.0))
	}
}

#Preview {
	LandscapeCard(artObject: .preview)
		.padding()
}

extension ArtObject {
	static let preview: Self = Result.preview.artObjects[0]
}

struct LoadingImage: View {
	let url: URL

	var body: some View {
		AsyncImage(url: url) { image in
			image
				.resizable()
				.aspectRatio(contentMode: .fit)
		} placeholder: {
			ProgressView()
				.frame(maxWidth: .infinity)
		}
	}
}

Images in JSON are usually represented by URLs, so the AsyncImage view is a common occurrence in SwiftUI apps that display JSON data.

Colors are often encoded as hexadecimal strings, so we need to convert them into Color views to display in our user interface.

struct ColorGrid: View {
	let colors: [String]
	
    var body: some View {
		HStack {
			ForEach(colors, id: \.self) { color in
				Color(hex: color)
					.frame(height: 36.0)
			}
		}
    }
}

extension Color {
	init(hex: String) {
		let hex = hex.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
		var int: UInt64 = 0
		Scanner(string: hex).scanHexInt64(&int)
		self.init(
			.sRGB,
			red: Double(int >> 16) / 255,
			green: Double(int >> 8 & 0xFF) / 255,
			blue:  Double(int & 0xFF) / 255,
			opacity: 1.0
		)
	}
}

#Preview {
	ColorGrid(colors: Painting.preview.colors)
		.padding()
}

We also need a view to represent the makers of a painting that can handle the missing information.

struct MakerLabel: View {
	let maker: Maker

    var body: some View {
		Label(
			title: {
				VStack(alignment: .leading) {
					Text(maker.name)
					Text(optional: maker.placeOfBirth) 
					+ Text(" - ")
					+ Text(optional: maker.dateOfBirth?.formatted(.dateTime.year()))
					Text(optional: maker.placeOfDeath) 
					+ Text(" - ")
					+ Text(optional: maker.dateOfDeath?.formatted(.dateTime.year()))
				}
			},
			icon: {
				Image(systemName: "paintpalette")
			}
		)
		.foregroundStyle(.secondary)
    }
}

extension Text {
	init(optional: String?) {
		self.init(optional ?? "")
	}
}

#Preview {
	MakerLabel(maker: Painting.preview.makers[0])
}

Fetching the app’s data from the resource URLs of a REST API

We can now assemble the main views for our app, which need to fetch the JSON data from the API, decode it, and display it on screen.

Let’s start with the view that displays the top landscape paintings.

struct TopLandscapesView: View {
	@State private var landscapes: [ArtObject] = []

	var body: some View {
		ScrollView {
			LazyVStack(spacing: 16.0) {
				ForEach(landscapes) { landscape in
					NavigationLink(value: landscape) {
						LandscapeCard(artObject: landscape)
					}
					.buttonStyle(.plain)
				}
			}
		}
		.contentMargins(20.0, for: .scrollContent)
		.navigationTitle("Landscapes")
		.task { try? await fetchLandscapes() }
	}

	func fetchLandscapes() async throws {
		let url = URL(
			string: "https://www.rijksmuseum.nl/api/en/collection?key=[your-key]&q=landscape&toppieces=true&type=painting"
		)!
		let (data, _)  = try await URLSession.shared.data(from: url)
		let result = try JSONDecoder.rijksMuseumDecoder.decode(Result.self, from: data)
		landscapes = result.artObjects
	}
}

#Preview {
	NavigationStack {
		TopLandscapesView()
	}
}

Beware that the code above does not work as is because I cannot share my Rijksstudio API key for security reasons.

The fetchLandscapes() asynchronous function fetches the list of art objects from the Rijksmuseum API, decodes the returned JSON data, and refreshes the view’s content by updating the landscapes state property.

The view fetching the details of a single painting works the same way.

struct PaintingView: View {
	let artObject: ArtObject
	@State private var details: Painting?

	var body: some View {
		ScrollView {
			if let details {
				VStack(alignment: .leading) {
					LoadingImage(url: details.imageURL)
					VStack(alignment: .leading, spacing: 16.0) {
						Text(details.title)
							.font(.title)
							.bold()
						ForEach(details.makers) { maker in
							MakerLabel(maker: maker)
						}
						ColorGrid(colors: details.colors)
						Text(details.description)
					}
					.safeAreaPadding()
				}
			}
		}
		.navigationTitle("Landscape Details")
		.task { try! await fetchDetails() }
	}

	func fetchDetails() async throws {
		let url = URL(string: "https://www.rijksmuseum.nl/api/en/collection/\(artObject.id)?key=[your-key]")!
		let (data, _)  = try await URLSession.shared.data(from: url)
		details = try JSONDecoder.rijksMuseumDecoder.decode(Painting.self, from: data)
	}
}

#Preview {
	NavigationStack {
		PaintingView(artObject: Result.preview.artObjects[0])
	}
}

For simplicity’s sake, I put the fetching asynchronous functions directly inside the views. In a real SwiftUI app, I would instead have used the MVMM pattern and placed the asynchronous code inside a view model.


Further reading:

If you want a complete reference of all the techniques to parse JSON data in Swift, including those in this article and many others, 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

18 thoughts on “Parsing JSON in Swift: The Complete Guide [With Examples]”

  1. What if I’m using Core data? Do I still need to make a model(struct) file when my entities and attributes are essentially my model? How do I go about it? Thanks.

    Reply
    • The quick answer is that you can use the same approach with the managed objects of Core Data. The Codable protocols work with any type, including classes.

      The most complex answer requires a longer discussion about architecture, on wheter you should let managed objects spread around your code, or you should isolate Core Data behind a model controller that transforms them into structures.

      It’s a question I get sometimes. Hopefully I’ll get to write something about it someday.

      Reply
  2. Hi matteo, what a great and detailed tutorial for Codable using swift. You are my man! Keep posting such wonderful content. Long Live and cheers!

    Reply
  3. This is the best tutorial I have read about the JSON parsing. It emphasizes all the crucial points very clear and understandable way. Great job. Thanks Matteo…

    Reply
  4. Great Article, here you mean willDisplay.
    “When a cell appears, the table view calls tableView(_:cellForRowAt:) on its delegate. That’s where we perform network requests.”

    Reply
  5. You have the ability to read minds of the readers it feels. Wonderful and some really useful suggestions all along this amazing tutorial. I have personally avoided reading your post because it was a long one but it did not disappoint me once I read. I felt confident that I had the proper understanding of the concepts . Thanks Matteo.

    Reply
  6. Hello Matteo, Thanks for your always on point tutorials, recently i ran into a wall on an app that i am building with SwiftUI, I have a lot of Json data stored locally and my CodableBundleExtension can handle everything but as the application gets bigger with more pages and data it starts to crash because of memory usage. I have checked online for weeks on a way to solve the issue with no solution, please can you help pointing me to the right path on solving this issue?. Thanks.

    Reply
    • It’s hard for me to say what the right solution could be since it depends on how you app works and how your code is structured.

      My assumption is that you are storing everything in a single JSON file that is getting too big to decode. You probably don’t need all that data at once, so you should maybe split it across separate files that you can read independently.

      If you need anything more sophisticated than that, e.g., you need to query your data, then JSON is probably not the right solution. You need a database, so look into either Core Data or SQLite.

      Reply
      • Thanks for your reply, it is much appreciated. I already did try splitting acrossseparate json files, still didnt work. On taking on your advice i have started looking into Sqlite and Core Data, will keep you updated on my progress. Thanks a million Matteo.

        Reply
  7. The Codable stuff for custom parsing is great. Very useful and well-written. Thanks!

    One thing isn’t covered, though, as far as I can tell: How to handle nulls in the structure that’s being decoded. For example, doing this in init(from: Decoder):

    missionName = try container.decode(String.self, forKey: .missionName)

    Causes the entire init to fail if missionName is null. This will be very common, as databases are often full of columns that permit null.

    Reply
  8. Great article! Thank you for writing and sharing.
    One possible nit: “They are not concerned with data storage of networking” Should that ‘of’ read ‘or’?

    And a question: In NetworkController.fetchPatch() the images are assigned directly to the array entries:
    self?.launches[index].patch = UIImage(data: data)

    Whereas in the preview code (ie. struct TestData) they are first assigned to a local copy and the local copy is assigned to the array entry:
    for (index, var launch) in launches.enumerated() {
    launch.patch = UIImage(named: “\(launch.id)”)
    launches[index] = launch
    }
    Question is: Was this intentional and is there any reason to prefer one over the other? Thank you in advance.

    Reply
    • There is no difference; I sometimes use intermediate constants to make the code more readable. The inconsistency is probably due to some refactoring before publishing the article.

      However, that was the old way of downloading images from an API. Nowadays, it’s better to store the URL in a model type and use an AsyncImage view. I have just finished rewriting this article. I will publish the update soon with the new approach.

      Reply

Leave a Comment