SICT

DPS923 & MAP523

Mobile App Development for iOS

Notes Topics Weekly Resources Graded work Professor Code examples

Web API and data structures

This document has the general rules and guidelines for writing Swift structs or classes that enable JSON to be decoded or encoded.

Read/skim (then refer to when you need to) the Structures and Classes section of the Swift Language Guide.


struct or class?

In general:

The examples below will show typical coding approaches. Each use a struct, but the needs of your app may cause you to use a class instead. They cover the basics, and then you can build upon them to handle a wide variety of web API responses.


Name of the struct or class?

To prevent name collisions with entity and other classes that you will create in your app, we recommend that you use a two- or three- character prefix for all your web API data structures.

That will enable you to have, for example, an APICustomer struct that defines the web API data structure, and a Customer (entity) class in your app. This naming convention makes the role of each very clear.


Properties… var or let?

Use common sense. If you have all the property values when the object is created, let works fine. Otherwise, declare it as var (and then you must initialize…).


Initialized with a value?

A property declared with var or let can be configured with an initial value. Recall the difference:

let name: String
// ...or...
let name: String = "Peter McIntyre"
// ...or...
var name: String = "Peter McIntyre"
// ...or...
let customers = [Customer]()

Alternatively, if you declare a data structure with var members, you must write an initializer.


Property declarations

The type of a property value can be declared as optional. In that situation, the decoder will safely tolerate JSON that is missing that property.

Must you code all incoming properties in your Swift struct or class? No. Write code for the properties that are needed. You can ignore the rest.

However…
For sending (POST or PUT), you MUST send all the data that the web API expects. Otherwise, the request will cause an HTTP 400-series response.


Incoming object

Assume that the incoming JSON is an object:

{
  "version": 2.3,
  "timestamp": "2019-11-22T15:38:55Z",
  "id": 345,
  "name": "Peter McIntyre",
  "jobTitle": "Professor",
  "schoolIdentifier": 37
}

What is the Swift struct?

struct APIEmployee: Codable {
  let version: Double
  let timestamp: Date
  let id: Int
  let name: String
  let jobTitle: String
  let schoolIdentifier: Int
}


Incoming collection

Assume that the incoming JSON is an array (of objects):

[{"id":1,"firstName":"Ruthie","lastName":"Kellert","city":"Shediac","province":"New Brunswick","ip_address":"194.146.180.248"},
{"id":2,"firstName":"Calida","lastName":"Tynewell","city":"Stonewall","province":"Manitoba","ip_address":"52.163.147.254"},
{"id":3,"firstName":"Cordy","lastName":"Woolham","city":"Ucluelet","province":"British Columbia","ip_address":"39.20.8.60"}]


What is the Swift struct?

struct APICustomer: Codable {
  let id: Int
  let firstName: String
  let lastName: String
  let city: String
  let province: String
  let ip_address: String
}

Then, the type that’s passed in to the closure function will be an array: [Customer]


Incoming object with collection

Assume that the incoming JSON is a package that includes an array of objects:

{
  "version": "1.0.0",
  "timestamp": "2019-11-22T15:38:55Z",
  "count": 34,
  "data": [
    {"id":1,"firstName":"Ruthie","lastName":"Kellert","city":"Shediac","province":"New Brunswick","ip_address":"194.146.180.248"},
    {"id":2,"firstName":"Calida","lastName":"Tynewell","city":"Stonewall","province":"Manitoba","ip_address":"52.163.147.254"},
    {"id":3,"firstName":"Cordy","lastName":"Woolham","city":"Ucluelet","province":"British Columbia","ip_address":"39.20.8.60"}
    ]
}


What is the Swift struct? First, define any nested or embedded object or collection. Then, define the package.

struct APICustomer: Codable {
  let id: Int
  let firstName: String
  let lastName: String
  let city: String
  let province: String
  let ip_address: String
}

struct APICustomerPackage: Codable {
  let version: String
  let count: Int
  let timestamp: Date
  let data: [APICustomer]
}


Incoming object with nested/embedded object

Assume that the incoming JSON is a package that includes a nested/embedded object (or objects):

{
  "version": "1.0.0",
  "timestamp": "2019-11-22T15:38:55Z",
  "count": 34,
  "token": {
    "required": false
  },
  "link": {
    "href": "https://example.com/api/customers",
    "rel": "self",
    "method": "GET"
  }
  "data": [
    {"id":1,"firstName":"Ruthie","lastName":"Kellert","city":"Shediac","province":"New Brunswick","ip_address":"194.146.180.248"},
    {"id":2,"firstName":"Calida","lastName":"Tynewell","city":"Stonewall","province":"Manitoba","ip_address":"52.163.147.254"},
    {"id":3,"firstName":"Cordy","lastName":"Woolham","city":"Ucluelet","province":"British Columbia","ip_address":"39.20.8.60"}
    ]
}

The “token” property’s value is an object.
And, the “link” property’s value is an object.

What is the Swift struct? First, define any nested or embedded object, then, define the packaging.

// Define the embedded object
struct APICustomerPackageToken: Codable {
  let required: Bool
}

// Define the embedded object 
struct APICustomerPackageLink: Codable {
  let href: String
  let rel: String
  let method: String
}

// As before, define the embedded collection 
struct APICustomer: Codable {
  let id: Int
  let firstName: String
  let lastName: String
  let city: String
  let province: String
  let ip_address: String
}

// Finally, define the package 
struct APICustomerPackage: Codable {
  let version: String
  let count: Int
  let timestamp: Date
  let token: APICustomerPackageToken
  let link: APICustomerPackageLink
  let data: [APICustomer]
}


Anything else I should know?

Yes, plenty. Consult the Apple guides and reference documentation for a complete treatment.