Skip to content

Commit b852ff1

Browse files
feat(backgroundmode): app-93 MQTT Client reconnect Android
1 parent 6b5506b commit b852ff1

File tree

6 files changed

+113
-33
lines changed

6 files changed

+113
-33
lines changed

README.md

+70-26
Original file line numberDiff line numberDiff line change
@@ -7,27 +7,29 @@ MQTT client for React Native application.
77
- Secure MQTT connection over TLS 1.2.
88
- Authentication of both of server and client by X.509 certificates.
99
- Certificates and a private key stored in a device specific key store.
10-
- [Android KeyStore](https://developer.android.com/training/articles/keystore#UsingAndroidKeyStore) on Android
11-
- [Default keychain](https://developer.apple.com/documentation/security/keychain_services/keychains) on iOS
10+
- [Android KeyStore](https://developer.android.com/training/articles/keystore#UsingAndroidKeyStore) on Android
11+
- [Default keychain](https://developer.apple.com/documentation/security/keychain_services/keychains) on iOS
1212

1313
## Dependencies
1414

1515
This library wraps the following libraries,
16+
1617
- [Paho MQTT Client for Android variant maintained by hannesa2](https://github.com/hannesa2/paho.mqtt.android) (Android)
1718

1819
This library is forked as [emoto-kc-ak/paho.mqtt.android](https://github.com/emoto-kc-ak/paho.mqtt.android) to make Maven artifacts.
20+
1921
- [CocoaMQTT](https://github.com/emqx/CocoaMQTT) (iOS)
2022

2123
## Installation
2224

2325
```sh
24-
npm install --save git+https://github.com/emoto-kc-ak/react-native-mqtt-client.git#v0.1.1
26+
npm install --save git+https://github.com/emoto-kc-ak/react-native-mqtt-client.git#v0.1.4
2527
```
2628

2729
## Usage
2830

2931
```js
30-
import MqttClient from "react-native-mqtt-client";
32+
import MqttClient from 'react-native-mqtt-client';
3133
```
3234

3335
### Configuring an identity
@@ -43,13 +45,18 @@ MqttClient.setIdentity({
4345
keyPem: IOT_KEY, // PEM representation string of a private key
4446
keyStoreOptions, // options for a device specific key store. may be omitted
4547
})
46-
.then(() => { /* handle success */ })
47-
.catch(({code, message}) => { /* handle error */ });
48+
.then(() => {
49+
/* handle success */
50+
})
51+
.catch(({ code, message }) => {
52+
/* handle error */
53+
});
4854
```
4955

5056
`keyStoreOptions` is an optional object that may have the following fields,
51-
- `caCertAlias`: (string) Alias associated with a root certificate (Android only). See [`KeyStore.setCertificateEntry`](https://developer.android.com/reference/java/security/KeyStore#setCertificateEntry(java.lang.String,%20java.security.cert.Certificate))
52-
- `keyAlias`: (string) Alias associated with a private key (Android only). See [`KeyStore.setKeyEntry`](https://developer.android.com/reference/java/security/KeyStore#setKeyEntry(java.lang.String,%20java.security.Key,%20char[],%20java.security.cert.Certificate[]))
57+
58+
- `caCertAlias`: (string) Alias associated with a root certificate (Android only). See [`KeyStore.setCertificateEntry`](<https://developer.android.com/reference/java/security/KeyStore#setCertificateEntry(java.lang.String,%20java.security.cert.Certificate)>)
59+
- `keyAlias`: (string) Alias associated with a private key (Android only). See [`KeyStore.setKeyEntry`](<https://developer.android.com/reference/java/security/KeyStore#setKeyEntry(java.lang.String,%20java.security.Key,%20char[],%20java.security.cert.Certificate[])>)
5360
- `caCertLabel`: (string) Label associated with a root certificate (iOS only). See [`kSecAttrLabel`](https://developer.apple.com/documentation/security/ksecattrlabel)
5461
- `certLabel`: (string) Label associated with a client certificate (iOS only). See [`kSecAttrLabel`](https://developer.apple.com/documentation/security/ksecattrlabel)
5562
- `keyApplicationTag`: (string) Tag associated with a private key (iOS only). See [`kSecAttrApplicationTag`](https://developer.apple.com/documentation/security/ksecattrapplicationtag)
@@ -64,8 +71,12 @@ MqttClient.connect({
6471
port: IOT_PORT, // (number) Port to connect.
6572
clientId: IOT_DEVICE_ID, // (string) Client ID of a device connecting.
6673
})
67-
.then(() => { /* handle success */ })
68-
.catch(({code, message}) => { /* handle error */ });
74+
.then(() => {
75+
/* handle success */
76+
})
77+
.catch(({ code, message }) => {
78+
/* handle error */
79+
});
6980
```
7081

7182
It attempts to connect to `ssl://$IOT_ENDPOINT:$IOT_PORT`.
@@ -76,11 +87,16 @@ It attempts to connect to `ssl://$IOT_ENDPOINT:$IOT_PORT`.
7687

7788
```js
7889
MqttClient.publish(topic, payload)
79-
.then(() => { /* handle success */ })
80-
.catch(({code, message}) => { /* handle error */ });
90+
.then(() => {
91+
/* handle success */
92+
})
93+
.catch(({ code, message }) => {
94+
/* handle error */
95+
});
8196
```
8297

8398
Where,
99+
84100
- `topic`: (string) Topic where `payload` is to be published.
85101
- `payload`: (string) Payload to be published. Usually a stringified JSON object.
86102

@@ -90,11 +106,16 @@ Where,
90106

91107
```js
92108
MqttClient.subscribe(topic)
93-
.then(() => { /* handle success */ })
94-
.catch(({code, message}) => { /* handle error */ });
109+
.then(() => {
110+
/* handle success */
111+
})
112+
.catch(({ code, message }) => {
113+
/* handle error */
114+
});
95115
```
96116

97117
Where,
118+
98119
- `topic`: (string) Topic to subscribe.
99120

100121
To handle messages in the subscribed topic, you have to handle a [`receive-message` event](#received-message).
@@ -104,7 +125,7 @@ To handle messages in the subscribed topic, you have to handle a [`receive-messa
104125
`MqttClient.disconnect` disconnects from an MQTT broker.
105126

106127
```js
107-
MqttClient.disconnect()
128+
MqttClient.disconnect();
108129
```
109130

110131
### Loading an identity
@@ -113,8 +134,12 @@ An identity stored in a device specific key store by `MqttClient.setIdentity` ma
113134

114135
```js
115136
MqttClient.loadIdentity(keyStoreOptions)
116-
.then(() => { /* handle success */ })
117-
.catch(({code, message}) => { /* handle error */ });
137+
.then(() => {
138+
/* handle success */
139+
})
140+
.catch(({ code, message }) => {
141+
/* handle error */
142+
});
118143
```
119144

120145
Please refer to [Configuring an identity](#configuring-an-identity) for details of `keyStoreOptions`.
@@ -125,8 +150,12 @@ An identity stored in a device specific key store by `MqttClient.setIdentity` ma
125150

126151
```js
127152
MqttClient.resetIdentity(keyStoreOptions)
128-
.then(() => { /* handle success */ })
129-
.catch(({code, message}) => { /* handle error */ });
153+
.then(() => {
154+
/* handle success */
155+
})
156+
.catch(({ code, message }) => {
157+
/* handle error */
158+
});
130159
```
131160

132161
Please refer to [Configuring an identity](#configuring-an-identity) for details of `keyStoreOptions`.
@@ -137,11 +166,16 @@ Please refer to [Configuring an identity](#configuring-an-identity) for details
137166

138167
```js
139168
MqttClient.isIdentityStored(keyStoreOptions)
140-
.then((isStored) => { /* handle success */ })
141-
.catch(({code, message}) => { /* handle error */ });
169+
.then((isStored) => {
170+
/* handle success */
171+
})
172+
.catch(({ code, message }) => {
173+
/* handle error */
174+
});
142175
```
143176

144177
Where,
178+
145179
- `isStored`: (boolean) Whether an identity is stored in a device-specific key store.
146180

147181
Please refer to [Configuring an identity](#configuring-an-identity) for details of `keyStoreOptions`.
@@ -155,26 +189,33 @@ Please refer to [Configuring an identity](#configuring-an-identity) for details
155189
A `connected` event is notified when connection to an MQTT broker is established.
156190

157191
```js
158-
MqttClient.addListener('connected', () => { /* handle connection */ })
192+
MqttClient.addListener('connected', () => {
193+
/* handle connection */
194+
});
159195
```
160196

161197
#### disconnected
162198

163199
A `disconnected` event is notified when connection to an MQTT broker is disconnected.
164200

165201
```js
166-
MqttClient.addListener('disconnected', () => { /* handle disconnection */ })
202+
MqttClient.addListener('disconnected', () => {
203+
/* handle disconnection */
204+
});
167205
```
168206

169207
#### received-message
170208

171209
A `received-message` event is notified when a message is arrived from an MQTT broker.
172210

173211
```js
174-
MqttClient.addListener('received-message', ({topic, payload}) => { /* handle message */ });
212+
MqttClient.addListener('received-message', ({ topic, payload }) => {
213+
/* handle message */
214+
});
175215
```
176216

177217
Where,
218+
178219
- `topic`: (string) Topic where a message has been published.
179220
- `payload`: (string) Payload of a message. Usually a stringified JSON object.
180221

@@ -183,7 +224,9 @@ Where,
183224
A `got-error` event is notified when an error has occurred.
184225

185226
```js
186-
MqttClient.addListener('got-error', (err) => { /* handle error */ })
227+
MqttClient.addListener('got-error', (err) => {
228+
/* handle error */
229+
});
187230
```
188231

189232
## iOS Tips
@@ -215,6 +258,7 @@ npm run prepare
215258
```
216259

217260
Artifacts will be updated in the following directories,
261+
218262
- `lib/commonjs`
219263
- `lib/module`
220264
- `lib/typescript`
@@ -229,4 +273,4 @@ MIT
229273

230274
## Acknowlegement
231275

232-
This project is bootstrapped with [callstack/react-native-builder-bob](https://github.com/callstack/react-native-builder-bob).
276+
This project is bootstrapped with [callstack/react-native-builder-bob](https://github.com/callstack/react-native-builder-bob).

android/src/main/java/com/github/emotokcak/reactnative/mqtt/RNMqttClient.kt

+11-5
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,7 @@ class RNMqttClient(reactContext: ReactApplicationContext)
252252
* - `clientId`: {`String`} Client ID of the device.
253253
* - `host`: {`String`} Host name of the MQTT broker.
254254
* - `port`: {`int`} Port of the MQTT broker.
255+
* - `reconnect`: {`Boolean`} Reconnect if connection is lost.
255256
*
256257
* @param params
257258
*
@@ -304,7 +305,9 @@ class RNMqttClient(reactContext: ReactApplicationContext)
304305
override fun connectionLost(cause: Throwable?) {
305306
Log.d(NAME, "connectionLost", cause)
306307
this@RNMqttClient.notifyError("ERROR_CONNECTION", cause)
307-
this@RNMqttClient.notifyEvent("disconnected", null)
308+
if(!parsedParams.reconnect) {
309+
this@RNMqttClient.notifyEvent("disconnected", null)
310+
}
308311
}
309312

310313
override fun deliveryComplete(token: IMqttDeliveryToken) {
@@ -340,8 +343,9 @@ class RNMqttClient(reactContext: ReactApplicationContext)
340343
}
341344
})
342345
val connectOptions = MqttConnectOptions()
343-
connectOptions.setSocketFactory(socketFactory)
344-
connectOptions.setCleanSession(true)
346+
connectOptions.socketFactory = socketFactory
347+
connectOptions.isCleanSession = true
348+
connectOptions.isAutomaticReconnect = parsedParams.reconnect
345349
Log.d(NAME, "connecting to the broker")
346350
val token = client.connect(connectOptions)
347351
token.setActionCallback(object: IMqttActionListener {
@@ -549,15 +553,17 @@ class RNMqttClient(reactContext: ReactApplicationContext)
549553
private class ConnectionParameters(
550554
val host: String,
551555
val port: Int,
552-
val clientId: String
556+
val clientId: String,
557+
val reconnect: Boolean
553558
) {
554559
companion object {
555560
// Parses a given object from JavaScript.
556561
fun parseReadableMap(params: ReadableMap): ConnectionParameters {
557562
return ConnectionParameters(
558563
host=params.getRequiredString("host"),
559564
port=params.getRequiredInt("port"),
560-
clientId=params.getRequiredString("clientId")
565+
clientId=params.getRequiredString("clientId"),
566+
reconnect=params.getRequiredBoolean("reconnect")
561567
)
562568
}
563569
}

android/src/main/java/com/github/emotokcak/reactnative/mqtt/package.kt

+29
Original file line numberDiff line numberDiff line change
@@ -118,3 +118,32 @@ fun ReadableMap.getOptionalMap(key: String): ReadableMap? {
118118
}
119119
return this.getMap(key)
120120
}
121+
122+
/**
123+
* Obtains an optional Boolean value from a given `ReadableMap`.
124+
*
125+
* @param key
126+
*
127+
* Key associated with the value to be obtained.
128+
*
129+
* @return
130+
*
131+
* Boolean value associated with `key`.
132+
* `false` if no value is associated with `key`.
133+
*
134+
* @throws IllegalArgumentException
135+
*
136+
* If the value associated with `key` is not a Boolean.
137+
*/
138+
fun ReadableMap.getRequiredBoolean(key: String): Boolean {
139+
if (!this.hasKey(key)) {
140+
return false
141+
}
142+
if (this.getType(key) != ReadableType.Boolean) {
143+
throw IllegalArgumentException(
144+
"$key must be associated with a Boolean" +
145+
" but ${this.getType(key)} was given"
146+
)
147+
}
148+
return this.getBoolean(key)
149+
}

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-native-mqtt-client",
3-
"version": "0.1.1",
3+
"version": "0.1.4",
44
"description": "MQTT client for React Native",
55
"main": "lib/commonjs/index",
66
"module": "lib/module/index",

react-native-mqtt-client.podspec

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,5 @@ Pod::Spec.new do |s|
1818

1919

2020
s.dependency "React"
21-
s.dependency "CocoaMQTT"
21+
s.dependency "CocoaMQTT", "~> 2.1.1"
2222
end

src/index.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,7 @@ export type ConnectionParameters = {
287287
host: string;
288288
port: string;
289289
clientId: string;
290+
reconnect: boolean;
290291
};
291292

292293
const defaultInstance = new MqttClient();

0 commit comments

Comments
 (0)