Description
Describe the bug
AppSync GraphQL subscriptions established using Amplify.API.subscribe with a custom GraphQLRequest consistently disconnect after a specific period of inactivity (no messages received or sent over the subscription). We observe this reliably when the app is in the foreground and the subscription is active but idle. Our backend setup, involving a custom mutation handled by a Lambda function triggering a custom subscription, follows the patterns described in the Amplify Gen 2 documentation for this type of real-time flow.
The disconnection manifests as a failure in the Combine sink's receiveCompletion block, reporting APIError: Subscription item event failed with error caused by the underlying error Error Domain=NSPOSIXErrorDomain Code=57 "Socket is not connected". The timing of this disconnection appears highly consistent: it occurs almost exactly at 120 seconds (2 minutes) of inactivity, or, less frequently but still precisely, at exactly 180 seconds (3 minutes) of inactivity.
This issue occurs when using the latest stable Amplify Swift libraries, even with simplified subscription lifecycle code. Because the basic setup failed, we subsequently implemented custom client-side retry logic (attempting to re-establish the subscription upon detecting the Code 57 error), however, this retry mechanism itself failed to function correctly. While the retry logic successfully initiates a new connection attempt (indicated by logging the .connection(.connecting) state), this reconnection attempt subsequently fails silently. It never reaches the .connection(.connected) state, nor does the Combine sink's receiveCompletion handler get called with a success or failure for this second attempt. We frequently observed [AWSGraphQLSubscriptionTaskRunner] Failed to unsubscribe logs during these disconnection events, suggesting the SDK's internal state prevented a clean teardown and re-initialization of the subscription, rendering the retry logic ineffective. The SDK's built-in WebSocket keep-alive mechanism does not seem to prevent the initial, specific, timed disconnection. We believe network connectivity is generally good when this occurs. Restarting the app allows reconnection, but the idle disconnect happens again after the same exact interval.
Steps To Reproduce
- Configure an AWS AppSync backend with:
A custom GraphQL mutation (e.g., publishMessage) handled by a Lambda function.
A custom GraphQL subscription (e.g., subscribeToMessages) linked to the mutation via .for(a.ref('publishMessage')) in the schema definition (resource.ts). - In the iOS client app (using Amplify Swift SDK):
Configure Amplify with AWSAPIPlugin.
Implement code to subscribe to the custom subscribeToMessages using Amplify.API.subscribe and a GraphQLRequest. - Run the app and successfully establish the subscription (observe .connection(.connected) state is reached).
- Keep the app in the foreground and ensure the subscription remains active.
- Do not trigger the publishMessage mutation (i.e., ensure no events are sent over the subscription, allowing it to remain idle).
- Wait for approximately 2 to 3 minutes.
- Observe that the Combine sink's receiveCompletion handler associated with the subscription receives a .failure event containing the NSPOSIXErrorDomain Code=57 "Socket is not connected" error.
Expected behavior
The WebSocket connection for the AppSync GraphQL subscription should remain active indefinitely (as long as the client maintains the subscription) via the SDK's automatic keep-alive mechanisms, without disconnecting due to short periods (2-3 minutes) of inactivity.
Amplify Framework Version
2.46.1
Amplify Categories
API
Dependency manager
Swift PM
Swift version
5
CLI version
1.5.0
Xcode version
16.2.0
Is this a regression?
No
Platforms
iOS
OS Version
iOS 18.3
Device
iPhone16Pro