How to store relational (one to many or many to one) data with Amplify iOS (AppSync)?

2.1k views Asked by At

today checking some of the amplify documentation (I know this one says it is a preview in the iOS scenario) but I have ran into a road block.

Assumptions

  1. Amplify is correctly configured in my iOS project. I can push data to Person and query the Amplify.API
  2. The schema has been defined as:
type Person @model {
  id: ID!
  name: String!
  possessions: [Thing] # list of things this person owns.
    @connection(keyName: "byPerson", fields: ["id"])
}

type Thing @model
           @key(name: "byPerson", fields: ["personId"]) {

  id: ID!
  name: String!
  personId: ID!
  ownerOfThings: Person # defining the 'belongsTo' property.
    @connection(fields: ["personId"])
}

This generates the following code:

public struct Person: Model {
  public let id: String
  public var name: String
  public var possessions: List<Thing>?

  public init(id: String = UUID().uuidString,
      name: String,
      possessions: List<Thing>? = []) {
      self.id = id
      self.name = name
      self.possessions = possessions
  }
}

public struct Person: Model {
  public let id: String
  public var name: String
  public var ownerOfThings: Person?

  public init(id: String = UUID().uuidString,
      name: String,
      ownerOfThings: Person? = nil) {
      self.id = id
      self.name = name
      self.ownerOfThings = ownerOfThings
  }
}

Here is where I ran into trouble. Amplify.API doesn't seem be saving my object and its associated data in a single mutation. I have to call it as nested operations to have an effect.

// sample on how I am trying to save data.

var thing = Thing(name: "Long Claw")
let person = Person(
  name: "Jon Snow",
  possessions: List([ thing ])
)

Amplify.API.mutate(of: person, type: .create) { ev in
  // doing something with the event.
  print(String(describing: ev)) // this works. It saves the instance to DynamoDB
  // unfortunately, it did not save the instance of thing... let's try to correct this.

  thing.ownerOfThings = person
  Amplify.API.mutate(of: thing, type: .create) { ev2 in
    // do something else with this...
    print(String(describing: ev2))
    // this ^ crashes badly...
  }
}

The code above will generate an output similar to:

Result.success(Person(id: "EC4BEEE1-C1A1-4831-AB86-EA1E22D8AD48", name: "Jon Snow", possessions: nil))

GraphQLResponseError<Thing>: GraphQL service returned a successful response containing errors: [Amplify.GraphQLError(message: "Variable \'input\' has coerced Null value for NonNull type \'ID!\'", locations: Optional([Amplify.GraphQLError.Location(line: 1, column: 26)]), path: nil, extensions: nil)]

I've tried declaring the relationship as:

type Person @model {
  id: ID!
  name: String!
  possessions: [Thing] # list of things this person owns.
    @connection(keyName: "byPerson", fields: ["id"])
}

type Thing @model
           @key(name: "byPerson", fields: ["personId"]) {

  id: ID!
  name: String!
  personId: ID!
  # ownerOfThings: Person
  #   @connection(fields: ["personId"]) # Not belongsTo for you!
}

Or a variation of this, defining the possessions as possessions: [Thing] @connection.

All of them generate various (although some what related) errors, preventing me from storing my data.

So, the question is: How do you specify the relationship in iOS to save it?

0

There are 0 answers