amplify-swift icon indicating copy to clipboard operation
amplify-swift copied to clipboard

Deserializing Nested Objects in GraphQL for iOS not working

Open danhaab opened this issue 3 years ago • 1 comments

Describe the bug

I've got a scenario in an app I'm creating where 3 objects have nested manyToMany relationships

for example: 1 top level object (TestStructure1) has multiple objects of type TestStructure2 and TestStructure3 objects of type TestStructure3 can have multiple objects of type TestStructure4

when I make a graphQL request in the AppSync console the correct structure is returned, and when I use that same query in swift using GraphQLRequest<JSONValue>, the correct JSON is returned, but when I make that same request as GraphQLRequest<TestStructure1>, 2, 3, and 4 are all nil

For the sake of simplicity, I've recreated this issue with simple test objects types

schema

`type TestStructure4 @model @auth(rules: [{allow: public}]) { id: ID! test4A: String test4B: String teststructure3s: [TestStructure3] @manyToMany(relationName: "TestStructure3TestStructure4") }

type TestStructure3 @model @auth(rules: [{allow: public}]) { id: ID! test3A: String test3B: String teststructure1s: [TestStructure1] @manyToMany(relationName: "TestStructure1TestStructure3") TestStructure4s: [TestStructure4] @manyToMany(relationName: "TestStructure3TestStructure4") }

type TestStructure2 @model @auth(rules: [{allow: public}]) { id: ID! test2A: String test2B: String teststructure1s: [TestStructure1] @manyToMany(relationName: "TestStructure1TestStructure2") }

type TestStructure1 @model @auth(rules: [{allow: public}]) { id: ID! test1A: String Test1B: String TestStructure2s: [TestStructure2] @manyToMany(relationName: "TestStructure1TestStructure2") TestStructure3s: [TestStructure3] @manyToMany(relationName: "TestStructure1TestStructure3") }`

query:

query MyQuery { getTestStructure1(id: "a54982d2-fe9a-4f74-b096-1db1b3e8722b") { TestStructure2s { items { testStructure2 { test2A test2B id } id } } TestStructure3s { items { testStructure3 { test3A test3B TestStructure4s { items { testStructure4 { test4A test4B } } } } } } Test1B id } }

returned json (correct)

{ "data": { "getTestStructure1": { "TestStructure2s": { "items": [ { "testStructure2": { "test2A": "test", "test2B": "test", "id": "4d784086-e105-4b25-bd20-77e20a25ad85" }, "id": "c148b571-3a77-48b1-aa46-09b9ddf855fd" }, { "testStructure2": { "test2A": "test", "test2B": "test", "id": "d340702a-5b7e-4870-b7b6-2e27c19b1357" }, "id": "72c9a71f-04fa-4cba-ac7c-56324cbcdb83" } ] }, "TestStructure3s": { "items": [ { "testStructure3": { "test3A": "test", "test3B": "test", "TestStructure4s": { "items": [ { "testStructure4": { "test4A": "test", "test4B": "test" } } ] } } } ] } } } }

iOS query (all nested objects nil)

extension GraphQLRequest { static func getTestStructure1Full(id: String) -> GraphQLRequest<TestStructure1> { let document = """ query getTestStructure1Full { getTestStructure1(id: "(id)") { TestStructure2s { items { testStructure2 { test2A test2B id } id } } TestStructure3s { items { testStructure3 { test3A test3B TestStructure4s { items { testStructure4 { test4A test4B } } } } } } Test1B id }

    }
    """
    return GraphQLRequest<TestStructure1>(
      document: document,
      variables: ["id": id],
      responseType: TestStructure1.self,
      decodePath: "getTestStructure1"
    )
}

}

Steps To Reproduce

Use schema and query above in iOS

Expected behavior

When the swift return type is set to JSONValue, the object is received and can be deserialized into the object type correctly, when the same query's return type is set to the generated model type, the nested objects are nil

Amplify Framework Version

1.28.0

Amplify Categories

API

Dependency manager

Swift PM

Swift version

5.6.1

CLI version

10.0.0

Xcode version

13.4.1

Relevant log output

<details>
<summary>Log Messages</summary>


INSERT LOG MESSAGES HERE
```

Is this a regression?

No

Regression additional context

No response

Device

iphone13 simulator

iOS Version

ios 15

Specific to simulators

No response

Additional context

No response

danhaab avatar Sep 08 '22 15:09 danhaab

My workaround for this was to create new models that matched the actual JSON being returned by AppSync and deserializing it manually, but if anyone knows what the issue may be or if any more info is needed, please let me know.

danhaab avatar Sep 09 '22 18:09 danhaab

Hi @danhaab, taking a look at the selection set you produced, I suggest adding all fields available including the id for nested testStructure3. The swift model type has a required id property it might be failing to decode. Any items selection set should also contain the nextToken. I think the decoding logic also uses __typename (this one might not be a hard requirement for your use case). So to get this to work as expected, basically I think you need to populate the selection set fully.

Have you considered just calling .get(TestStructure1.self, byId: id) to get the GraphQLRequest? This builder creates the selection set for you and you can call it with Amplify.API.query(request: .get(TestStructure1.self, byid: id). this will allow lazy loading of the nested fields. For your nested selection set issue, we are also working on an extension to the .get builder to allow you to specific how many levels of data to return

lawmicha avatar Oct 31 '22 17:10 lawmicha

Closing this due to no activity. Feel free to reach us if the above suggestion does not work for you.

royjit avatar Mar 02 '23 20:03 royjit