diff --git a/CHANGELOG.md b/CHANGELOG.md index aac600b..5b9067e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to the LaunchDarkly React Native SDK will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org). +## [4.0.4] - 2021-06-02 +### Fixed: +- iOS: Fixed an issue where an exception was thrown when calling `LDClient.configure` with an optional `timeout` ([#80](https://github.com/launchdarkly/react-native-client-sdk/issues/80)). + ## [4.2.1] - 2021-06-01 ### Fixed: - iOS: Fixed an issue where an exception was thrown when calling `LDClient.configure` with an optional `timeout` ([#80](https://github.com/launchdarkly/react-native-client-sdk/issues/80)). @@ -10,7 +14,6 @@ All notable changes to the LaunchDarkly React Native SDK will be documented in t - Android: Fixed an issue where the promise for `LDClient.configure` could be resolved before the client had finished initializing when not providing the optional `timeout` parameter. - Android: Fixed an issue where `LDClient.allFlags` would reject the promise when the client was configured but not yet initialized, rather than resolving with any cached flags. - ## [4.2.0] - 2021-05-19 ### Added: - `LDUser` now has an optional `secondary` attribute to match other LaunchDarkly SDKs. For more on the behavior of this attribute see [the documentation on targeting users](https://docs.launchdarkly.com/home/managing-flags/targeting-users). @@ -18,7 +21,6 @@ All notable changes to the LaunchDarkly React Native SDK will be documented in t - `secondaryMobileKeys` is now a config option which allows a mapping of names to the SDK keys for each additional environment. `mobileKey` is still required, and represents the primary environment. - Many methods including variations, track, and listeners now support an optional `environment` parameter to evaluate the method against the given `environment`. - ## [4.1.2] - 2021-04-28 ### Fixed: - The `LDEvaluationReasonErrorKind`, `LDEvaluationReasonKind`, `LDConnectionMode`, and `LDFailureReason` enum TypeScript types were undefined when evaluated at runtime due to being defined in an ambient context. This was also fixed in SDK version 4.0.3 with React Native 0.63 compatibility. diff --git a/android/src/main/java/com/launchdarkly/reactnative/LaunchdarklyReactNativeClientModule.java b/android/src/main/java/com/launchdarkly/reactnative/LaunchdarklyReactNativeClientModule.java index cabce69..47910e6 100644 --- a/android/src/main/java/com/launchdarkly/reactnative/LaunchdarklyReactNativeClientModule.java +++ b/android/src/main/java/com/launchdarkly/reactnative/LaunchdarklyReactNativeClientModule.java @@ -902,11 +902,13 @@ private String envConcat(String environment, String identifier) { @ReactMethod public void registerFeatureFlagListener(final String flagKey, final String environment) { - FeatureFlagChangeListener listener = new FeatureFlagChangeListener() { + final String multiListenerId = envConcat(environment, flagKey); + final FeatureFlagChangeListener listener = new FeatureFlagChangeListener() { @Override public void onFeatureFlagChange(String flagKey) { WritableMap result = Arguments.createMap(); - result.putString("flagKey", envConcat(environment, flagKey)); + result.putString("flagKey", flagKey); + result.putString("listenerId", multiListenerId); getReactApplicationContext() .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) @@ -924,10 +926,11 @@ public void onFeatureFlagChange(String flagKey) { @ReactMethod public void unregisterFeatureFlagListener(String flagKey, String environment) { + String multiListenerId = envConcat(environment, flagKey); try { - if (listeners.containsKey(flagKey)) { - LDClient.getForMobileKey(environment).unregisterFeatureFlagListener(flagKey, listeners.get(flagKey)); - listeners.remove(flagKey); + if (listeners.containsKey(multiListenerId)) { + LDClient.getForMobileKey(environment).unregisterFeatureFlagListener(flagKey, listeners.get(multiListenerId)); + listeners.remove(multiListenerId); } } catch (Exception e) { Timber.w(e); @@ -936,12 +939,13 @@ public void unregisterFeatureFlagListener(String flagKey, String environment) { @ReactMethod public void registerCurrentConnectionModeListener(final String listenerId, final String environment) { + final String multiListenerId = envConcat(environment, listenerId); LDStatusListener listener = new LDStatusListener() { @Override public void onConnectionModeChanged(ConnectionInformation connectionInfo) { WritableMap result = Arguments.createMap(); result.putString("connectionMode", gson.toJson(connectionInfo)); - result.putString("listenerId", envConcat(environment, listenerId)); + result.putString("listenerId", multiListenerId); getReactApplicationContext() .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) @@ -954,7 +958,7 @@ public void onInternalFailure(LDFailure ldFailure) {} try { LDClient.getForMobileKey(environment).registerStatusListener(listener); - connectionModeListeners.put(listenerId, listener); + connectionModeListeners.put(multiListenerId, listener); } catch (Exception e) { Timber.w(e); } @@ -963,9 +967,10 @@ public void onInternalFailure(LDFailure ldFailure) {} @ReactMethod public void unregisterCurrentConnectionModeListener(String listenerId, String environment) { try { - if (connectionModeListeners.containsKey(listenerId)) { - LDClient.getForMobileKey(environment).unregisterStatusListener(connectionModeListeners.get(listenerId)); - connectionModeListeners.remove(listenerId); + String multiListenerId = envConcat(environment, listenerId); + if (connectionModeListeners.containsKey(multiListenerId)) { + LDClient.getForMobileKey(environment).unregisterStatusListener(connectionModeListeners.get(multiListenerId)); + connectionModeListeners.remove(multiListenerId); } } catch (Exception e) { Timber.w(e); @@ -974,12 +979,13 @@ public void unregisterCurrentConnectionModeListener(String listenerId, String en @ReactMethod public void registerAllFlagsListener(final String listenerId, final String environment) { + final String multiListenerId = envConcat(environment, listenerId); LDAllFlagsListener listener = new LDAllFlagsListener() { @Override public void onChange(List flagKeys) { WritableMap result = Arguments.createMap(); result.putString("flagKeys", gson.toJson(flagKeys)); - result.putString("listenerId", envConcat(environment, listenerId)); + result.putString("listenerId", multiListenerId); getReactApplicationContext() .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) @@ -989,7 +995,7 @@ public void onChange(List flagKeys) { try { LDClient.getForMobileKey(environment).registerAllFlagsListener(listener); - allFlagsListeners.put(listenerId, listener); + allFlagsListeners.put(multiListenerId, listener); } catch (Exception e) { Timber.w(e); } @@ -998,9 +1004,10 @@ public void onChange(List flagKeys) { @ReactMethod public void unregisterAllFlagsListener(String listenerId, String environment) { try { - if (allFlagsListeners.containsKey(listenerId)) { - LDClient.getForMobileKey(environment).unregisterAllFlagsListener(allFlagsListeners.get(listenerId)); - allFlagsListeners.remove(listenerId); + String multiListenerId = envConcat(environment, listenerId); + if (allFlagsListeners.containsKey(multiListenerId)) { + LDClient.getForMobileKey(environment).unregisterAllFlagsListener(allFlagsListeners.get(multiListenerId)); + allFlagsListeners.remove(multiListenerId); } } catch (Exception e) { Timber.w(e); diff --git a/index.js b/index.js index 1681161..54d7d6f 100644 --- a/index.js +++ b/index.js @@ -227,8 +227,9 @@ export default class LDClient { _flagUpdateListener(changedFlag) { const flagKey = changedFlag.flagKey; - if (this.flagListeners.hasOwnProperty(flagKey)) { - let listeners = this.flagListeners[flagKey]; + const listenerId = changedFlag.listenerId; + if (this.flagListeners.hasOwnProperty(listenerId)) { + let listeners = this.flagListeners[listenerId]; for (const listener of listeners) { listener(flagKey); } @@ -238,20 +239,16 @@ export default class LDClient { _allFlagsUpdateListener(changedFlags) { const flagKeys = changedFlags.flagKeys; const listenerId = changedFlags.listenerId; - for (const [key, value] of Object.entries(this.allFlagsListeners)) { - if (key == listenerId) { - key(flagKeys); - } + if (this.allFlagsListeners.hasOwnProperty(listenerId)) { + this.allFlagsListeners[listenerId](flagKeys); } } _connectionModeUpdateListener(connectionStatus) { const connectionMode = connectionStatus.connectionMode; const listenerId = connectionStatus.listenerId; - for (const [key, value] of Object.entries(this.connectionModeListeners)) { - if (key == listenerId) { - key(connectionMode); - } + if (this.connectionModeListeners.hasOwnProperty(listenerId)) { + this.connectionModeListeners[listenerId](connectionMode); } } @@ -296,7 +293,7 @@ export default class LDClient { return; } const env = environment !== undefined ? environment : "default"; - const multiListenerId = this._envConcat(env, flagKey); + const multiListenerId = this._envConcat(env, listenerId); this.connectionModeListeners[multiListenerId] = callback; LaunchdarklyReactNativeClient.registerCurrentConnectionModeListener(listenerId, env); @@ -304,7 +301,7 @@ export default class LDClient { unregisterCurrentConnectionModeListener(listenerId, environment) { const env = environment !== undefined ? environment : "default"; - const multiListenerId = this._envConcat(env, flagKey); + const multiListenerId = this._envConcat(env, listenerId); if (!this.connectionModeListeners.hasOwnProperty(multiListenerId)) { return; } @@ -318,7 +315,7 @@ export default class LDClient { return; } const env = environment !== undefined ? environment : "default"; - const multiListenerId = this._envConcat(env, flagKey); + const multiListenerId = this._envConcat(env, listenerId); this.allFlagsListeners[multiListenerId] = callback; LaunchdarklyReactNativeClient.registerAllFlagsListener(listenerId, env); @@ -326,7 +323,7 @@ export default class LDClient { unregisterAllFlagsListener(listenerId, environment) { const env = environment !== undefined ? environment : "default"; - const multiListenerId = this._envConcat(env, flagKey); + const multiListenerId = this._envConcat(env, listenerId); if (!this.allFlagsListeners.hasOwnProperty(multiListenerId)) { return; } diff --git a/ios/LaunchdarklyReactNativeClient.swift b/ios/LaunchdarklyReactNativeClient.swift index 5344588..da7d816 100644 --- a/ios/LaunchdarklyReactNativeClient.swift +++ b/ios/LaunchdarklyReactNativeClient.swift @@ -541,27 +541,22 @@ class LaunchdarklyReactNativeClient: RCTEventEmitter { } @objc func registerFeatureFlagListener(_ flagKey: String, environment: String) -> Void { - let flagChangeOwner = flagKey as LDObserverOwner - if listenerKeys[flagKey] == nil { - listenerKeys[flagKey] = flagChangeOwner - } else { - return - } - LDClient.get(environment: environment)!.observe(keys: [flagKey], owner: flagChangeOwner, handler: { (changedFlags) in - if changedFlags[flagKey] != nil && self.bridge != nil { - self.sendEvent(withName: self.FLAG_PREFIX, body: ["flagKey": self.envConcat(environment: environment, identifier: flagKey)]) + let multiListenerId = envConcat(environment: environment, identifier: flagKey) + let flagChangeOwner = multiListenerId as LDObserverOwner + listenerKeys[multiListenerId] = flagChangeOwner + LDClient.get(environment: environment)!.observe(key: flagKey, owner: flagChangeOwner, handler: { changedFlag in + if self.bridge != nil { + self.sendEvent(withName: self.FLAG_PREFIX, body: ["flagKey": changedFlag.key, "listenerId": multiListenerId]) } }) } private func unregisterListener(_ key: String, _ environment: String) -> Void { - let owner = key as LDObserverOwner - if listenerKeys[key] != nil { - listenerKeys.removeValue(forKey: key) - } else { - return + let multiListenerId = envConcat(environment: environment, identifier: key) + let owner = multiListenerId as LDObserverOwner + if listenerKeys.removeValue(forKey: multiListenerId) != nil { + LDClient.get(environment: environment)!.stopObserving(owner: owner) } - LDClient.get(environment: environment)!.stopObserving(owner: owner) } @objc func unregisterFeatureFlagListener(_ flagKey: String, environment: String) -> Void { @@ -569,15 +564,11 @@ class LaunchdarklyReactNativeClient: RCTEventEmitter { } @objc func registerCurrentConnectionModeListener(_ listenerId: String, environment: String) -> Void { - let currentConnectionModeOwner = listenerId as LDObserverOwner - if listenerKeys[listenerId] == nil { - listenerKeys.removeValue(forKey: listenerId) - } else { - return - } - LDClient.get(environment: environment)!.observeCurrentConnectionMode(owner: currentConnectionModeOwner, handler: { (connectionMode) in + let multiListenerId = envConcat(environment: environment, identifier: listenerId) + let currentConnectionModeOwner = multiListenerId as LDObserverOwner + LDClient.get(environment: environment)!.observeCurrentConnectionMode(owner: currentConnectionModeOwner, handler: { connectionMode in if self.bridge != nil { - self.sendEvent(withName: self.CONNECTION_MODE_PREFIX, body: ["connectionMode": connectionMode, "listenerId": self.envConcat(environment: environment, identifier: listenerId)]) + self.sendEvent(withName: self.CONNECTION_MODE_PREFIX, body: ["connectionMode": connectionMode, "listenerId": multiListenerId]) } }) } @@ -587,15 +578,11 @@ class LaunchdarklyReactNativeClient: RCTEventEmitter { } @objc func registerAllFlagsListener(_ listenerId: String, environment: String) -> Void { - let flagChangeOwner = listenerId as LDObserverOwner - if listenerKeys[listenerId] == nil { - listenerKeys[listenerId] = flagChangeOwner - } else { - return - } - LDClient.get(environment: environment)!.observeAll(owner: flagChangeOwner, handler: { (changedFlags) in + let multiListenerId = envConcat(environment: environment, identifier: listenerId) + let flagChangeOwner = multiListenerId as LDObserverOwner + LDClient.get(environment: environment)!.observeAll(owner: flagChangeOwner, handler: { changedFlags in if self.bridge != nil { - self.sendEvent(withName: self.ALL_FLAGS_PREFIX, body: ["flagKeys": changedFlags.keys.description, "listenerId": self.envConcat(environment: environment, identifier: listenerId)]) + self.sendEvent(withName: self.ALL_FLAGS_PREFIX, body: ["flagKeys": Array(changedFlags.keys), "listenerId": multiListenerId]) } }) }