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
266 stars 109 forks source link

Relationships and class is not key value coding-compliant #103

Closed jarredszabadi closed 7 years ago

jarredszabadi commented 8 years ago

Hello,

Looking for some help as I get started using spine. Specifically the problem is setting up a relationship between two classes as well as understanding the correct way to name my fields to match with those in the api endpoint.

In this situation the endpoint has:

My problem is that I'm receiving "class is not key value coding-compliant" at different places in the code and setting attributes for the three classes (Gym, Equipment, Device) are throwing undefinedKey errors.

Issue 1: All the attributes in the objects throw `setValue:forUndefinedKey:]: this class is not key value coding-compliant

Issue 2: The arrays which hold the equipment and devices throw the same error.

Here's a snip of the json and the three class models.

class Gym: Resource {

var name: String?

var equipments: LinkedResourceCollection?

override class var resourceType: ResourceType {
    return "organization-locations"
}

override class var fields: [Field] {
    return fieldsFromDictionary([
        "name": Attribute(),
        "equipment": ToManyRelationship(Equipment)
        ])
}

class Equipment: Resource {

var gym: Gym?

var modelNumber: String?

var serialNumber: String?

var devices: LinkedResourceCollection?

override class var resourceType: ResourceType {
    return "equipment-gym-weight-stack-machines"
}

override class var fields: [Field] {
    return fieldsFromDictionary([
        "model-number": Attribute(),
        "serial-number": Attribute(),
        "organization-locations": ToOneRelationship(Gym),
        "devices": ToManyRelationship(Device)
        ])
}

class Device: Resource {

var equipment: Equipment?

var hardwareUID: String?

override class var resourceType: ResourceType {
    return "equipment-device-stack-tracks"
}

override class var fields: [Field] {
    return fieldsFromDictionary([
        "equipment-gym-weight-stack-machines": ToOneRelationship(Equipment),
        "hardware-uid": Attribute()
        ])
}

## JSON SNIPPET (Pardon the formatting issues)

{

"data": {

"id": "cf3ff8e6-4c04-4185-8f34-f6fee62ec0cb",

"type": "organization-locations",

"attributes": {

  "name": "Test Gym"

},
"relationships": {

  "equipment": {

    "data": [
      {

        "id": "d2d51fe9-4e23-4fa8-9693-11f1fe81fc58",

        "type": "equipment-gym-weight-stack-machines"

      }
    ]
  }
}

}, "included": [ {

  "id": "d2d51fe9-4e23-4fa8-9693-11f1fe81fc58",

  "type": "equipment-gym-weight-stack-machines",

  "attributes": {

    "model-number": "w5vz80ie",

    "serial-number": "htzr41c9gdbxjq7paiek",

    "properties": {

    }
  },
  "relationships": {
    "manufacturer": {
      "data": {
        "id": "9c269e16-72f6-46ec-b2ca-00936d3b4c81",
        "type": "organization-manufacturers"
      }
    },
    "location": {
      "data": {
        "id": "cf3ff8e6-4c04-4185-8f34-f6fee62ec0cb",
        "type": "organization-locations"
      }
    },
    "operator": {
      "data": {
        "id": "582e4cbb-8af2-4d8a-afd2-1e8905effdc3",
        "type": "organization-operators"
      }
    },
    "devices": {
      "data": [
        {
          "id": "ae93bfdd-0f11-4a83-bf8d-02abdfb4f911",
          "type": "equipment-device-stack-tracks"
        }
      ]
    }
  }
},
{
  "id": "ae93bfdd-0f11-4a83-bf8d-02abdfb4f911",
  "type": "equipment-device-stack-tracks",
  "attributes": {
    "auth-id": "eqp_device_m9hwRBi7NPgJQbfSctX64opak2lzU1uML",
    "hardware-uid": "976be123-ff7c-43de-bbfb-113d9b34bf5a",
    "mac-address": "f3-9a-57-1c-69-f5",
    "activated": false
  },
  "relationships": {
    "equipment": {
      "data": {
        "id": "d2d51fe9-4e23-4fa8-9693-11f1fe81fc58",
        "type": "equipment-gym-weight-stack-machines"
      }
    }
  }
}

] }

## Code To Parse JSON

`func spineGetTestGym(){

    (spine!.networkClient.request("GET", URL: NSURL(string: "https://.../api/organization/locations/cf3ff8e6-4c04-4185-8f34-f6fee62ec0cb")!, callback: { (statusCode, data, error) in

        let serializer = self.spine!.serializer

        serializer.registerResource(Gym)

        serializer.registerResource(Equipment)

        serializer.registerResource(Device)

        let document = try! serializer.deserializeData(data!)

        var gym = document.data

        self.spine!.save((document.data?.first)!)

    }))
}`
wvteijlingen commented 7 years ago

Your keys in the fieldsFromDictionary method are not set up correctly. The keys there must correspond to the name of variable in the Swift class. Then if you want to use a different name in the JSON, use the serializeAs(name: String) method. So for the Equipment class:

override class var fields: [Field] {
    return fieldsFromDictionary([
        "modelNumber": Attribute(),
        "serialNumber": Attribute(),
        "gym": ToOneRelationship(Gym).serializeAs("location"),
        "devices": ToManyRelationship(Device)
        ])
}