diff --git a/README.md b/README.md index c690785..01d7bd0 100644 --- a/README.md +++ b/README.md @@ -7,27 +7,29 @@ MQTT client for React Native application. - Secure MQTT connection over TLS 1.2. - Authentication of both of server and client by X.509 certificates. - Certificates and a private key stored in a device specific key store. - - [Android KeyStore](https://developer.android.com/training/articles/keystore#UsingAndroidKeyStore) on Android - - [Default keychain](https://developer.apple.com/documentation/security/keychain_services/keychains) on iOS + - [Android KeyStore](https://developer.android.com/training/articles/keystore#UsingAndroidKeyStore) on Android + - [Default keychain](https://developer.apple.com/documentation/security/keychain_services/keychains) on iOS ## Dependencies This library wraps the following libraries, + - [Paho MQTT Client for Android variant maintained by hannesa2](https://github.com/hannesa2/paho.mqtt.android) (Android) This library is forked as [emoto-kc-ak/paho.mqtt.android](https://github.com/emoto-kc-ak/paho.mqtt.android) to make Maven artifacts. + - [CocoaMQTT](https://github.com/emqx/CocoaMQTT) (iOS) ## Installation ```sh -npm install --save git+https://github.com/emoto-kc-ak/react-native-mqtt-client.git#v0.1.1 +npm install --save git+https://github.com/emoto-kc-ak/react-native-mqtt-client.git#v0.1.4 ``` ## Usage ```js -import MqttClient from "react-native-mqtt-client"; +import MqttClient from 'react-native-mqtt-client'; ``` ### Configuring an identity @@ -43,13 +45,18 @@ MqttClient.setIdentity({ keyPem: IOT_KEY, // PEM representation string of a private key keyStoreOptions, // options for a device specific key store. may be omitted }) - .then(() => { /* handle success */ }) - .catch(({code, message}) => { /* handle error */ }); + .then(() => { + /* handle success */ + }) + .catch(({ code, message }) => { + /* handle error */ + }); ``` `keyStoreOptions` is an optional object that may have the following fields, -- `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)) -- `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[])) + +- `caCertAlias`: (string) Alias associated with a root certificate (Android only). See [`KeyStore.setCertificateEntry`]() +- `keyAlias`: (string) Alias associated with a private key (Android only). See [`KeyStore.setKeyEntry`]() - `caCertLabel`: (string) Label associated with a root certificate (iOS only). See [`kSecAttrLabel`](https://developer.apple.com/documentation/security/ksecattrlabel) - `certLabel`: (string) Label associated with a client certificate (iOS only). See [`kSecAttrLabel`](https://developer.apple.com/documentation/security/ksecattrlabel) - `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({ port: IOT_PORT, // (number) Port to connect. clientId: IOT_DEVICE_ID, // (string) Client ID of a device connecting. }) - .then(() => { /* handle success */ }) - .catch(({code, message}) => { /* handle error */ }); + .then(() => { + /* handle success */ + }) + .catch(({ code, message }) => { + /* handle error */ + }); ``` It attempts to connect to `ssl://$IOT_ENDPOINT:$IOT_PORT`. @@ -76,11 +87,16 @@ It attempts to connect to `ssl://$IOT_ENDPOINT:$IOT_PORT`. ```js MqttClient.publish(topic, payload) - .then(() => { /* handle success */ }) - .catch(({code, message}) => { /* handle error */ }); + .then(() => { + /* handle success */ + }) + .catch(({ code, message }) => { + /* handle error */ + }); ``` Where, + - `topic`: (string) Topic where `payload` is to be published. - `payload`: (string) Payload to be published. Usually a stringified JSON object. @@ -90,11 +106,16 @@ Where, ```js MqttClient.subscribe(topic) - .then(() => { /* handle success */ }) - .catch(({code, message}) => { /* handle error */ }); + .then(() => { + /* handle success */ + }) + .catch(({ code, message }) => { + /* handle error */ + }); ``` Where, + - `topic`: (string) Topic to subscribe. 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 `MqttClient.disconnect` disconnects from an MQTT broker. ```js -MqttClient.disconnect() +MqttClient.disconnect(); ``` ### Loading an identity @@ -113,8 +134,12 @@ An identity stored in a device specific key store by `MqttClient.setIdentity` ma ```js MqttClient.loadIdentity(keyStoreOptions) - .then(() => { /* handle success */ }) - .catch(({code, message}) => { /* handle error */ }); + .then(() => { + /* handle success */ + }) + .catch(({ code, message }) => { + /* handle error */ + }); ``` 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 ```js MqttClient.resetIdentity(keyStoreOptions) - .then(() => { /* handle success */ }) - .catch(({code, message}) => { /* handle error */ }); + .then(() => { + /* handle success */ + }) + .catch(({ code, message }) => { + /* handle error */ + }); ``` 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 ```js MqttClient.isIdentityStored(keyStoreOptions) - .then((isStored) => { /* handle success */ }) - .catch(({code, message}) => { /* handle error */ }); + .then((isStored) => { + /* handle success */ + }) + .catch(({ code, message }) => { + /* handle error */ + }); ``` Where, + - `isStored`: (boolean) Whether an identity is stored in a device-specific key store. Please refer to [Configuring an identity](#configuring-an-identity) for details of `keyStoreOptions`. @@ -155,7 +189,9 @@ Please refer to [Configuring an identity](#configuring-an-identity) for details A `connected` event is notified when connection to an MQTT broker is established. ```js -MqttClient.addListener('connected', () => { /* handle connection */ }) +MqttClient.addListener('connected', () => { + /* handle connection */ +}); ``` #### disconnected @@ -163,7 +199,9 @@ MqttClient.addListener('connected', () => { /* handle connection */ }) A `disconnected` event is notified when connection to an MQTT broker is disconnected. ```js -MqttClient.addListener('disconnected', () => { /* handle disconnection */ }) +MqttClient.addListener('disconnected', () => { + /* handle disconnection */ +}); ``` #### received-message @@ -171,10 +209,13 @@ MqttClient.addListener('disconnected', () => { /* handle disconnection */ }) A `received-message` event is notified when a message is arrived from an MQTT broker. ```js -MqttClient.addListener('received-message', ({topic, payload}) => { /* handle message */ }); +MqttClient.addListener('received-message', ({ topic, payload }) => { + /* handle message */ +}); ``` Where, + - `topic`: (string) Topic where a message has been published. - `payload`: (string) Payload of a message. Usually a stringified JSON object. @@ -183,7 +224,9 @@ Where, A `got-error` event is notified when an error has occurred. ```js -MqttClient.addListener('got-error', (err) => { /* handle error */ }) +MqttClient.addListener('got-error', (err) => { + /* handle error */ +}); ``` ## iOS Tips @@ -215,6 +258,7 @@ npm run prepare ``` Artifacts will be updated in the following directories, + - `lib/commonjs` - `lib/module` - `lib/typescript` @@ -229,4 +273,4 @@ MIT ## Acknowlegement -This project is bootstrapped with [callstack/react-native-builder-bob](https://github.com/callstack/react-native-builder-bob). \ No newline at end of file +This project is bootstrapped with [callstack/react-native-builder-bob](https://github.com/callstack/react-native-builder-bob). diff --git a/android/src/main/java/com/github/emotokcak/reactnative/mqtt/RNMqttClient.kt b/android/src/main/java/com/github/emotokcak/reactnative/mqtt/RNMqttClient.kt index 75e38b7..a97dc64 100644 --- a/android/src/main/java/com/github/emotokcak/reactnative/mqtt/RNMqttClient.kt +++ b/android/src/main/java/com/github/emotokcak/reactnative/mqtt/RNMqttClient.kt @@ -252,6 +252,7 @@ class RNMqttClient(reactContext: ReactApplicationContext) * - `clientId`: {`String`} Client ID of the device. * - `host`: {`String`} Host name of the MQTT broker. * - `port`: {`int`} Port of the MQTT broker. + * - `reconnect`: {`Boolean`} Reconnect if connection is lost. * * @param params * @@ -304,7 +305,9 @@ class RNMqttClient(reactContext: ReactApplicationContext) override fun connectionLost(cause: Throwable?) { Log.d(NAME, "connectionLost", cause) this@RNMqttClient.notifyError("ERROR_CONNECTION", cause) - this@RNMqttClient.notifyEvent("disconnected", null) + if(!parsedParams.reconnect) { + this@RNMqttClient.notifyEvent("disconnected", null) + } } override fun deliveryComplete(token: IMqttDeliveryToken) { @@ -340,8 +343,9 @@ class RNMqttClient(reactContext: ReactApplicationContext) } }) val connectOptions = MqttConnectOptions() - connectOptions.setSocketFactory(socketFactory) - connectOptions.setCleanSession(true) + connectOptions.socketFactory = socketFactory + connectOptions.isCleanSession = true + connectOptions.isAutomaticReconnect = parsedParams.reconnect Log.d(NAME, "connecting to the broker") val token = client.connect(connectOptions) token.setActionCallback(object: IMqttActionListener { @@ -549,7 +553,8 @@ class RNMqttClient(reactContext: ReactApplicationContext) private class ConnectionParameters( val host: String, val port: Int, - val clientId: String + val clientId: String, + val reconnect: Boolean ) { companion object { // Parses a given object from JavaScript. @@ -557,7 +562,8 @@ class RNMqttClient(reactContext: ReactApplicationContext) return ConnectionParameters( host=params.getRequiredString("host"), port=params.getRequiredInt("port"), - clientId=params.getRequiredString("clientId") + clientId=params.getRequiredString("clientId"), + reconnect=params.getRequiredBoolean("reconnect") ) } } diff --git a/android/src/main/java/com/github/emotokcak/reactnative/mqtt/package.kt b/android/src/main/java/com/github/emotokcak/reactnative/mqtt/package.kt index 1f16b61..1653fdd 100644 --- a/android/src/main/java/com/github/emotokcak/reactnative/mqtt/package.kt +++ b/android/src/main/java/com/github/emotokcak/reactnative/mqtt/package.kt @@ -118,3 +118,32 @@ fun ReadableMap.getOptionalMap(key: String): ReadableMap? { } return this.getMap(key) } + +/** +* Obtains an optional Boolean value from a given `ReadableMap`. +* +* @param key +* +* Key associated with the value to be obtained. +* +* @return +* +* Boolean value associated with `key`. +* `false` if no value is associated with `key`. +* +* @throws IllegalArgumentException +* +* If the value associated with `key` is not a Boolean. +*/ +fun ReadableMap.getRequiredBoolean(key: String): Boolean { + if (!this.hasKey(key)) { + return false + } + if (this.getType(key) != ReadableType.Boolean) { + throw IllegalArgumentException( + "$key must be associated with a Boolean" + + " but ${this.getType(key)} was given" + ) + } + return this.getBoolean(key) +} \ No newline at end of file diff --git a/ios/MqttClient.swift b/ios/MqttClient.swift index 85397f0..c4540c9 100644 --- a/ios/MqttClient.swift +++ b/ios/MqttClient.swift @@ -299,6 +299,7 @@ class MqttClient : RCTEventEmitter { let host: String = RCTConvert.nsString(params["host"]) let port: Int = RCTConvert.nsInteger(params["port"]) let clientId: String = RCTConvert.nsString(params["clientId"]) + let reconnect: Bool = RCTConvert.bool(params["reconnect"]) self.client = CocoaMQTT(clientID: clientId, host: host, port: UInt16(port)) self.client!.username = "" self.client!.password = "" @@ -311,6 +312,7 @@ class MqttClient : RCTEventEmitter { self.client!.keepAlive = 60 self.client!.delegate = self self.client!.logLevel = .debug + self.client!.autoReconnect = reconnect _ = self.client!.connect() resolve(nil) } @@ -441,7 +443,7 @@ extension MqttClient : CocoaMQTTDelegate { completionHandler(false) return } - + switch result { case .proceed: completionHandler(true) diff --git a/ios/Podfile b/ios/Podfile index 05f706f..30bb941 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -3,5 +3,5 @@ platform :ios, '11.0' target 'MqttClient' do use_frameworks! - pod 'CocoaMQTT' + pod 'CocoaMQTT', '2.1.1' end diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 0f09393..64debdd 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1,20 +1,22 @@ PODS: - - CocoaAsyncSocket (7.6.4) - - CocoaMQTT (1.2.0): - - CocoaAsyncSocket (~> 7.6.3) + - CocoaMQTT (2.1.1): + - CocoaMQTT/Core (= 2.1.1) + - CocoaMQTT/Core (2.1.1): + - MqttCocoaAsyncSocket (~> 1.0.8) + - MqttCocoaAsyncSocket (1.0.8) DEPENDENCIES: - - CocoaMQTT + - CocoaMQTT (= 2.1.1) SPEC REPOS: trunk: - - CocoaAsyncSocket - CocoaMQTT + - MqttCocoaAsyncSocket SPEC CHECKSUMS: - CocoaAsyncSocket: 694058e7c0ed05a9e217d1b3c7ded962f4180845 - CocoaMQTT: 0b43b6ec5196c1ea30f4636d9ad5afa474fd616b + CocoaMQTT: 204713d369b9a3ed8d90af2e0666f11121d06e74 + MqttCocoaAsyncSocket: 77d3b74f76228dd5a05d1f9526eab101d415b30c -PODFILE CHECKSUM: c5d39cc2472e2b2a31cd4a3611619e1f90f00a77 +PODFILE CHECKSUM: d869e597c5c87ca654a9bd0e46189acac14b8d2c -COCOAPODS: 1.9.3 +COCOAPODS: 1.11.3 diff --git a/package.json b/package.json index de63888..5e51273 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-mqtt-client", - "version": "0.1.1", + "version": "0.1.4", "description": "MQTT client for React Native", "main": "lib/commonjs/index", "module": "lib/module/index", diff --git a/react-native-mqtt-client.podspec b/react-native-mqtt-client.podspec index d6a6590..1c86781 100644 --- a/react-native-mqtt-client.podspec +++ b/react-native-mqtt-client.podspec @@ -18,5 +18,5 @@ Pod::Spec.new do |s| s.dependency "React" - s.dependency "CocoaMQTT" + s.dependency "CocoaMQTT", "~> 2.1.1" end diff --git a/src/index.tsx b/src/index.tsx index 485414f..7bd0ae6 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -287,6 +287,7 @@ export type ConnectionParameters = { host: string; port: string; clientId: string; + reconnect: boolean; }; const defaultInstance = new MqttClient();