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

Listen in to a custom subscription within an AppSync schema

Open benrosen78 opened this issue 5 years ago • 4 comments

Is your feature request related to a problem? Please describe. Amplify for iOS currently does not seem to have the capability to create a subscription for a change besides the default Subscription handler for a Model object. What I would like is to be able to pass in inputs to filter my subscription by ID or by some other parameter.

This is the subscription I currently have set up in my schema. As you can see, it takes an ID as a parameter.

type Subscription {
	onUpdate(id: ID!): Item
		@aws_subscribe(mutations: ["updateItem"])
}

Describe the solution you'd like I would like a way to specify the name of a subscription function and pass parameters into it before listening to it. I would like to see it as an optional parameter in the subscription enum for GraphQLRequest.

For what it's worth, I was able to find a work-around to accomplish this.

let request = GraphQLRequest(apiName: "myapi", document: "subscription { onUpdate(id: \"19dee48e-6daa-417d-a9b1-8d7108ab8735\") { id, conversionCount } }", variables: [:], responseType: JSONValue.self, decodePath: nil)
subscription = Amplify.API.subscribe(request: .subs, valueListener: { _ in
    // do stuff
}, completionListener: nil)

benrosen78 avatar Jul 22 '20 05:07 benrosen78

Hi @benrosen78, thanks for the detailed solution. The Model types generated from amplify codegen models uses the graphql schema with types containing the @model directive, which is used in the GraphQL Transform (like when you run amplify add api, amplify push) to generate the AppSync API and related resources. The extensions help build the GraphQLRequest for those APIs and do not currently take in optional inputs since there wasn't a clear requirement for it.

To identify the need for it, how did you provision the AppSync service? Through AppSync console? or was there a set of directives that you used that provisioned the subscription API withid as input?

The work-around is what we would expect for requests which need customization. You can also extract the variable out to dynamically inject it.

let variables = ["id": id] // `id` variable
let request = GraphQLRequest(apiName: "myapi", document: "subscription($id: ID!) { onUpdate(id: $id) { id, conversionCount } }", variables: variables, responseType: JSONValue.self, decodePath: nil)
subscription = Amplify.API.subscribe(request: .subs, valueListener: { _ in
    // do stuff
}, completionListener: nil)

Another example here

Also, is there a reason to use JSONValue as the response type over the Model type or a Codable type that matches your Item?

lawmicha avatar Jul 22 '20 17:07 lawmicha

Your own docs specify the way to create custom subscriptions with arguments. Still this is categorised as iOS, the example code is in JavaScript: https://docs.amplify.aws/guides/api-graphql/subscriptions-by-id/q/platform/ios

Also adding the mentioned Subscription does only have the effect with amplify codegen models that two more files are created (Subscription.swift & Subscription+Schema.swift) which are basically empty

// swiftlint:disable all
import Amplify
import Foundation

We have multiple clients needed to subscribe to updates of their own data. In the moment every client gets every update and is missing a separation, so a default support would be greatly appreciated..

LaszloDev avatar Aug 20 '20 13:08 LaszloDev

Your own docs specify the way to create custom subscriptions with arguments. Still this is categorised as iOS, the example code is in JavaScript: https://docs.amplify.aws/guides/api-graphql/subscriptions-by-id/q/platform/ios

Thanks for this link, the relevant piece that I missed in my earlier comment was that this

type Subscription {
  onCommentByPostId(postCommentsId: ID!): Comment
    @aws_subscribe(mutations: ["createComment"])
}

is actually placed in the schema used for GraphQLTransform. As for those docs categorized as iOS, that is something we need to address as well. I believe there just isn't a code snippet provided for the iOS platform so it shows JS for all of the platforms including Android as well.

Also adding the mentioned Subscription does only have the effect with amplify codegen models that two more files are created (Subscription.swift & Subscription+Schema.swift) which are basically empty

// swiftlint:disable all
import Amplify
import Foundation

We have multiple clients needed to subscribe to updates of their own data. In the moment every client gets every update and is missing a separation, so a default support would be greatly appreciated..

This looks like an artifact of the fact that amplify codegen models has not handled this custom subscription use cases. A modification to the codesnippet above would be to continue to use the Model type as type to decode to, since the custom subscriptions are returning that type as well.

let variables = ["id": id] // `id` variable
let request = GraphQLRequest(document: "subscription($id: ID!) { onUpdate(id: $id) { id, conversionCount } }", variables: variables, responseType: Item.self, decodePath: nil)
subscription = Amplify.API.subscribe(request: .subs, valueListener: { _ in
    // do stuff
}, completionListener: nil)

JSONValue.self -> Item.self .

lawmicha avatar Aug 21 '20 23:08 lawmicha

I am encountering the same problem, and when I use the workaround I get a "Failed to decode GraphQL response to the 'ResponseType' Item Any help would be appreciated!

leungy2 avatar Aug 28 '20 16:08 leungy2

@LaszloDev Could you please open a new issue with details you mentioned in your comments?

thisisabhash avatar Aug 04 '23 18:08 thisisabhash

Please track https://github.com/aws-amplify/docs/pull/5649 for the Upgrade Guide. Once we have this ready, the latest CLI will allow generating API.swift through amplify codegen types that is compatible with Amplify.API, thus custom subscriptions use case like this will be possible

lawmicha avatar Aug 10 '23 15:08 lawmicha