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 GUIDETable of contents
Chapter 1
How to parse JSON data in Swift
To parse JSON data in Swift, follow these two straightforward steps:
- Create one or more
Decodable
Swift types that match the structure of the JSON data; - 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
, andURL
; - Collection types like
Array
,Dictionary
, andSet
.
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 GUIDEUsing 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:
- Coding Keys in Swift with Decodable: How and When to Use Them
- Decode JSON with Dynamic Keys in Swift [Dictionaries and Arrays]
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 GUIDEFetching 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 GUIDEMatteo has been developing apps for iOS since 2008. He has been teaching iOS development best practices to hundreds of students since 2015 and he is the developer of Vulcan, a macOS app to generate SwiftUI code. Before that he was a freelance iOS developer for small and big clients, including TomTom, Squla, Siilo, and Layar. Matteo got a master’s degree in computer science and computational logic at the University of Turin. In his spare time he dances and teaches tango.
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.
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.
anyone looking for generating models from json, online tool for generating swift codable, swiftyJSON, Struct etc.. models jsoncafe.com
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!
As always, your tutorials are detailed and a pleasure to read Matteo. Thank you for producing such high quality content.
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…
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.”
Fixed, thanks.
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.
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.
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.
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.
Great info! Thank you!
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.
Thanks!
To handle missing values, use
decodeIfPresent
instead. It will simply returnnil
instead of throwing an error.Thanks, this fixed the crash I was having! Glad I checked the comments.
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.
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.