Saving Data in iOS

May 31 2022 · Swift 5.5, iOS 15, Xcode 13

Part 2: JSON

11. JSON Decoding

Episode complete

Play next episode

Next
About this episode

Leave a rating/review

See forum comments
Cinema mode Mark complete Download course materials
Previous episode: 10. Introduction Next episode: 12. Challenge: Decoding JSON Arrays

Get immediate access to this and 4,000+ other videos and books.

Take your career further with a Kodeco Personal Plan. With unlimited access to over 40+ books and 4,000+ professional videos in a single subscription, it's simply the best investment you can make in your development career.

Learn more Already a subscriber? Sign in.

Notes: 11. JSON Decoding

Apple’s documentation on Encoding and Decoding Custom Types

This course was originally recorded in April 2020. It has been reviewed and all content and materials updated as of November 2021.

Heads up... You've reached locked video content where the transcript will be shown as obfuscated text.

This video is where you'll learn about the JSON file format, or as some people pronounce it, JSON. Regardless of how you say it, JSON is currently a very popular format for transmitting data over the web. Working with JSON supplied by a web service is a frequent task in app development. Additionally, JSON is also a viable solution for general purpose data saving in iOS. The term JSON is used somewhat confusingly both for the data format and colloquially as the data you'll store in that format. For example, I just downloaded some fabulous JSON. JSON is an acronym for JavaScript Object Notation. That's where the JS acronym, often how JavaScript itself is abbreviated comes from. What JavaScript calls an object is the same concept as a heterogeneous Swift dictionary with strings for keys. Heterogeneous just means that the values can be of more than one type, of multiple types. Another term for this kind of data structure is associative array but neither Swift nor JavaScript use these terms. In Swift, you use the same angle bracket syntax for arrays and dictionaries. JavaScript arrays also use angle brackets but even though JS objects are more like dictionaries than Swift classes or structures, their syntax involves being wrapped up in curly braces. Aside from that, their syntax is just like Swift dictionary literals. JSON supports only a few data types natively. Your basic Booleans, numbers and strings are all available and as long as they hold the supported types, so are arrays. The same goes for dictionaries with string keys. Data is not directly supported, instead, you can store a data bytes in encoded strings and dates in properly formatted strings. It's demo time. From now on, you'll be working with a proper Xcode project that builds an iOS app of tasks or reminders. To familiarize yourself with the app, open the task list Xcode project and run it in an iPhone simulator of your choice. Note how you have a list of tasks already created for you, each within different categories of priority. You can swipe to delete or enter edit mode or tap the plus button to create new tasks. Add one or two tasks, delete some as well then stop the app and run it again. What happened? Well, as of now, the app is using hard coded data, which means that none of your changes are being persisted or saved between app launches. Now it's time to look at how the app is structured. This is a regular Swift UI app but of interest to you are the items in the Models folder and some of the items in the Views folder. One of the model objects is the task Swift structure that adheres to the identifiable protocol to work correctly and seamlessly with Swift UI and define some properties. You also have an extension of task for an enumeration called priority. This is how you can categorize and prioritize your tasks. Next up, there's the PrioritizedTasks structure that contains an array of tasks along with a priority for them. The extension in this file is so there is a unique identifier for our prioritized task. Finally, you have a TaskStore Swift structure that stores and manages your data. This is how the app will interface with your data. The store contains a property for an array of PrioritizedTasks and a helper method to get the index in the PrioritizedTasks array that matches the priority passed to the method as a parameter. There is also a private extension for another model object, the PrioritizedTasks structure to help with its initialization. Moving on to some of the views, let's focus on content view for now. This is a Swift UI view that in its body creates a navigation view with a list of tasks. The list is separated into sections, each one corresponding to a priority and then the section contents have the actual tasks for that priority. The body of this view also adds the edit and add buttons to the appropriate interactions. One final thing I wanted to draw your attention to are the two JSON files in the project navigator, Task.json and PrioritizedTask.json. Each file corresponds to a single object of each type represented in JSON. The structure looks very similar to Swift dictionaries but instead of square brackets, you use curly braces. Task.json is a dictionary representing a single task object with a key value pair for ID, name and completed. PrioritizedTask.json is very similar with the interesting thing being the tasks key with what corresponds to a JSON array of tasks. That was at a high level, an overview of the app, the model objects, some views and what JSON files look like. In the next few videos, you'll go from loading data from a file in your app bundle to loading and saving your data from your app's document directory. One step at a time though. For now you'll practice decoding those JSON files into native Swift types and ensuring everything gets correctly set up in your model. Open ContentView.swift and add a private method called loadJSON. The first thing you wanna do in this method is to get the URLs for each of the JSON files. For that, you'll use bundles URL for resource with extension method. Bundle is a representation of your app, its code and resources. All the files you add in Xcode to your app target are packaged up into what is referred to as the app bundle. Main represents the current executable, in this case your app. So by asking your main bundle for the URL for a given resource and assuming it's located in the bundle, you will get back the URL where the file is located. The forResource parameter is the name of the file you want the URL for. With extension is the file extension of the file. Add another let to get the URL for the PrioritizedTask JSON file. You do this in our guard statement as there isn't much you can do without these URLs. After the guard statement, if you're able to get the URLs, it's time to create a JSONDecoder object. JSONDecoder helps you decode JSON objects into instances of a given data type. In your case, it will be instances of task in PrioritizedTask. The steps to achieve this are to load the contents of your JSON files or Swift data and then decode that data into your types. You use the data initializer that takes your URL but it can throw an exception so you wanna mark the code with a try and also wrap this code in a do catch statement. If there's an error then you print that to the console for debugging purposes. Do the same for the PrioritizedTasks file. If everything is correct so far, you should not see any errors in Xcode, just a couple of warnings. This also means you have two data objects that need to be decoded from JSON data to a Swift type. Start with the task data by using the decode method. The first parameter is the type to decode from your data. You're loading a task so using Task.self will tell that to the method. The from parameter is the JSON object to decode. This method also throws an exception, so be sure to mark it with try accordingly. Everything should be fine, right? So why is Xcode saying that task doesn't conform to codable? Well, even though the JSON file exactly matches a task object, you need to have your task type conform to the codable protocol for it to be able to convert to and from a different representation. Because you're using JSON decoder and its decode method, the representation is JSON. Open Task.swift, make your task structure conform to codable. By default, you don't need to implement any methods for your types to conform to codable. As long as all the types used within a Swift type are also codable, then you get a ton of functionality out of the box. Switch back to ContentView.swift, do a build by going to Product, Build or hitting the Command + B shortcut. Note that in content view, there is no longer an error when trying to decode your data, great work. To be able to view what got loaded, go ahead and print your task object. Right now there is no place that calls this method to load your JSON data so if we were to run the app, you would not notice anything different or see the print statement in the Debug pane in Xcode. Fix that by adding the onAppear method to your content views body and build and run your app. Awesome, if you look at the Task.json file and compare it to the output in the Xcode console, you'll see that they are identical. I'm already getting excited with the possibilities that this will open up, no more forgotten wallet or sunglasses, yay. The one thing to note is that even though you're loading your task from the Task.json file, the IDs are different because ID is being initialized every time we create a new task item. So for now, don't worry about it but know that it is not loading the ID from the task JSON file. Back in content view, in your loadJSON method, do the same to load the PrioritizedTask data. An identical process except that you decode the PrioritizedTask data into an object of type PrioritizedTask. Don't forget to make PrioritizedTask conform to the codable protocol so you can decode and encode it. Remember, we're using JSON for now. Why is Xcode showing a warning now? PrioritizeTask is now conforming to the codable protocol, isn't it? Well, not quite, priority isn't a native Swift type and thus, it too needs to conform to codable. Build and run your app one last time. No errors, yay. Check out the output in the console. It's not the prettiest looking console output but you can see that the PrioritizedTask directly matches the JSON data in your bundles file, great work.