© Jesse Feiler 2019
Jesse FeilerImplementing iOS and macOS Documents with the Files Apphttps://doi.org/10.1007/978-1-4842-4492-0_2

2. Looking Inside a Document

Jesse Feiler1 
(1)
Plattsburgh, NY, USA
 

In Chapter 1, you learned how to describe and structure a document. You now know that you, as the designer and developer of an app and its documents, control what data is stored, where and how it is stored, and how to identify and reference it.

You can decide that the data will be stored as a sequence of integers or as a single long string, whatever matters to you and the data you will use. In practice, it makes sense to structure the data inside a document if only to be able to access it easily. This chapter shows how to structure the data within a document using JSON encoding. This structure and encoding provides an easy-to-use format for data that relies on Unicode strings that can represent basic types recognized by JSON.

Using JSON Encoding

What matters most for JSON is the fact that the format is text-based (as opposed, for example, to a binary or digital representation) and the fact that each element can be named (as opposed to being identified by location or sequence).

A location- or sequence-based coding style lets you specify the format of each element in the encoding sequence. Knowing the format of an element means that you know how much space it will take up, and this will let you read or write the data using the standard read/write syntax in any programming language.

The disadvantage of sequence- or location-based coding is that if you change the sequence of data elements or the format of a data element, you break any read/write code that you already have. JSON encoding relies on names of data elements rather than their formats or sequence. Thus, you avoid the frequent problem of breaking read/write code when you modify a format of a single data element or when you change the order of the data elements.

Introducing JSON

JSON starts as a text format for serialization of structured data. In this sense, serialization means converting the strings or other objects into a format that can be read or written. JSON starts from four primitive types, the meanings of which are common to many programming languages:
  • A string is an ordered collection of Unicode characters.

  • A number is just that; the most basic JSON number is a double.

  • A Boolean is true or false.

  • The final primitive value in JSON is null, an object that has no value.

In JSON, these types can be combined into objects, which are unordered collections of name/value pairs; a JSON array is an ordered collection of name/value pairs.

JSON and Swift

Swift goes beyond the basic JSON types with its JSONSerialization class (part of the Foundation framework). JSONSerialization converts JSON into array and dictionary Swift data types in addition to the basic JSON string, number, and Bool data types.

Note

Swift bridges Boolean and bool (C) types into Bool types. This is handled automatically for you.

Using Swift Structs

JSON is a flexible and easy-to-use notation tool. On the other hand, Swift is designed to be a powerful tool for building apps, particularly those using the model-view-controller (MVC) design pattern, which is more complex than JSON. One area that demonstrates this well is the Swift struct type. You may often declare structs in Swift that you will use throughout your app (or not at all). When you work strictly with JSON, it is uncommon to declare a struct that is not used to store data. This section explains how to create and use Swift structs with JSON.

Listing 2-1 shows how to create a Swift struct for a Student object or model (the terms are interchangeable in this section) using a playground.
import Foundation
struct Student {
  var name: String
  var studentID: Int
}
Listing 2-1

Swift Struct

What matters here is that the Student struct contains two var elements: name and studentID. Also worth noting is the fact that in this playground the Foundation framework must be imported because it will be used for working with JSON data. The other elements of the struct are standard Swift elements.

Tip

Note that the Swift style is to capitalize names of objects such as structs, so the name of the Student struct is capitalized.

With the struct shown in Listing 2-1, you can create an instance of the struct using code such as the following:
let student1 = Student(name: "John Appleseed", studentID: 154)
You can integrate JSON with Swift by using an encode (to: encoder) function to encode data along with an init (from decoder:) to do the reverse. To do this, you need to create keys to identify the elements that you will be coding and decoding. The first step is to declare coding keys as an enum CodingKeys element, as shown in Listing 2-2.
  enum CodingKeys: String, CodingKey {
    case studentID = "studentID"
    case name
  }
}
Listing 2-2

Swift Extension for Coding Keys

Note that they are the keys you will use to encode and decode the data for the name and studentID variables. With the keys established along with the variables, you can now create an encode (to: encoder) function, as shown in Listing 2-3. Note that this extension indicates that the Student struct conforms to the Encodable protocol.
extension Student: Encodable {
  func encode(to encoder: Encoder) throws {
    var container = encoder.container(keyedBy: CodingKeys.self)
    try container.encode(name, forKey: .name)
    try container.encode(studentID, forKey: .studentID)
  }
}
Listing 2-3

Swift Extension for Encoding

Encoding JSON

You’ll need a JSONEncoder object to handle encoding. Such a JSONEncoder object is often named jsonEncoder but you can use any name you want. A JSONEncoder object specifies the container for the encoded data.

In Listing 2-3, there is only one function, encode (to: encoder), and it uses a jsonEncoder object, the container that will contain the encoded data retrieved from the encoder.

The heart of the encode (to: encoder) function consists of the three lines of code that encode the struct elements (name and studentID).

The first of these lines tries to encode the name var using the .name key:
    try container.encode(name, forKey: .name)
The second of these lines tries to encode the studentID var using the .studentID key:
    try container.encode(studentID, forKey: .studentID)
These lines of code appear frequently in this type of function. In case you are wondering how the try is handled, note that the
func encode(to encoder: Encoder)function

can throw an error.

Tip

If you are debugging this code, set a breakpoint on the try statement so that you can see what causes a problem. Typical problems you may encounter in this code are typos in the key names.

Once you have created a jsonEncoder, you can reference the container within it and associate it with keys that you have declared elsewhere in the function with this line of code:
    var container = encoder.container(keyedBy: CodingKeys.self)

Decoding JSON

Listing 2-4 shows the reverse operation to decode the JSON code.
extension Student: Decodable {
  init(from decoder: Decoder) throws {
    let values = try decoder.container(keyedBy: CodingKeys.self)
    name = try values.decode(String.self, forKey: .name)
    studentID = try values.decode(Int.self, forKey: .studentID)
  }
}
Listing 2-4

Extension for Decoding

Rather than an encode(to: encoder:) function, the heart of this code is init(from decoder: Decoder).

You retrieve the values from the decoder container using the coding keys, like so:
    let values = try decoder.container(keyedBy: CodingKeys.self)
Rather than encode each variable and key, you then decode them using code such as the following:
    name = try values.decode(String.self, forKey: .name)
    studentID = try values.decode(Int.self, forKey: .studentID)

Putting the Encoding and Decoding Together

You can encode and decode the data as you wish. For the purpose of debugging, you can print out encoded data using code such as the following:

../images/465467_1_En_2_Chapter/465467_1_En_2_Figa_HTML.jpg

You can print out a string showing the JSON code:

../images/465467_1_En_2_Chapter/465467_1_En_2_Figb_HTML.jpg

You can then reverse the process to print out the decoded data, as you see here:

../images/465467_1_En_2_Chapter/465467_1_En_2_Figc_HTML.jpg

Note

For debugging, you may want to add the code in this section to your app so that you can use breakpoints to verify the encoding and decoding of the data. Usually once it is working and the keys are correct, you can disable the breakpoints or even remove them.

Summary

This chapter showed you how to encode and decode JSON data to and from Swift structs. Because you will be working with named Swift objects, you don’t have to worry about the sequence or formatting of data.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.136.154.103