apollo-ios
apollo-ios copied to clipboard
Subscription not working after 30 minutes
Bug report
- Subscribe to subscriptions.
- Receiving data from subscriptions (working).
- Still uses the app about 30 . In 30 minutes BE doesn't send anything. 31st minute BE sends data to subscriptions . The app doesn't receive anything
- I checked apollo client webSocketTransport isConnected() = true and in list subscriptions exist data and don't have an error from apollo
The function init Apollo client
func initApollo(){
self.apoloStore = ApolloStore()
let url = DeploymentMode.currentMode() == .dev ? URL(string: “dev”)! : URL(string: “prod”)!
var request = URLRequest(url: url)
webSocketTransport = WebSocketTransport(
websocket: DefaultWebSocket(request: request),
reconnect: true
)
webSocketTransport.delegate = self
webSocketTransport.initServer()
let provider = NetworkInterceptorProvider(client: URLSessionClient(), shouldInvalidateClientOnDeinit: false, store: apoloStore)
let normalTransport = RequestChainNetworkTransport(interceptorProvider: provider,
endpointURL: url)
let splitNetworkTransport = SplitNetworkTransport(
uploadingNetworkTransport: normalTransport,
webSocketNetworkTransport: webSocketTransport
)
self.apolloClient = ApolloClient(networkTransport: splitNetworkTransport, store: apoloStore)
}
Subscribe to subscriptions
func subscriptionApollo(){
subscription = ApolloManager.shared.apolloClient
.subscribe(subscription: NameSubscription()) { [weak self] (result) in
guard let self = self else { return }
switch result {
case .success(let value):
LogManager.shared.log(logStr: "GRAPHQL - subscription - success ")
break
case .failure(let value):
LogManager.shared.log(logStr: "GRAPHQL - subscription - failure ")
break
}
}
}
Versions
Please fill in the versions you're currently using:
-
apollo-ios
SDK version: 0.45.0 - Xcode version: Version 12.4
- Swift version: 5.0
Thanks for the bug report! I'm not sure what would be causing this. @designatednerd any ideas from your experience?
Thanks @siliconprime-manhnguyen, I've got a couple questions:
- does the app stay in the foreground all the time?
- is it 30 minutes from init subscription or 30 minutes from last received data?
@calvincestari
- Yes , The app stay in the foreground all the time
- 30 minutes from last received data
Weird! I haven't seen this at all.
- Is this new behavior in
0.45.0
or was this also happening before? - You mentioned the app is in the foreground - can you confirm that either the screen is disabled from locking or that the app is in use for those 30 minutes? Basically want to make sure the device didn't lock.
- Is it only after 30 minutes specifically, or is that just the amount of time you were testing for?
- Is there any other data going over the web socket from the app, like pings or anything like that?
- Does the server think the websocket connection is still open?
- Are you able to tell if anything comes in over the wire to the app? You should be able to throw in a breakpoint at either
WebSocketTransport.didReceive(event:)
and/orWebSocket.didReceive(event:)
and see if either of those get hit when stuff comes in over the wire.
In theory if the server dropped the connection there should be an error, but stranger things have happened.
@designatednerd
- Is this new behavior in 0.45.0 or was this also happening before? I don't know exactly the version was this happened but the two last versions were (0.45.0 and 0.44.0) happening
- You mentioned the app is in the foreground - can you confirm that either the screen is disabled from locking or that the app is in use for those 30 minutes? Basically want to make sure the device didn't lock. Yes i'm sure that the device didn't lock because the user still can see another user live stream and playing game
- Is there any other data going over the web socket from the app, like pings or anything like that? Yes we have a ping pong 30 seconds at a time when the issues happing ping_pong still working Below is function send and receive ping pong
func sendPing() {
LogManager.shared.log(logStr: "SOCKET - send ping", isSaveToFile: AppDelegate.shared.isLogToFile)
self.socket?.write(ping: Data(), completion: nil)
}
func websocketDidReceivePong(socket: WebSocketClient, data: Data?) {
LogManager.shared.log(logStr: "SOCKET - receive pong", isSaveToFile: AppDelegate.shared.isLogToFile)
self.lastPongTime = Date()
}
-
Does the server think the websocket connection is still open? Yes, websocket connection is still opening. because Android and web still working normally
-
Are you able to tell if anything comes in over the wire to the app? Sorry I still find it but I don't know the reason
-
You should be able to throw in a breakpoint at either WebSocketTransport.didReceive(event:) and/or WebSocket.didReceive(event:) and see if either of those get hit when stuff comes in over the wire. Thank you for your suggestions I will check about that
Thanks for all the info! I'm wondering what the best method to investigate this is. I want to figure out if this is an issue with Apollo, or with Starscream (the library we use for the websocket connection under the hood).
This issue could be related: https://github.com/daltoniam/Starscream/issues/649 This may be an Apple issue, and the workaround is to have the websocket send a ping periodically to prevent the stream from going stale.
@designatednerd Do you think that is something we should bake into Apollo?
Yeah that certainly sounds like it could be it.
I can see arguments both ways in terms of baking it into Apollo or not: Theoretically the websocket is an implementation detail of subscriptions, so we should build in some kind of auto-ping mechanism to disguise that detail (pro), but also not every websocket server handles pings exactly the same way and it's not entirely clear how often we'd need to do this, so it might be something better for each dev to configure what they actually need rather than us setting it up by default (con).
If we do it, maybe a default setup where it pings every N seconds, and passing 0 or less just turns off the ping? Or an enum of (none
, every(seconds: UInt)`)?
@AnthonyMDev @designatednerd Thank you so much for your investigate Do you plan to release next time to resolve the problem? We really really looking forward to that because all ios users are affected This is very important for us
Hi, @designatednerd @AnthonyMDev Could you please let me know if there is any news? Can you give me advice on a workaround solution to resolve the problem?
I'm going to keep this open to work on it in the future, but for now, I think you should just set up a timer that periodically sends a ping. You can have it call the WebSocketTransport.ping()
function with some dummy data that your server can ignore to keep the connection open.
Hi, I'm also facing very similar issue.
So in my case web socket(subscription) stop receiving updates from server after 10-15 minutes but i can see ping pong event in Charles proxy.
- On app launch fetch 10 items using api call and subscribe for each item individually.
- Every 30 seconds I'm refetching 10 items using api call and canceling previous subscription and making new subscription
- App is always in foreground without screen lock.
- I can see app canceling(type:complete) old subscription and making new subscription(type: subscribe) with ping/pong around every 12 seconds
- Initially(first 10-15 minutes) i can see app receiving updates
- In order to cross check I also open parallel subscription using Postman and after 10-15 minutes i can see events in post man but not in app
- My Apollo library version is 1.9.0
class MySubscriptionClient {
private let apolloClient: ApolloClient?
init(url: URL?) {
var component = URLComponents(string: url?.absoluteString ?? "")
component?.scheme = "wss"
if let url = component?.url {
/// A common store to use for `httpTransport` and `webSocketTransport`.
let store = ApolloStore()
/// A web socket transport to use for subscriptions
let webSocketTransport: WebSocketTransport = {
let webSocketClient = WebSocket(url: url, protocol: .graphql_transport_ws)
webSocketClient.enableSOCKSProxy = true
return WebSocketTransport(
websocket: webSocketClient
)
}()
self.apolloClient = ApolloClient(networkTransport: webSocketTransport, store: store)
} else {
self.apolloClient = nil
log("SubscriptionClient: Unable to initialise ApolloClient!", type: .error)
}
}
}
I'm sorry you're experiencing this issue. I'm not sure what could be causing this. 🤔
Cancelling and restarting your subscriptions every 30 seconds theoretically should work, but it might be causing some sort of race condition or bug we aren't aware of?
If you are pinging the web socket and getting success responses every 12 seconds, it really should continue to work. At the point when the subscription stops receiving updates, are the ping/pong events still continuing to work correctly?
Hi @AnthonyMDev, Yes i can see ping/pong events works correctly(in Charles) after its stops receiving updates!