wvteijlingen / Spine

A Swift library for working with JSON:API APIs. It supports mapping to custom model classes, fetching, advanced querying, linking and persisting.
MIT License
264 stars 109 forks source link

How to get relationships data from JSONAPI document top-level "included" #162

Closed Arcovv closed 7 years ago

Arcovv commented 7 years ago

Environment

Xcode Version 8.2.1 (8C1002) Swift 3.0

Code

There's my json data format:

{
  "data": {
    "type": "library_items",
    "id": "1",
    "attributes": {
      "action": "download",
      "new": false,
    },
    "relationships": {
      "book": {
        "data": {
          "type": "books",
          "id": "1"
        }
      },
      "reading": {
        "data": {
          "type": "readings",
          "id": "10"
        }
      }
    }
  },
  "included": [
    {
      "type": "publishers",
      "id": "2",
      "attributes": {
        "name": "..."
      },
      "links": {
        "self": "api url"
      }
    },
    {
      "type": "books",
      "id": "1",
      "attributes": {
        "title": "...",
        "author": "..."
      },
      "relationships": {
        "publisher": {
          "data": {
            "type": "publishers",
            "id": "2"
          }
        }
      },
      "links": {
        "self": "api url"
      }
    },
    {
      "type": "readings",
      "id": "10",
      "attributes": {
        "state": "reading",
        "privacy": "everyone",
      },
      "links": {
        "self": "api url"
      }
    }
  ]
}

There's my Resource classes:

final class LibraryItemResource: Resource {

  // MARK: - Attributes

  public var action = ""
  public var new = false
  public var books: BookResource!
  public var readings: ReadingResource!

  // MARK: - Override properties

  public override class var resourceType: ResourceType {
    return "library_items"
  }

  public override class var fields: [Field] {
    return fieldsFromDictionary([
      "action": Attribute(),
      "new": BooleanAttribute(),
      "books": ToOneRelationship(BookResource.self),
      "readings": ToOneRelationship(ReadingResource.self)
    ])
  }

}
final public class BookResource: Resource {

  // MARK: - Attributes

  public var title = ""
  public var author = ""

  // MARK: - Override properties

  public override class var resourceType: ResourceType {
    return "books"
  }

  public override class var fields: [Field] {
    return fieldsFromDictionary([
      "title": Attribute(),
      "author": Attribute()
    ])
  }

}
final public class ReadingResource: Resource {

  // MARK: - Attributes

  public var state = ""
  public var privacy = ""

  // MARK: - Override properties

  public override class var resourceType: ResourceType {
    return "readings"
  }

  public override class var fields: [Field] {
    return fieldsFromDictionary([
      "state": Attribute(),
      "privacy": Attribute()
      ])
  }

}

For test, I implemented my code like this:

var query = Query(resourceType: LibraryItemResource.self, path: "/.../my path")
query.include("books", "readings")

spine.find(query)
  .onSuccess() { resources, meta, jsonapi in
    printf("resources count: \(resources.count)")

    for resource in resources.resources {
      let libraryItemResource = resource as! LibraryItemResource
      printf("books: \(libraryItemResource.books)")
      printf("readings: \(libraryItemResource.readings)")
    }

    printf("meta: \(meta)")
    printf("jsonapi: \(jsonapi)")
  }.onFailure() { error in
    printf(error)
}

The spine had call my onSuccess callback and gave me some resources.

But in the for-in loop, libraryItemResource.books and libraryItemResource.readingsare always nil.

I have verified that query.include("books", "readings") is not what I expected to use.

But I still don't know how to get the data from included json and assign them to the libraryItemResource.

Thanks for your help!

wvteijlingen commented 7 years ago

The attributes you defined in the LibraryItemResource are called books and readings, but they are called book and reading in your JSON. They should match. Because this is a to-one relationship, the singular form is probably correct.