Skip to content

Commit 5f62829

Browse files
fix react native ios reconnect
1 parent b054bf1 commit 5f62829

File tree

4 files changed

+77
-2
lines changed

4 files changed

+77
-2
lines changed

.changeset/cyan-penguins-smell.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@powersync/react-native': minor
3+
---
4+
5+
Fixed issue where iOS WebSockets could fail to reconnect after a connection issue.

packages/common/src/client/sync/stream/AbstractRemote.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ import ndjsonStream from 'can-ndjson-stream';
44
import { type fetch } from 'cross-fetch';
55
import Logger, { ILogger } from 'js-logger';
66
import { RSocket, RSocketConnector, Requestable } from 'rsocket-core';
7-
import { WebsocketClientTransport } from 'rsocket-websocket-client';
87
import PACKAGE from '../../../../package.json' with { type: 'json' };
98
import { AbortOperation } from '../../../utils/AbortOperation.js';
109
import { DataStream } from '../../../utils/DataStream.js';
1110
import { PowerSyncCredentials } from '../../connection/PowerSyncCredentials.js';
1211
import { StreamingSyncLine, StreamingSyncRequest } from './streaming-sync-types.js';
12+
import { WebsocketClientTransport } from './WebsocketClientTransport.js';
1313

1414
export type BSONImplementation = typeof BSON;
1515

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/**
2+
* Adapted from rsocket-websocket-client
3+
* https://github.com/rsocket/rsocket-js/blob/e224cf379e747c4f1ddc4f2fa111854626cc8575/packages/rsocket-websocket-client/src/WebsocketClientTransport.ts#L17
4+
* This adds additional error handling for React Native iOS.
5+
* This particularly adds a close listener to handle cases where the WebSocket
6+
* connection closes immediately after opening without emitting an error.
7+
*/
8+
import {
9+
ClientTransport,
10+
Closeable,
11+
Demultiplexer,
12+
Deserializer,
13+
DuplexConnection,
14+
FrameHandler,
15+
Multiplexer,
16+
Outbound
17+
} from 'rsocket-core';
18+
import { ClientOptions } from 'rsocket-websocket-client';
19+
import { WebsocketDuplexConnection } from 'rsocket-websocket-client/dist/WebsocketDuplexConnection.js';
20+
21+
export class WebsocketClientTransport implements ClientTransport {
22+
private readonly url: string;
23+
private readonly factory: (url: string) => WebSocket;
24+
25+
constructor(options: ClientOptions) {
26+
this.url = options.url;
27+
this.factory = options.wsCreator ?? ((url: string) => new WebSocket(url));
28+
}
29+
30+
connect(
31+
multiplexerDemultiplexerFactory: (outbound: Outbound & Closeable) => Multiplexer & Demultiplexer & FrameHandler
32+
): Promise<DuplexConnection> {
33+
return new Promise((resolve, reject) => {
34+
const websocket = this.factory(this.url);
35+
36+
websocket.binaryType = 'arraybuffer';
37+
38+
let removeListeners: () => void;
39+
40+
const openListener = () => {
41+
removeListeners();
42+
resolve(new WebsocketDuplexConnection(websocket, new Deserializer(), multiplexerDemultiplexerFactory));
43+
};
44+
45+
const errorListener = (ev: ErrorEvent) => {
46+
removeListeners();
47+
reject(ev.error);
48+
};
49+
50+
/**
51+
* In some cases, such as React Native iOS, the WebSocket connection may close immediately after opening
52+
* without and error. In such cases, we need to handle the close event to reject the promise.
53+
*/
54+
const closeListener = () => {
55+
removeListeners();
56+
reject(new Error('WebSocket connection closed while opening'));
57+
};
58+
59+
removeListeners = () => {
60+
websocket.removeEventListener('open', openListener);
61+
websocket.removeEventListener('error', errorListener);
62+
websocket.removeEventListener('close', closeListener);
63+
};
64+
65+
websocket.addEventListener('open', openListener);
66+
websocket.addEventListener('error', errorListener);
67+
websocket.addEventListener('close', closeListener);
68+
});
69+
}
70+
}

packages/react-native/rollup.config.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export default (commandLineArgs) => {
5454
}
5555
]
5656
}),
57-
terser()
57+
terser({ sourceMap: sourcemap })
5858
],
5959
external: [
6060
'@journeyapps/react-native-quick-sqlite',

0 commit comments

Comments
 (0)