Skip to content

Latest commit

 

History

History
134 lines (108 loc) · 4.2 KB

File metadata and controls

134 lines (108 loc) · 4.2 KB

JSONAPI Compound Example

We are about to walk through an example to show how easy it is to parse JSON:API includes. Information on creating models that take advantage of more of the features from the JSON:API Specification can be found in the README.

We will begin by quickly redefining the same types of ResourceObjects from the Basic Example.

typealias Resource<Description: JSONAPI.ResourceObjectDescription> = JSONAPI.ResourceObject<Description, NoMetadata, NoLinks, String>

struct PersonDescription: ResourceObjectDescription {
  
  static let jsonType: String = "people"
  
  struct Attributes: JSONAPI.Attributes {
    let firstName: Attribute<String>
    let lastName: Attribute<String>
    
    let age: Attribute<Int?> 
  }
  
  struct Relationships: JSONAPI.Relationships {
    let pets: ToManyRelationship<Dog, NoIdMetadata, NoMetadata, NoLinks>
  }
}

typealias Person = Resource<PersonDescription>

struct DogDescription: ResourceObjectDescription {
  static let jsonType: String = "dogs"
  
  struct Attributes: JSONAPI.Attributes {
    let name: Attribute<String>
  }
  
  typealias Relationships = NoRelationships
}

typealias Dog = Resource<DogDescription>

Next we will create similar typealiases for single and batch documents as we did in the Basic Example, but we will allow for an include type to be specified.

/// Our JSON:API Documents will still have no metadata or links associated with them but they will allow us to specify an include type later.
typealias SingleDocument<Resource: ResourceObjectType, Include: JSONAPI.Include> = JSONAPI.Document<SingleResourceBody<Resource>, NoMetadata, NoLinks, Include, NoAPIDescription, UnknownJSONAPIError>

typealias BatchDocument<Resource: ResourceObjectType, Include: JSONAPI.Include> = JSONAPI.Document<ManyResourceBody<Resource>, NoMetadata, NoLinks, Include, NoAPIDescription, UnknownJSONAPIError>

Now let's define a mock response containing a single person and including any dogs that are related to that person.

// snag Foundation for Data and JSONDecoder
import Foundation

let mockSinglePersonResponse = 
"""
{
  "data": {
    "type": "people",
    "id": "88223",
    "attributes": {
      "first_name": "Lisa",
      "last_name": "Offenbrook",
      "age": null
    },
    "relationships": {
      "pets": {
        "data": [
          {
            "type": "dogs",
            "id": "123"
          },
          {
            "type": "dogs",
            "id": "456"
          }
        ]
      }
    }
  },
  "included": [
    {
      "type": "dogs",
      "id": "123",
      "attributes": {
        "name": "Sparky"
      }
    },
    {
      "type": "dogs",
      "id": "456",
      "attributes": {
        "name": "Charlie Dog"
      }
    }
  ]
}
""".data(using: .utf8)!

Parsing the above response looks almost identical to in the Basic Example. The key thing to note is that instead of specifying NoIncludes we specify Include1<Dog> below. This does not mean "include one dog," it means "include one type of thing, with that type being Dog." The JSONAPI framework comes with built-in support for Include2<...>, Include3<...> and many more. If you wanted to include both Person and Dog resources (perhaps because your primary Person resource had a "friends" relationship), you would use Include2<Person, Dog>.

let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase

let includeDocument = try! decoder.decode(SingleDocument<Person, Include1<Dog>>.self, from: mockSinglePersonResponse)

The Person is pulled out as before with Document.body.primaryResource. The dogs can be accessed from Document.body.includes; note that because multiple types of things can be included, we must specify that we want things of type Dog by using the JSONAPI.Includes subscript operator.

let person = includeDocument.body.primaryResource!.value
let dogs = includeDocument.body.includes![Dog.self]

print("Parsed person named \(person.firstName) \(person.lastName)")
print("Parsed dogs named \(dogs.map { $0.name }.joined(separator: " and "))")