From b6a113ce502cdc237bed7eac7a50d3e87c310878 Mon Sep 17 00:00:00 2001 From: Joe Cieslik <5600929+torchhound@users.noreply.github.com> Date: Wed, 31 Mar 2021 18:56:37 -0700 Subject: [PATCH] V4.0 (#68) * Update to iOS 5.4.0 (#48) * Bump iOS SDK version to 5.4.0 * Replace shared with get() * Replace old method names * Provide default values for EvaluationDetail * Fixed defaultValue for detail value * Changed individual nil coalesce to NSNull * Remove isDisableBackgroundPolling method (#52) * Update iOS base url (#49) * Update iOS base url * Added wrapper name and version to iOS and Android config (#50) * Added wrapper name and version to iOS and Android config * V4 not 5 * Rename wrapper * Add getVersion method (#51) * Added getVersion method * Revert package.json version bump * Fix version case Co-authored-by: Ben Woskow <48036130+bwoskow-ld@users.noreply.github.com> Co-authored-by: Ben Woskow <48036130+bwoskow-ld@users.noreply.github.com> Co-authored-by: Ben Woskow <48036130+bwoskow-ld@users.noreply.github.com> * Rename fallback to defaultValue (#53) * Rename fallback to defaultValue * Fix awkward wording defaultValue value * Fix defaultValue value line breaks * Improved resiliency when running in Android (#54) * Add new config values (#55) * Added new config values to iOS and Android * Added typescript config * PR feedback * Fix millis, add default doc * fix common build + add tests for recent introductions (#56) * fix: start background thread for identify rather than running it (#66) * Bump Android SDK version to 2.14.1 (#59) * Update Android to 2.14.1, change setBaseUri to setPollUri, change floatVariation to doubleVariation * Fix float to double * Doublevalue on non detail * Run CI against v4.0 * URL fix iOS * Add isInitialized to iOS, check initialization in configure (#60) * Added isInitialized to iOS * Add init check to configure * Base url iOS fix, v4.0 ci * Var not func iOS, remove getMap * disable auto-alias in iOS (#61) * Add configureWithTimeout method (#58) * Added configureTimeout method * Fix timeout type on iOS * Simplify timeout nil check * Added param labels, Java syntax fixes * Small fixes * Make timeout final * Remove unnecessary catch * Fix baseUrl * Fix Android config error * Test against v4.0 hello branch * Fix tests * timeout never nil inside check * Convert Int to TimeInterval * Fixed iOS startWaitSeconds * Catch LDException * Fix merge conflict in iOS bridge * Fix merge conflict in index.js * PR feedback * Fix unused timeoutClient * Remove unnecessary ConfigEntryType * Remove StringSet * If let in timeout check * Configure method now takes optional timeout parameter instead of separate method * Renames for consistency (#62) * up-leveling the override for the default polling uri so it affects android too (#63) * Added ip, avatar, and allUserAttributesPrivate (#57) * Added ip, avatar, and allUserAttributesPrivate * Updated test-types.ts, fix case typo * Special case allUserAttributesPrivate * Fix ReadableMap loading of all private * CI fix * Fix string to URL baseUrl * Change to non-default values in test, combine lines in config.yml * Simplify allAttrsPrivate if * resolve breakage with latest merge Co-authored-by: Ben Woskow * Added accessor methods for ConnectionInformation (#64) * Added ConnectionInformation accessors * Fixes from manual testing * PR feedback * Fix allUserAttributesPrivate * Improve typescript docs * Minor fixes (#66) Fixes `floatVariation` to keep double precision and fix build warnings. * Fix track metricValue on iOS (#67) * Remove CI checkout for release Co-authored-by: Ben Woskow <48036130+bwoskow-ld@users.noreply.github.com> Co-authored-by: Ed Costello Co-authored-by: Ben Woskow Co-authored-by: Gavin Whelan --- README.md | 2 +- android/build.gradle | 2 +- .../LaunchdarklyReactNativeClientModule.java | 425 ++++++++++++------ index.d.ts | 259 +++++++++-- index.js | 151 ++++--- ios/LaunchdarklyReactNativeClient.podspec | 2 +- ios/LaunchdarklyReactNativeClient.swift | 378 ++++++++++------ ios/LaunchdarklyReactNativeClientBridge.m | 68 +-- test-types.ts | 34 +- 9 files changed, 883 insertions(+), 438 deletions(-) diff --git a/README.md b/README.md index 53f6285..8b88cbc 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ LaunchDarkly overview Supported versions ------------------------- -This SDK is compatible with React Native 0.62.x and Xcode 11.4 and is tested in Android 27 and iOS 12.4. Earlier versions of this SDK are compatible with prior versions of React Native, Android, and iOS. +This SDK is compatible with React Native 0.62.x and Xcode 12 and is tested in Android 27 and iOS 13.5. Earlier versions of this SDK are compatible with prior versions of React Native, Android, and iOS. Getting started --------------- diff --git a/android/build.gradle b/android/build.gradle index 96c8c73..8eeeced 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -49,7 +49,7 @@ allprojects { dependencies { implementation 'com.facebook.react:react-native:+' - implementation 'com.launchdarkly:launchdarkly-android-client-sdk:2.10.0' + implementation 'com.launchdarkly:launchdarkly-android-client-sdk:2.14.1' implementation 'com.jakewharton.timber:timber:4.7.1' implementation "com.google.code.gson:gson:2.8.5" } diff --git a/android/src/main/java/com/launchdarkly/reactnative/LaunchdarklyReactNativeClientModule.java b/android/src/main/java/com/launchdarkly/reactnative/LaunchdarklyReactNativeClientModule.java index ccb0a15..4190b76 100644 --- a/android/src/main/java/com/launchdarkly/reactnative/LaunchdarklyReactNativeClientModule.java +++ b/android/src/main/java/com/launchdarkly/reactnative/LaunchdarklyReactNativeClientModule.java @@ -36,6 +36,7 @@ import com.launchdarkly.android.EvaluationDetail; import com.launchdarkly.android.EvaluationReason; import com.launchdarkly.android.LDFailure; +import com.launchdarkly.android.LaunchDarklyException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -68,7 +69,7 @@ public class LaunchdarklyReactNativeClientModule extends ReactContextBaseJavaMod */ enum ConfigMapping { CONFIG_MOBILE_KEY("mobileKey", ConfigEntryType.String, "setMobileKey"), - CONFIG_BASE_URI("baseUri", ConfigEntryType.Uri, "setBaseUri"), + CONFIG_BASE_URI("pollUri", ConfigEntryType.Uri, "setPollUri"), CONFIG_EVENTS_URI("eventsUri", ConfigEntryType.UriMobile, "setEventsUri"), CONFIG_STREAM_URI("streamUri", ConfigEntryType.Uri, "setStreamUri"), CONFIG_EVENTS_CAPACITY("eventsCapacity", ConfigEntryType.Integer, "setEventsCapacity"), @@ -81,7 +82,12 @@ enum ConfigMapping { CONFIG_DISABLE_BACKGROUND_UPDATING("disableBackgroundUpdating", ConfigEntryType.Boolean, "setDisableBackgroundUpdating"), CONFIG_OFFLINE("offline", ConfigEntryType.Boolean, "setOffline"), CONFIG_PRIVATE_ATTRIBUTES("privateAttributeNames", ConfigEntryType.StringSet, "setPrivateAttributeNames"), - CONFIG_EVALUATION_REASONS("evaluationReasons", ConfigEntryType.Boolean, "setEvaluationReasons"); + CONFIG_EVALUATION_REASONS("evaluationReasons", ConfigEntryType.Boolean, "setEvaluationReasons"), + CONFIG_WRAPPER_NAME("wrapperName", ConfigEntryType.String, "setWrapperName"), + CONFIG_WRAPPER_VERSION("wrapperVersion", ConfigEntryType.String, "setWrapperVersion"), + CONFIG_MAX_CACHED_USERS("maxCachedUsers", ConfigEntryType.Integer, "setMaxCachedUsers"), + CONFIG_DIAGNOSTIC_OPT_OUT("diagnosticOptOut", ConfigEntryType.Boolean, "setDiagnosticOptOut"), + CONFIG_DIAGNOSTIC_RECORDING_INTERVAL("diagnosticRecordingIntervalMillis", ConfigEntryType.Integer, "setDiagnosticRecordingIntervalMillis"); final String key; final ConfigEntryType type; @@ -98,9 +104,9 @@ void loadFromMap(ReadableMap map, LDConfig.Builder builder) { try { setter.invoke(builder, type.getFromMap(map, key)); } catch (IllegalAccessException e) { - e.printStackTrace(); + Timber.w(e); } catch (InvocationTargetException e) { - e.printStackTrace(); + Timber.w(e); } } } @@ -149,9 +155,9 @@ void loadFromMap(ReadableMap map, LDUser.Builder builder, Set privateAtt setter.invoke(builder, type.getFromMap(map, key)); } } catch (IllegalAccessException e) { - e.printStackTrace(); + Timber.w(e); } catch (InvocationTargetException e) { - e.printStackTrace(); + Timber.w(e); } } } @@ -214,19 +220,38 @@ public Map getConstants() { * configuration. * * @param config LDConfig configuration, @see configBuild - * @param userConfig LDUser configuration, @see userBuild + * @param user LDUser configuration, @see userBuild * @param promise Either rejected if an error was encountered, otherwise resolved with null * once client is initialized. */ @ReactMethod - public void configure(ReadableMap config, ReadableMap userConfig, final Promise promise) { + public void configure(ReadableMap config, ReadableMap user, final Promise promise) { + internalConfigure(config, user, null, promise); + } + + /** + * React Method called from JavaScript to initialize the LDClient using the supplied + * configuration with a timeout. + * + * @param config LDConfig configuration, @see configBuild + * @param user LDUser configuration, @see userBuild + * @param timeout Integer that blocks until the latest feature flags have been retrieved from LaunchDarkly + * @param promise Either rejected if an error was encountered, otherwise resolved with null + * once client is initialized. + */ + @ReactMethod + public void configureWithTimeout(ReadableMap config, ReadableMap user, Integer timeout, final Promise promise) { + internalConfigure(config, user, timeout, promise); + } + + private void internalConfigure(ReadableMap config, ReadableMap user, final Integer timeout, final Promise promise) { if (ldClient != null) { promise.reject(ERROR_INIT, "Client was already initialized"); return; } final LDConfig.Builder ldConfigBuilder = configBuild(config); - final LDUser.Builder userBuilder = userBuild(userConfig); + final LDUser.Builder userBuilder = userBuild(user); if (ldConfigBuilder == null) { promise.reject(ERROR_INIT, "Client could not be built using supplied configuration"); @@ -238,6 +263,12 @@ public void configure(ReadableMap config, ReadableMap userConfig, final Promise return; } + if (config.hasKey("allUserAttributesPrivate") + && config.getType("allUserAttributesPrivate").equals(ConfigEntryType.Boolean.getReadableType()) + && config.getBoolean("allUserAttributesPrivate")) { + ldConfigBuilder.allAttributesPrivate(); + } + final Application application = (Application) getReactApplicationContext().getApplicationContext(); if (application != null) { @@ -245,13 +276,20 @@ public void configure(ReadableMap config, ReadableMap userConfig, final Promise @Override public void run() { try { - ldClient = LDClient.init(application, ldConfigBuilder.build(), userBuilder.build()).get(); + if (timeout != null) { + ldClient = LDClient.init(application, ldConfigBuilder.build(), userBuilder.build(), timeout).get(); + } else { + ldClient = LDClient.init(application, ldConfigBuilder.build(), userBuilder.build()).get(); + } promise.resolve(null); } catch (InterruptedException e) { - e.printStackTrace(); + Timber.w(e); promise.reject(ERROR_INIT, e); } catch (ExecutionException e) { - e.printStackTrace(); + Timber.w(e); + promise.reject(ERROR_INIT, e); + } catch (LaunchDarklyException e) { + Timber.w(e); promise.reject(ERROR_INIT, e); } } @@ -259,7 +297,7 @@ public void run() { background.start(); } else { - Timber.e("Couldn't initialize LaunchDarklyModule because the application was null"); + Timber.e("Couldn't initialize the LaunchDarkly module because the application was null"); promise.reject(ERROR_INIT, "Couldn't acquire application context"); } } @@ -396,89 +434,89 @@ private LDUser.Builder userBuild(ReadableMap options) { @ReactMethod public void boolVariation(String flagKey, Promise promise) { - boolVariationFallback(flagKey, null, promise); + boolVariationDefaultValue(flagKey, null, promise); } /** - * Looks up the current value for a flag, in the case of any issues, returns the given fallback + * Looks up the current value for a flag, in the case of any issues, returns the given default * value. * * @param flagKey The lookup key of the flag. - * @param fallback A fallback value to return if current value could not be acquired. + * @param defaultValue A default value to return if current value could not be acquired. * @param promise Used to return the result to React Native */ @ReactMethod - public void boolVariationFallback(String flagKey, Boolean fallback, Promise promise) { + public void boolVariationDefaultValue(String flagKey, Boolean defaultValue, Promise promise) { try { - promise.resolve(ldClient.boolVariation(flagKey, fallback)); + promise.resolve(ldClient.boolVariation(flagKey, defaultValue)); } catch (Exception e) { - promise.resolve(fallback); + promise.resolve(defaultValue); } } @ReactMethod public void intVariation(String flagKey, Promise promise) { - intVariationFallback(flagKey, null, promise); + intVariationDefaultValue(flagKey, null, promise); } /** - * Looks up the current value for a flag, in the case of any issues, returns the given fallback + * Looks up the current value for a flag, in the case of any issues, returns the given default * value. * * @param flagKey The lookup key of the flag. - * @param fallback A fallback value to return if current value could not be acquired. + * @param defaultValue A default value to return if current value could not be acquired. * @param promise Used to return the result to React Native */ @ReactMethod - public void intVariationFallback(String flagKey, Integer fallback, Promise promise) { + public void intVariationDefaultValue(String flagKey, Integer defaultValue, Promise promise) { try { - promise.resolve(ldClient.intVariation(flagKey, fallback)); + promise.resolve(ldClient.intVariation(flagKey, defaultValue)); } catch (Exception e) { - promise.resolve(fallback); + promise.resolve(defaultValue); } } @ReactMethod public void floatVariation(String flagKey, Promise promise) { - floatVariationFallback(flagKey, null, promise); + floatVariationDefaultValue(flagKey, null, promise); } /** - * Looks up the current value for a flag, in the case of any issues, returns the given fallback + * Looks up the current value for a flag, in the case of any issues, returns the given default * value. * * @param flagKey The lookup key of the flag. - * @param fallback A fallback value to return if current value could not be acquired. + * @param defaultValue A default value to return if current value could not be acquired. * @param promise Used to return the result to React Native */ @ReactMethod - public void floatVariationFallback(String flagKey, Float fallback, Promise promise) { + public void floatVariationDefaultValue(String flagKey, Double defaultValue, Promise promise) { try { - promise.resolve(ldClient.floatVariation(flagKey, fallback)); + promise.resolve(ldClient.doubleVariation(flagKey, defaultValue)); } catch (Exception e) { - promise.resolve(fallback); + promise.resolve(defaultValue); } } @ReactMethod public void stringVariation(String flagKey, Promise promise) { - stringVariationFallback(flagKey, null, promise); + stringVariationDefaultValue(flagKey, null, promise); } /** - * Looks up the current value for a flag, in the case of any issues, returns the given fallback + * Looks up the current value for a flag, in the case of any issues, returns the given default * value. * * @param flagKey The lookup key of the flag. - * @param fallback A fallback value to return if current value could not be acquired. + * @param defaultValue A default value to return if current value could not be acquired. * @param promise Used to return the result to React Native */ @ReactMethod - public void stringVariationFallback(String flagKey, String fallback, Promise promise) { + public void stringVariationDefaultValue(String flagKey, String defaultValue, Promise promise) { try { - promise.resolve(ldClient.stringVariation(flagKey, fallback)); + promise.resolve(ldClient.stringVariation(flagKey, defaultValue)); } catch (Exception e) { - promise.resolve(fallback); + promise.resolve(defaultValue); } } @@ -495,83 +533,83 @@ public void jsonVariationNone(String flagKey, Promise promise) { } /** - * Looks up the current value for a flag, in the case of any issues, returns the given fallback + * Looks up the current value for a flag, in the case of any issues, returns the given default * value. * * @param flagKey The lookup key of the flag. - * @param fallback A fallback value to return if current value could not be acquired. + * @param defaultValue A default value to return if current value could not be acquired. * @param promise Used to return the result to React Native */ @ReactMethod - public void jsonVariationNumber(String flagKey, Double fallback, Promise promise) { - jsonVariationBase(flagKey, new JsonPrimitive(fallback), promise); + public void jsonVariationNumber(String flagKey, Double defaultValue, Promise promise) { + jsonVariationBase(flagKey, new JsonPrimitive(defaultValue), promise); } /** - * Looks up the current value for a flag, in the case of any issues, returns the given fallback + * Looks up the current value for a flag, in the case of any issues, returns the given default * value. * * @param flagKey The lookup key of the flag. - * @param fallback A fallback value to return if current value could not be acquired. + * @param defaultValue A default value to return if current value could not be acquired. * @param promise Used to return the result to React Native */ @ReactMethod - public void jsonVariationBool(String flagKey, Boolean fallback, Promise promise) { - jsonVariationBase(flagKey, new JsonPrimitive(fallback), promise); + public void jsonVariationBool(String flagKey, Boolean defaultValue, Promise promise) { + jsonVariationBase(flagKey, new JsonPrimitive(defaultValue), promise); } /** - * Looks up the current value for a flag, in the case of any issues, returns the given fallback + * Looks up the current value for a flag, in the case of any issues, returns the given default * value. * * @param flagKey The lookup key of the flag. - * @param fallback A fallback value to return if current value could not be acquired. + * @param defaultValue A default value to return if current value could not be acquired. * @param promise Used to return the result to React Native */ @ReactMethod - public void jsonVariationString(String flagKey, String fallback, Promise promise) { - jsonVariationBase(flagKey, new JsonPrimitive(fallback), promise); + public void jsonVariationString(String flagKey, String defaultValue, Promise promise) { + jsonVariationBase(flagKey, new JsonPrimitive(defaultValue), promise); } /** - * Looks up the current value for a flag, in the case of any issues, returns the given fallback + * Looks up the current value for a flag, in the case of any issues, returns the given default * value. * * @param flagKey The lookup key of the flag. - * @param fallback A fallback value to return if current value could not be acquired. + * @param defaultValue A default value to return if current value could not be acquired. * @param promise Used to return the result to React Native */ @ReactMethod - public void jsonVariationArray(String flagKey, ReadableArray fallback, Promise promise) { - jsonVariationBase(flagKey, toJsonArray(fallback), promise); + public void jsonVariationArray(String flagKey, ReadableArray defaultValue, Promise promise) { + jsonVariationBase(flagKey, toJsonArray(defaultValue), promise); } /** - * Looks up the current value for a flag, in the case of any issues, returns the given fallback + * Looks up the current value for a flag, in the case of any issues, returns the given default * value. * * @param flagKey The lookup key of the flag. - * @param fallback A fallback value to return if current value could not be acquired. + * @param defaultValue A default value to return if current value could not be acquired. * @param promise Used to return the result to React Native */ @ReactMethod - public void jsonVariationObject(String flagKey, ReadableMap fallback, Promise promise) { - jsonVariationBase(flagKey, toJsonObject(fallback), promise); + public void jsonVariationObject(String flagKey, ReadableMap defaultValue, Promise promise) { + jsonVariationBase(flagKey, toJsonObject(defaultValue), promise); } @ReactMethod public void boolVariationDetail(String flagKey, Promise promise) { - boolVariationDetailFallback(flagKey, null, promise); + boolVariationDetailDefaultValue(flagKey, null, promise); } @ReactMethod - public void boolVariationDetailFallback(String flagKey, Boolean fallback, Promise promise) { + public void boolVariationDetailDefaultValue(String flagKey, Boolean defaultValue, Promise promise) { EvaluationDetail detailResult; try { - detailResult = ldClient.boolVariationDetail(flagKey, fallback); + detailResult = ldClient.boolVariationDetail(flagKey, defaultValue); } catch (Exception e) { - e.printStackTrace(); - detailResult = new EvaluationDetail(EvaluationReason.error(EvaluationReason.ErrorKind.EXCEPTION), null, fallback); + Timber.w(e); + detailResult = new EvaluationDetail(EvaluationReason.error(EvaluationReason.ErrorKind.EXCEPTION), null, defaultValue); } JsonObject jsonObject = gson.toJsonTree(detailResult).getAsJsonObject(); WritableMap detailMap = fromJsonObject(jsonObject); @@ -580,17 +618,17 @@ public void boolVariationDetailFallback(String flagKey, Boolean fallback, Promis @ReactMethod public void intVariationDetail(String flagKey, Promise promise) { - intVariationDetailFallback(flagKey, null, promise); + intVariationDetailDefaultValue(flagKey, null, promise); } @ReactMethod - public void intVariationDetailFallback(String flagKey, Integer fallback, Promise promise) { + public void intVariationDetailDefaultValue(String flagKey, Integer defaultValue, Promise promise) { EvaluationDetail detailResult; try { - detailResult = ldClient.intVariationDetail(flagKey, fallback); + detailResult = ldClient.intVariationDetail(flagKey, defaultValue); } catch (Exception e) { - e.printStackTrace(); - detailResult = new EvaluationDetail(EvaluationReason.error(EvaluationReason.ErrorKind.EXCEPTION), null, fallback); + Timber.w(e); + detailResult = new EvaluationDetail(EvaluationReason.error(EvaluationReason.ErrorKind.EXCEPTION), null, defaultValue); } JsonObject jsonObject = gson.toJsonTree(detailResult).getAsJsonObject(); WritableMap detailMap = fromJsonObject(jsonObject); @@ -599,17 +637,17 @@ public void intVariationDetailFallback(String flagKey, Integer fallback, Promise @ReactMethod public void floatVariationDetail(String flagKey, Promise promise) { - floatVariationDetailFallback(flagKey, null, promise); + floatVariationDetailDefaultValue(flagKey, null, promise); } @ReactMethod - public void floatVariationDetailFallback(String flagKey, Float fallback, Promise promise) { - EvaluationDetail detailResult; + public void floatVariationDetailDefaultValue(String flagKey, Double defaultValue, Promise promise) { + EvaluationDetail detailResult; try { - detailResult = ldClient.floatVariationDetail(flagKey, fallback); + detailResult = ldClient.doubleVariationDetail(flagKey, defaultValue); } catch (Exception e) { - e.printStackTrace(); - detailResult = new EvaluationDetail(EvaluationReason.error(EvaluationReason.ErrorKind.EXCEPTION), null, fallback); + Timber.w(e); + detailResult = new EvaluationDetail(EvaluationReason.error(EvaluationReason.ErrorKind.EXCEPTION), null, defaultValue); } JsonObject jsonObject = gson.toJsonTree(detailResult).getAsJsonObject(); WritableMap detailMap = fromJsonObject(jsonObject); @@ -618,17 +656,17 @@ public void floatVariationDetailFallback(String flagKey, Float fallback, Promise @ReactMethod public void stringVariationDetail(String flagKey, Promise promise) { - stringVariationDetailFallback(flagKey, null, promise); + stringVariationDetailDefaultValue(flagKey, null, promise); } @ReactMethod - public void stringVariationDetailFallback(String flagKey, String fallback, Promise promise) { + public void stringVariationDetailDefaultValue(String flagKey, String defaultValue, Promise promise) { EvaluationDetail detailResult; try { - detailResult = ldClient.stringVariationDetail(flagKey, fallback); + detailResult = ldClient.stringVariationDetail(flagKey, defaultValue); } catch (Exception e) { - e.printStackTrace(); - detailResult = new EvaluationDetail(EvaluationReason.error(EvaluationReason.ErrorKind.EXCEPTION), null, fallback); + Timber.w(e); + detailResult = new EvaluationDetail(EvaluationReason.error(EvaluationReason.ErrorKind.EXCEPTION), null, defaultValue); } JsonObject jsonObject = gson.toJsonTree(detailResult).getAsJsonObject(); WritableMap detailMap = fromJsonObject(jsonObject); @@ -641,53 +679,53 @@ public void jsonVariationDetailNone(String flagKey, Promise promise) { } @ReactMethod - public void jsonVariationDetailNumber(String flagKey, Double fallback, Promise promise) { - jsonVariationDetailBase(flagKey, new JsonPrimitive(fallback), promise); + public void jsonVariationDetailNumber(String flagKey, Double defaultValue, Promise promise) { + jsonVariationDetailBase(flagKey, new JsonPrimitive(defaultValue), promise); } @ReactMethod - public void jsonVariationDetailBool(String flagKey, Boolean fallback, Promise promise) { - jsonVariationDetailBase(flagKey, new JsonPrimitive(fallback), promise); + public void jsonVariationDetailBool(String flagKey, Boolean defaultValue, Promise promise) { + jsonVariationDetailBase(flagKey, new JsonPrimitive(defaultValue), promise); } @ReactMethod - public void jsonVariationDetailString(String flagKey, String fallback, Promise promise) { - jsonVariationDetailBase(flagKey, new JsonPrimitive(fallback), promise); + public void jsonVariationDetailString(String flagKey, String defaultValue, Promise promise) { + jsonVariationDetailBase(flagKey, new JsonPrimitive(defaultValue), promise); } @ReactMethod - public void jsonVariationDetailArray(String flagKey, ReadableArray fallback, Promise promise) { - jsonVariationDetailBase(flagKey, toJsonArray(fallback), promise); + public void jsonVariationDetailArray(String flagKey, ReadableArray defaultValue, Promise promise) { + jsonVariationDetailBase(flagKey, toJsonArray(defaultValue), promise); } @ReactMethod - public void jsonVariationDetailObject(String flagKey, ReadableMap fallback, Promise promise) { - jsonVariationDetailBase(flagKey, toJsonObject(fallback), promise); + public void jsonVariationDetailObject(String flagKey, ReadableMap defaultValue, Promise promise) { + jsonVariationDetailBase(flagKey, toJsonObject(defaultValue), promise); } /** * Helper for jsonVariation methods. * * @param flagKey The lookup key of the flag. - * @param fallback A fallback value to return if the current value could not be acquired. + * @param defaultValue A default value to return if the current value could not be acquired. * @param promise Used to return the result to React Native. */ - private void jsonVariationBase(String flagKey, JsonElement fallback, Promise promise) { + private void jsonVariationBase(String flagKey, JsonElement defaultValue, Promise promise) { try { - JsonElement jsonElement = ldClient.jsonVariation(flagKey, fallback); + JsonElement jsonElement = ldClient.jsonVariation(flagKey, defaultValue); resolveJsonElement(promise, jsonElement); } catch (Exception e) { - resolveJsonElement(promise, fallback); + resolveJsonElement(promise, defaultValue); } } - private void jsonVariationDetailBase(String flagKey, JsonElement fallback, Promise promise) { + private void jsonVariationDetailBase(String flagKey, JsonElement defaultValue, Promise promise) { EvaluationDetail jsonElementDetail; try { - jsonElementDetail = ldClient.jsonVariationDetail(flagKey, fallback); + jsonElementDetail = ldClient.jsonVariationDetail(flagKey, defaultValue); } catch (Exception e) { - e.printStackTrace(); - jsonElementDetail = new EvaluationDetail(EvaluationReason.error(EvaluationReason.ErrorKind.EXCEPTION), null, fallback); + Timber.w(e); + jsonElementDetail = new EvaluationDetail(EvaluationReason.error(EvaluationReason.ErrorKind.EXCEPTION), null, defaultValue); } resolveJsonElementDetail(promise, jsonElementDetail); } @@ -731,6 +769,11 @@ private void resolveJsonElementDetail(Promise promise, EvaluationDetail flags = ldClient.allFlags(); // Convert map of all flags into WritableMap for React Native @@ -790,7 +833,11 @@ public void allFlags(Promise promise) { */ @ReactMethod public void trackNumber(String eventName, Double data) { - ldClient.track(eventName, new JsonPrimitive(data)); + try { + ldClient.track(eventName, new JsonPrimitive(data)); + } catch (Exception e) { + Timber.w(e); + } } /** @@ -804,7 +851,11 @@ public void trackNumber(String eventName, Double data) { */ @ReactMethod public void trackBool(String eventName, Boolean data) { - ldClient.track(eventName, new JsonPrimitive(data)); + try { + ldClient.track(eventName, new JsonPrimitive(data)); + } catch (Exception e) { + Timber.w(e); + } } /** @@ -818,7 +869,11 @@ public void trackBool(String eventName, Boolean data) { */ @ReactMethod public void trackString(String eventName, String data) { - ldClient.track(eventName, new JsonPrimitive(data)); + try { + ldClient.track(eventName, new JsonPrimitive(data)); + } catch (Exception e) { + Timber.w(e); + } } /** @@ -832,7 +887,11 @@ public void trackString(String eventName, String data) { */ @ReactMethod public void trackArray(String eventName, ReadableArray data) { - ldClient.track(eventName, toJsonArray(data)); + try { + ldClient.track(eventName, toJsonArray(data)); + } catch (Exception e) { + Timber.w(e); + } } /** @@ -846,7 +905,11 @@ public void trackArray(String eventName, ReadableArray data) { */ @ReactMethod public void trackObject(String eventName, ReadableMap data) { - ldClient.track(eventName, toJsonObject(data)); + try { + ldClient.track(eventName, toJsonObject(data)); + } catch (Exception e) { + Timber.w(e); + } } /** @@ -856,37 +919,65 @@ public void trackObject(String eventName, ReadableMap data) { */ @ReactMethod public void track(String eventName) { - ldClient.track(eventName); + try { + ldClient.track(eventName); + } catch (Exception e) { + Timber.w(e); + } } @ReactMethod public void trackNumberMetricValue(String eventName, Double data, Double metricValue) { - ldClient.track(eventName, new JsonPrimitive(data), metricValue); + try { + ldClient.track(eventName, new JsonPrimitive(data), metricValue); + } catch (Exception e) { + Timber.w(e); + } } @ReactMethod public void trackBoolMetricValue(String eventName, Boolean data, Double metricValue) { - ldClient.track(eventName, new JsonPrimitive(data), metricValue); + try { + ldClient.track(eventName, new JsonPrimitive(data), metricValue); + } catch (Exception e) { + Timber.w(e); + } } @ReactMethod public void trackStringMetricValue(String eventName, String data, Double metricValue) { - ldClient.track(eventName, new JsonPrimitive(data), metricValue); + try { + ldClient.track(eventName, new JsonPrimitive(data), metricValue); + } catch (Exception e) { + Timber.w(e); + } } @ReactMethod public void trackArrayMetricValue(String eventName, ReadableArray data, Double metricValue) { - ldClient.track(eventName, toJsonArray(data), metricValue); + try { + ldClient.track(eventName, toJsonArray(data), metricValue); + } catch (Exception e) { + Timber.w(e); + } } @ReactMethod public void trackObjectMetricValue(String eventName, ReadableMap data, Double metricValue) { - ldClient.track(eventName, toJsonObject(data), metricValue); + try { + ldClient.track(eventName, toJsonObject(data), metricValue); + } catch (Exception e) { + Timber.w(e); + } } @ReactMethod public void trackMetricValue(String eventName, Double metricValue) { - ldClient.track(eventName, new JsonPrimitive(""), metricValue); + try { + ldClient.track(eventName, new JsonPrimitive(""), metricValue); + } catch (Exception e) { + Timber.w(e); + } } /** @@ -895,8 +986,12 @@ public void trackMetricValue(String eventName, Double metricValue) { */ @ReactMethod public void setOffline(Promise promise) { - ldClient.setOffline(); - promise.resolve(true); + try { + ldClient.setOffline(); + promise.resolve(true); + } catch (Exception e) { + promise.reject(ERROR_UNKNOWN, e); + } } /** @@ -919,8 +1014,12 @@ public void isOffline(Promise promise) { */ @ReactMethod public void setOnline(Promise promise) { - ldClient.setOnline(); - promise.resolve(true); + try { + ldClient.setOnline(); + promise.resolve(true); + } catch (Exception e) { + promise.reject(ERROR_UNKNOWN, e); + } } /** @@ -931,6 +1030,11 @@ public void setOnline(Promise promise) { */ @ReactMethod public void isInitialized(Promise promise) { + if (ldClient == null) { + promise.resolve(false); + return; + } + try { boolean result = ldClient.isInitialized(); promise.resolve(result); @@ -944,7 +1048,11 @@ public void isInitialized(Promise promise) { */ @ReactMethod public void flush() { - ldClient.flush(); + try { + ldClient.flush(); + } catch (Exception e) { + Timber.w(e); + } } /** @@ -981,32 +1089,51 @@ public void run() { ldClient.identify(userBuilder.build()).get(); promise.resolve(null); } catch (InterruptedException e) { - e.printStackTrace(); + Timber.w(e); promise.reject(ERROR_IDENTIFY, "Identify Interrupted"); } catch (ExecutionException e) { - e.printStackTrace(); + Timber.w(e); promise.reject(ERROR_IDENTIFY, "Exception while executing identify"); + } catch (Exception e) { + Timber.w(e); + promise.reject(ERROR_UNKNOWN, e); } } }); - background.run(); + background.start(); } @ReactMethod - public void isDisableBackgroundPolling(Promise promise) { + public void getConnectionMode(Promise promise) { try { - boolean result = ldClient.isDisableBackgroundPolling(); - promise.resolve(result); + promise.resolve(ldClient.getConnectionInformation().getConnectionMode().name()); } catch (Exception e) { promise.reject(ERROR_UNKNOWN, e); } } @ReactMethod - public void getConnectionInformation(Promise promise) { + public void getLastSuccessfulConnection(Promise promise) { try { - ConnectionInformation result = ldClient.getConnectionInformation(); - promise.resolve(result); + promise.resolve(ldClient.getConnectionInformation().getLastSuccessfulConnection().intValue()); + } catch (Exception e) { + promise.reject(ERROR_UNKNOWN, e); + } + } + + @ReactMethod + public void getLastFailedConnection(Promise promise) { + try { + promise.resolve(ldClient.getConnectionInformation().getLastFailedConnection().intValue()); + } catch (Exception e) { + promise.reject(ERROR_UNKNOWN, e); + } + } + + @ReactMethod + public void getLastFailure(Promise promise) { + try { + promise.resolve(ldClient.getConnectionInformation().getLastFailure().getFailureType().name()); } catch (Exception e) { promise.reject(ERROR_UNKNOWN, e); } @@ -1026,15 +1153,23 @@ public void onFeatureFlagChange(String flagKey) { } }; - ldClient.registerFeatureFlagListener(flagKey, listener); - listeners.put(flagKey, listener); + try { + ldClient.registerFeatureFlagListener(flagKey, listener); + listeners.put(flagKey, listener); + } catch (Exception e) { + Timber.w(e); + } } @ReactMethod public void unregisterFeatureFlagListener(String flagKey) { - if (listeners.containsKey(flagKey)) { - ldClient.unregisterFeatureFlagListener(flagKey, listeners.get(flagKey)); - listeners.remove(flagKey); + try { + if (listeners.containsKey(flagKey)) { + ldClient.unregisterFeatureFlagListener(flagKey, listeners.get(flagKey)); + listeners.remove(flagKey); + } + } catch (Exception e) { + Timber.w(e); } } @@ -1055,15 +1190,23 @@ public void onConnectionModeChanged(ConnectionInformation connectionInfo) { public void onInternalFailure(LDFailure ldFailure) {} }; - ldClient.registerStatusListener(listener); - connectionModeListeners.put(listenerId, listener); + try { + ldClient.registerStatusListener(listener); + connectionModeListeners.put(listenerId, listener); + } catch (Exception e) { + Timber.w(e); + } } @ReactMethod public void unregisterCurrentConnectionModeListener(String listenerId) { - if (connectionModeListeners.containsKey(listenerId)) { - ldClient.unregisterStatusListener(connectionModeListeners.get(listenerId)); - connectionModeListeners.remove(listenerId); + try { + if (connectionModeListeners.containsKey(listenerId)) { + ldClient.unregisterStatusListener(connectionModeListeners.get(listenerId)); + connectionModeListeners.remove(listenerId); + } + } catch (Exception e) { + Timber.w(e); } } @@ -1081,15 +1224,23 @@ public void onChange(List flagKeys) { } }; - ldClient.registerAllFlagsListener(listener); - allFlagsListeners.put(listenerId, listener); + try { + ldClient.registerAllFlagsListener(listener); + allFlagsListeners.put(listenerId, listener); + } catch (Exception e) { + Timber.w(e); + } } @ReactMethod public void unregisterAllFlagsListener(String listenerId) { - if (allFlagsListeners.containsKey(listenerId)) { - ldClient.unregisterAllFlagsListener(allFlagsListeners.get(listenerId)); - allFlagsListeners.remove(listenerId); + try { + if (allFlagsListeners.containsKey(listenerId)) { + ldClient.unregisterAllFlagsListener(allFlagsListeners.get(listenerId)); + allFlagsListeners.remove(listenerId); + } + } catch (Exception e) { + Timber.w(e); } } diff --git a/index.d.ts b/index.d.ts index aef0a23..9c01977 100644 --- a/index.d.ts +++ b/index.d.ts @@ -11,7 +11,7 @@ declare module 'launchdarkly-react-native-client-sdk' { /** * Configuration options for the LaunchDarkly React Native SDK. */ - export type LDClientConfig = { + export type LDConfig = { /** * The mobile SDK key associated with your LaunchDarkly environment. * @@ -21,11 +21,11 @@ declare module 'launchdarkly-react-native-client-sdk' { mobileKey: string; /** - * The base URI for the LaunchDarkly server. + * The base URI for the LaunchDarkly polling server. * * Most users should use the default value. */ - baseUri?: string; + pollUri?: string; /** * The base URI for the LaunchDarkly streaming server. @@ -145,12 +145,34 @@ declare module 'launchdarkly-react-native-client-sdk' { * such information is not sent unless you set this option to true. */ evaluationReasons?: boolean; + + /** + * Setting for the maximum number of locally cached users. Default is 5 users. + */ + maxCachedUsers?: number; + + /** + * Setting for whether sending diagnostic data about the SDK is disabled. The default is false. + */ + diagnosticOptOut?: boolean; + + /** + * The time interval between sending periodic diagnostic data. The default is 900000 (15 minutes). + */ + diagnosticRecordingIntervalMillis?: number; + + /** + * Whether to treat all user attributes as private for event reporting for all users. + * The SDK will not include private attribute values in analytics events, but private attribute names will be sent. + * The default is false. + */ + allUserAttributesPrivate?: boolean; }; /** * A LaunchDarkly user object. */ - export type LDUserConfig = { + export type LDUser = { /** * A unique string identifying a user. @@ -201,6 +223,16 @@ declare module 'launchdarkly-react-native-client-sdk' { * Any additional attributes associated with the user. */ custom?: { [key: string]: any }; + + /** + * The IP address associated with the user. + */ + ip?: string; + + /** + * The avatar associated with the user. + */ + avatar?: string; }; /** @@ -284,6 +316,105 @@ declare module 'launchdarkly-react-native-client-sdk' { */ ERROR = 'ERROR', } + + /** + * Describes what state of connection to LaunchDarkly the SDK is in. + */ + export enum LDConnectionMode { + /** + * The SDK is either connected to the flag stream, or is actively attempting to acquire a connection. + */ + STREAMING = 'STREAMING', + + /** + * The SDK was configured with streaming disabled, and is in foreground polling mode. + */ + POLLING = 'POLLING', + + /** + * (Android specific enum value) The SDK has detected the application is in the background and has transitioned to + * battery conscious background polling. + */ + BACKGROUND_POLLING = 'BACKGROUND_POLLING', + + /** + * (Android specific enum value) The SDK was configured with background polling disabled. The SDK has detected + * the application is in the background and is not attempting to update the flag cache. + */ + BACKGROUND_DISABLED = 'BACKGROUND_DISABLED', + + /** + * The SDK has detected that the mobile device does not have an active network connection so has ceased flag update + * attempts until the network status changes. + */ + OFFLINE = 'OFFLINE', + + /** + * (Android specific enum value) The SDK has been explicitly set offline, either in the initial configuration, + * by setOffline(), or as a result of failed authentication to LaunchDarkly. + * The SDK will stay offline unless setOnline() is called. + */ + SET_OFFLINE = 'SET_OFFLINE', + + /** + * (Android specific enum value) The shutdown state indicates the SDK has been permanently shutdown as a result of + * a call to close(). + */ + SHUTDOWN = 'SHUTDOWN', + + /** + * (iOS specific enum value) The SDK is attempting to connect to LaunchDarkly by streaming. + */ + ESTABLISHING_STREAMING_CONNECTION = 'ESTABLISHING_STREAMING_CONNECTION', + } + + /** + * Describes why a connection request to LaunchDarkly failed. + */ + export enum LDFailureReason { + /** + * This indicates when no error has been recorded. + */ + NONE = 'NONE', + + /** + * This indicates when there is an internal error in the stream request. + */ + UKNOWN_ERROR ='UNKNOWN_ERROR', + + /** + * (iOS specific enum value) This indicates when an incorrect mobile key is provided. + */ + UNAUTHORIZED = 'UNAUTHORIZED', + + /** + * (iOS specific enum value) This indicates when an error with an HTTP error code is present. + */ + HTTP_ERROR = 'HTTP_ERROR', + + /** + * (Android specific enum value) This indicates the LDFailure is an instance of LDInvalidResponseCodeFailure. + * See Android documentation for more details. + */ + UNEXPECTED_RESPONSE_CODE = 'UNEXPECTED_RESPONSE_CODE', + + /** + * (Android specific enum value) An event was received through the stream was had an unknown event name. + * This could indicate a newer SDK is available if new event types have become available through the + * flag stream since the SDKs release. + */ + UNEXPECTED_STREAM_ELEMENT_TYPE = 'UNEXPECTED_STREAM_ELEMENT_TYPE', + + /** + * (Android specific enum value) A network request for polling, or the EventSource stream reported a failure. + */ + NETWORK_FAILURE = 'NETWORK_FAILURE', + + /** + * (Android specific enum value) A response body received either through polling or streaming was unable to be parsed. + */ + INVALID_RESPONSE_BODY = 'INVALID_RESPONSE_BODY', + } /** * The flag is off and therefore returned its configured off value. @@ -408,184 +539,196 @@ declare module 'launchdarkly-react-native-client-sdk' { export default class LDClient { constructor(); + /** + * Returns the SDK version. + * + * @returns + * A string containing the SDK version. + */ + getVersion(): string; + /** * Initialize the SDK to work with the specified client configuration options and on behalf of the specified user. + * Will block for a number of seconds represented until flags are received from LaunchDarkly if the timeout parameter + * is passed. * * This should only be called once at application start time. * * @param config * the client configuration options - * @param userConfig + * @param user * the user + * @param timeout + * (Optional) A number representing how long to wait for flags */ - configure(config: LDClientConfig, userConfig: LDUserConfig): Promise; + configure(config: LDConfig, user: LDUser, timeout?: number): Promise; /** * Determines the variation of a boolean feature flag for the current user. * * @param flagKey * The unique key of the feature flag. - * @param fallback + * @param defaultValue * The default value of the flag, to be used if the value is not available from LaunchDarkly. * @returns * A promise containing the flag's value. */ - boolVariation(flagKey: string, fallback: boolean): Promise; + boolVariation(flagKey: string, defaultValue: boolean): Promise; /** * Determines the variation of an integer feature flag for the current user. * * @param flagKey * The unique key of the feature flag. - * @param fallback + * @param defaultValue * The default value of the flag, to be used if the value is not available from LaunchDarkly. * @returns * A promise containing the flag's value. */ - intVariation(flagKey: string, fallback: number): Promise; + intVariation(flagKey: string, defaultValue: number): Promise; /** * Determines the variation of a floating-point feature flag for the current user. * * @param flagKey * The unique key of the feature flag. - * @param fallback + * @param defaultValue * The default value of the flag, to be used if the value is not available from LaunchDarkly. * @returns * A promise containing the flag's value. */ - floatVariation(flagKey: string, fallback: number): Promise; + floatVariation(flagKey: string, defaultValue: number): Promise; /** * Determines the variation of a string feature flag for the current user. * * @param flagKey * The unique key of the feature flag. - * @param fallback + * @param defaultValue * The default value of the flag, to be used if the value is not available from LaunchDarkly. * @returns * A promise containing the flag's value. */ - stringVariation(flagKey: string, fallback: string): Promise; + stringVariation(flagKey: string, defaultValue: string): Promise; /** * Determines the variation of a JSON feature flag for the current user. * * @param flagKey * The unique key of the feature flag. - * @param fallback + * @param defaultValue * The default value of the flag, to be used if the value is not available from LaunchDarkly. * @returns * A promise containing the flag's value. */ jsonVariation( flagKey: string, - fallback: Record, + defaultValue: Record, ): Promise>; /** * Determines the variation of a boolean feature flag for a user, along with information about how it was * calculated. * - * Note that this will only work if you have set `evaluationReasons` to true in [[LDClientConfig]]. + * Note that this will only work if you have set `evaluationReasons` to true in [[LDConfig]]. * Otherwise, the `reason` property of the result will be null. * * For more information, see the [SDK reference guide](https://docs.launchdarkly.com/sdk/concepts/evaluation-reasons). * * @param flagKey * The unique key of the feature flag. - * @param fallback + * @param defaultValue * The default value of the flag, to be used if the value is not available from LaunchDarkly. * @returns * A promise containing an [[LDEvaluationDetail]] object containing the value and explanation. */ boolVariationDetail( flagKey: string, - fallback: boolean, + defaultValue: boolean, ): Promise>; /** * Determines the variation of an integer feature flag for a user, along with information about how it was * calculated. * - * Note that this will only work if you have set `evaluationReasons` to true in [[LDClientConfig]]. + * Note that this will only work if you have set `evaluationReasons` to true in [[LDConfig]]. * Otherwise, the `reason` property of the result will be null. * * For more information, see the [SDK reference guide](https://docs.launchdarkly.com/sdk/concepts/evaluation-reasons). * * @param flagKey * The unique key of the feature flag. - * @param fallback + * @param defaultValue * The default value of the flag, to be used if the value is not available from LaunchDarkly. * @returns * A promise containing an [[LDEvaluationDetail]] object containing the value and explanation. */ intVariationDetail( flagKey: string, - fallback: number, + defaultValue: number, ): Promise>; /** * Determines the variation of a floating-point feature flag for a user, along with information about how it was * calculated. * - * Note that this will only work if you have set `evaluationReasons` to true in [[LDClientConfig]]. + * Note that this will only work if you have set `evaluationReasons` to true in [[LDConfig]]. * Otherwise, the `reason` property of the result will be null. * * For more information, see the [SDK reference guide](https://docs.launchdarkly.com/sdk/concepts/evaluation-reasons). * * @param flagKey * The unique key of the feature flag. - * @param fallback + * @param defaultValue * The default value of the flag, to be used if the value is not available from LaunchDarkly. * @returns * A promise containing an [[LDEvaluationDetail]] object containing the value and explanation. */ floatVariationDetail( flagKey: string, - fallback: number, + defaultValue: number, ): Promise>; /** * Determines the variation of a string feature flag for a user, along with information about how it was * calculated. * - * Note that this will only work if you have set `evaluationReasons` to true in [[LDClientConfig]]. + * Note that this will only work if you have set `evaluationReasons` to true in [[LDConfig]]. * Otherwise, the `reason` property of the result will be null. * * For more information, see the [SDK reference guide](https://docs.launchdarkly.com/sdk/concepts/evaluation-reasons). * * @param flagKey * The unique key of the feature flag. - * @param fallback + * @param defaultValue * The default value of the flag, to be used if the value is not available from LaunchDarkly. * @returns * A promise containing an [[LDEvaluationDetail]] object containing the value and explanation. */ stringVariationDetail( flagKey: string, - fallback: string, + defaultValue: string, ): Promise>; /** * Determines the variation of a JSON feature flag for a user, along with information about how it was * calculated. * - * Note that this will only work if you have set `evaluationReasons` to true in [[LDClientConfig]]. + * Note that this will only work if you have set `evaluationReasons` to true in [[LDConfig]]. * Otherwise, the `reason` property of the result will be null. * * For more information, see the [SDK reference guide](https://docs.launchdarkly.com/sdk/concepts/evaluation-reasons). * * @param flagKey * The unique key of the feature flag. - * @param fallback + * @param defaultValue * The default value of the flag, to be used if the value is not available from LaunchDarkly. * @returns * A promise containing an [[LDEvaluationDetail]] object containing the value and explanation. */ jsonVariationDetail( flagKey: string, - fallback: Record, + defaultValue: Record, ): Promise>>; /** @@ -593,6 +736,7 @@ declare module 'launchdarkly-react-native-client-sdk' { * * @returns * A promise containing an object in which each key is a feature flag key and each value is the flag value. + * The promise will be rejected if the SDK has not yet completed initialization. * Note that there is no way to specify a default value for each flag as there is with the * `*Variation` methods, so any flag that cannot be evaluated will have a null value. */ @@ -612,7 +756,7 @@ declare module 'launchdarkly-react-native-client-sdk' { /** * Checks whether the client has been put into offline mode. This is true only if [[setOffline]] - * was called, or if the configuration had [[LDClientConfig.offline]] set to true, + * was called, or if the configuration had [[LDConfig.offline]] set to true, * not if the client is simply offline due to a loss of network connectivity. * * @returns @@ -655,20 +799,12 @@ declare module 'launchdarkly-react-native-client-sdk' { * A promise contianing true if the client is initialized or offline */ isInitialized(): Promise; - - /** - * Checks whether the `disableBackgroundUpdating` property of [[LDClientConfig]] was set to true. - * - * @returns - * A promise containing true if background polling is disabled - */ - isDisableBackgroundPolling(): Promise; /** * Flushes all pending analytics events. * * Normally, batches of events are delivered in the background at intervals determined by the - * `eventsFlushIntervalMillis` property of [[LDClientConfig]]. Calling `flush` triggers an + * `eventsFlushIntervalMillis` property of [[LDConfig]]. Calling `flush` triggers an * immediate delivery. */ flush(): void; @@ -683,12 +819,12 @@ declare module 'launchdarkly-react-native-client-sdk' { /** * Sets the current user, retrieves flags for that user, then sends an Identify Event to LaunchDarkly. * - * @param userConfig + * @param user * The user for evaluation and event reporting * @returns * A promise indicating when this operation is complete (meaning that flags are ready for evaluation). */ - identify(userConfig: LDUserConfig): Promise; + identify(user: LDUser): Promise; /** * Registers a callback to be called when the flag with key `flagKey` changes from its current value. @@ -715,14 +851,43 @@ declare module 'launchdarkly-react-native-client-sdk' { flagKey: string, callback: (flagKey: string) => void, ): void; - + + /** + * Returns the current state of the connection to LaunchDarkly. + * + * @returns + * A promise containing a LDConnectionMode enum value representing the status of the connection to LaunchDarkly. + */ + getConnectionMode(): Promise; + + /** + * Returns the most recent successful flag cache update in millis from the epoch + * or null if flags have never been retrieved. + * + * @returns + * A promise containing a number representing the status of the connection to LaunchDarkly, + * or null if a successful connection has yet to be established. + */ + getLastSuccessfulConnection(): Promise; + + /** + * Most recent unsuccessful flag cache update attempt in millis from the epoch + * or null if flag update has never been attempted. + * + * @returns + * A promise containing a number representing the status of the connection to LaunchDarkly, + * or null if a failed connection has yet to occur. + */ + getLastFailedConnection(): Promise; + /** - * Gets an object from the client representing the current state of the client's connection. + * Returns the most recent connection failure reason or null. * * @returns - * A promise containing an object representing the status of the connection to LaunchDarkly. + * A promise containing a LDFailureReason enum value representing the reason for the most recently failed + * connection to LaunchDarkly, or null if a failed connection has yet to occur. */ - getConnectionInformation(): Promise; + getLastFailure(): Promise; /** * Registers a callback to be called on connection status updates. diff --git a/index.js b/index.js index 94b110e..32464cd 100644 --- a/index.js +++ b/index.js @@ -1,4 +1,5 @@ import { NativeModules, NativeEventEmitter, Platform } from 'react-native'; +import { version } from './package.json'; let LaunchdarklyReactNativeClient = NativeModules.LaunchdarklyReactNativeClient; @@ -13,110 +14,124 @@ export default class LDClient { this.eventEmitter.addListener(LaunchdarklyReactNativeClient.CONNECTION_MODE_PREFIX, body => this._connectionModeUpdateListener(body)); } - configure(config, userConfig) { + getVersion() { + return String(version); + } + + configure(config, user, timeout) { + if (this.isInitialized() == true) { + Promise.reject('LaunchDarkly SDK already initialized'); + } const configWithOverriddenDefaults = Object.assign({ backgroundPollingIntervalMillis: 3600000, // the iOS SDK defaults this to 900000 - disableBackgroundUpdating: false // the iOS SDK defaults this to true + disableBackgroundUpdating: false, // the iOS SDK defaults this to true + pollUri: 'https://clientsdk.launchdarkly.com', + wrapperName: 'react-native-client-sdk', + wrapperVersion: this.getVersion() }, config); - return LaunchdarklyReactNativeClient.configure(configWithOverriddenDefaults, this._addUserOverrides(userConfig)); + if (timeout == undefined) { + return LaunchdarklyReactNativeClient.configure(configWithOverriddenDefaults, this._addUserOverrides(user)); + } else { + return LaunchdarklyReactNativeClient.configureWithTimeout(configWithOverriddenDefaults, this._addUserOverrides(user), timeout); + } } - boolVariation(flagKey, fallback) { - if (fallback == undefined) { + boolVariation(flagKey, defaultValue) { + if (defaultValue == undefined) { return LaunchdarklyReactNativeClient.boolVariation(flagKey); } else { - return LaunchdarklyReactNativeClient.boolVariationFallback(flagKey, fallback); + return LaunchdarklyReactNativeClient.boolVariationDefaultValue(flagKey, defaultValue); } } - intVariation(flagKey, fallback) { - if (fallback == undefined) { + intVariation(flagKey, defaultValue) { + if (defaultValue == undefined) { return LaunchdarklyReactNativeClient.intVariation(flagKey); } else { - return LaunchdarklyReactNativeClient.intVariationFallback(flagKey, fallback); + return LaunchdarklyReactNativeClient.intVariationDefaultValue(flagKey, defaultValue); } } - floatVariation(flagKey, fallback) { - if (fallback == undefined) { + floatVariation(flagKey, defaultValue) { + if (defaultValue == undefined) { return LaunchdarklyReactNativeClient.floatVariation(flagKey); } else { - return LaunchdarklyReactNativeClient.floatVariationFallback(flagKey, fallback); + return LaunchdarklyReactNativeClient.floatVariationDefaultValue(flagKey, defaultValue); } } - stringVariation(flagKey, fallback) { - if (fallback == undefined) { + stringVariation(flagKey, defaultValue) { + if (defaultValue == undefined) { return LaunchdarklyReactNativeClient.stringVariation(flagKey); } else { - return LaunchdarklyReactNativeClient.stringVariationFallback(flagKey, fallback); + return LaunchdarklyReactNativeClient.stringVariationDefaultValue(flagKey, defaultValue); } } - jsonVariation(flagKey, fallback) { - if (fallback == undefined) { + jsonVariation(flagKey, defaultValue) { + if (defaultValue == undefined) { return LaunchdarklyReactNativeClient.jsonVariationNone(flagKey); - } else if (typeof fallback === 'number') { - return LaunchdarklyReactNativeClient.jsonVariationNumber(flagKey, fallback); - } else if (typeof fallback === 'boolean') { - return LaunchdarklyReactNativeClient.jsonVariationBool(flagKey, fallback); - } else if (typeof fallback === 'string') { - return LaunchdarklyReactNativeClient.jsonVariationString(flagKey, fallback); - } else if (Array.isArray(fallback)) { - return LaunchdarklyReactNativeClient.jsonVariationArray(flagKey, fallback); + } else if (typeof defaultValue === 'number') { + return LaunchdarklyReactNativeClient.jsonVariationNumber(flagKey, defaultValue); + } else if (typeof defaultValue === 'boolean') { + return LaunchdarklyReactNativeClient.jsonVariationBool(flagKey, defaultValue); + } else if (typeof defaultValue === 'string') { + return LaunchdarklyReactNativeClient.jsonVariationString(flagKey, defaultValue); + } else if (Array.isArray(defaultValue)) { + return LaunchdarklyReactNativeClient.jsonVariationArray(flagKey, defaultValue); } else { // Should be an object - return LaunchdarklyReactNativeClient.jsonVariationObject(flagKey, fallback); + return LaunchdarklyReactNativeClient.jsonVariationObject(flagKey, defaultValue); } } - boolVariationDetail(flagKey, fallback) { - if (fallback == undefined) { + boolVariationDetail(flagKey, defaultValue) { + if (defaultValue == undefined) { return LaunchdarklyReactNativeClient.boolVariationDetail(flagKey); } else { - return LaunchdarklyReactNativeClient.boolVariationDetailFallback(flagKey, fallback); + return LaunchdarklyReactNativeClient.boolVariationDetailDefaultValue(flagKey, defaultValue); } } - intVariationDetail(flagKey, fallback) { - if (fallback == undefined) { + intVariationDetail(flagKey, defaultValue) { + if (defaultValue == undefined) { return LaunchdarklyReactNativeClient.intVariationDetail(flagKey); } else { - return LaunchdarklyReactNativeClient.intVariationDetailFallback(flagKey, fallback); + return LaunchdarklyReactNativeClient.intVariationDetailDefaultValue(flagKey, defaultValue); } } - floatVariationDetail(flagKey, fallback) { - if (fallback == undefined) { + floatVariationDetail(flagKey, defaultValue) { + if (defaultValue == undefined) { return LaunchdarklyReactNativeClient.floatVariationDetail(flagKey); } else { - return LaunchdarklyReactNativeClient.floatVariationDetailFallback(flagKey, fallback); + return LaunchdarklyReactNativeClient.floatVariationDetailDefaultValue(flagKey, defaultValue); } } - stringVariationDetail(flagKey, fallback) { - if (fallback == undefined) { + stringVariationDetail(flagKey, defaultValue) { + if (defaultValue == undefined) { return LaunchdarklyReactNativeClient.stringVariationDetail(flagKey); } else { - return LaunchdarklyReactNativeClient.stringVariationDetailFallback(flagKey, fallback); + return LaunchdarklyReactNativeClient.stringVariationDetailDefaultValue(flagKey, defaultValue); } } - jsonVariationDetail(flagKey, fallback) { - if (fallback == undefined) { + jsonVariationDetail(flagKey, defaultValue) { + if (defaultValue == undefined) { return LaunchdarklyReactNativeClient.jsonVariatioDetailNone(flagKey); - } else if (typeof fallback === 'number') { - return LaunchdarklyReactNativeClient.jsonVariationDetailNumber(flagKey, fallback); - } else if (typeof fallback === 'boolean') { - return LaunchdarklyReactNativeClient.jsonVariationDetailBool(flagKey, fallback); - } else if (typeof fallback === 'string') { - return LaunchdarklyReactNativeClient.jsonVariationDetailString(flagKey, fallback); - } else if (Array.isArray(fallback)) { - return LaunchdarklyReactNativeClient.jsonVariationDetailArray(flagKey, fallback); + } else if (typeof defaultValue === 'number') { + return LaunchdarklyReactNativeClient.jsonVariationDetailNumber(flagKey, defaultValue); + } else if (typeof defaultValue === 'boolean') { + return LaunchdarklyReactNativeClient.jsonVariationDetailBool(flagKey, defaultValue); + } else if (typeof defaultValue === 'string') { + return LaunchdarklyReactNativeClient.jsonVariationDetailString(flagKey, defaultValue); + } else if (Array.isArray(defaultValue)) { + return LaunchdarklyReactNativeClient.jsonVariationDetailArray(flagKey, defaultValue); } else { // Should be an object - return LaunchdarklyReactNativeClient.jsonVariationDetailObject(flagKey, fallback); + return LaunchdarklyReactNativeClient.jsonVariationDetailObject(flagKey, defaultValue); } } @@ -171,11 +186,7 @@ export default class LDClient { } isInitialized() { - if(Platform.OS === 'android') { - return LaunchdarklyReactNativeClient.isInitialized(); - } else { - return Promise.reject("Function is not available on this platform"); - } + return LaunchdarklyReactNativeClient.isInitialized(); } flush() { @@ -186,18 +197,14 @@ export default class LDClient { LaunchdarklyReactNativeClient.close(); } - identify(userConfig) { - return LaunchdarklyReactNativeClient.identify(this._addUserOverrides(userConfig)); + identify(user) { + return LaunchdarklyReactNativeClient.identify(this._addUserOverrides(user)); } - _addUserOverrides(userConfig) { + _addUserOverrides(user) { return Object.assign({ anonymous: false // the iOS SDK defaults this to true - }, userConfig); - } - - isDisableBackgroundPolling() { - return LaunchdarklyReactNativeClient.isDisableBackgroundPolling(); + }, user); } _flagUpdateListener(changedFlag) { @@ -253,10 +260,6 @@ export default class LDClient { } } - getConnectionInformation() { - return LaunchdarklyReactNativeClient.getConnectionInformation(); - } - registerCurrentConnectionModeListener(listenerId, callback) { if (typeof callback !== "function") { return; @@ -290,4 +293,20 @@ export default class LDClient { LaunchdarklyReactNativeClient.unregisterAllFlagsListener(listenerId); delete this.allFlagsListeners[listenerId]; } + + getConnectionMode() { + return LaunchdarklyReactNativeClient.getConnectionMode(); + } + + getLastSuccessfulConnection() { + return LaunchdarklyReactNativeClient.getLastSuccessfulConnection(); + } + + getLastFailedConnection() { + return LaunchdarklyReactNativeClient.getLastFailedConnection(); + } + + getLastFailure() { + return LaunchdarklyReactNativeClient.getLastFailure(); + } } diff --git a/ios/LaunchdarklyReactNativeClient.podspec b/ios/LaunchdarklyReactNativeClient.podspec index 269706b..2310b7c 100644 --- a/ios/LaunchdarklyReactNativeClient.podspec +++ b/ios/LaunchdarklyReactNativeClient.podspec @@ -16,6 +16,6 @@ Pod::Spec.new do |s| s.swift_version = "5.0" s.dependency "React" - s.dependency "LaunchDarkly", "4.5.0" + s.dependency "LaunchDarkly", "5.4.0" end diff --git a/ios/LaunchdarklyReactNativeClient.swift b/ios/LaunchdarklyReactNativeClient.swift index 92daacb..f3966b8 100644 --- a/ios/LaunchdarklyReactNativeClient.swift +++ b/ios/LaunchdarklyReactNativeClient.swift @@ -24,14 +24,32 @@ class LaunchdarklyReactNativeClient: RCTEventEmitter { return false } - @objc func configure(_ config: NSDictionary, userConfig: NSDictionary, resolve: @escaping RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { + @objc func configure(_ config: NSDictionary, user: NSDictionary, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void { + internalConfigure(config: config, user: user, timeout: nil, resolve: resolve, reject: reject) + } + + @objc func configureWithTimeout(_ config: NSDictionary, user: NSDictionary, timeout: Int, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void { + internalConfigure(config: config, user: user, timeout: timeout, resolve: resolve, reject: reject) + } + private func internalConfigure(config: NSDictionary, user: NSDictionary, timeout: Int?, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void { let config = configBuild(config: config) - let user = userBuild(userConfig: userConfig) + let user = userBuild(userDict: user) if config != nil && user != nil { - LDClient.shared.startCompleteWhenFlagsReceived(config: config!, user: user, completion: {() -> Void in + if let timeoutUnwrapped = timeout { + let startWaitSeconds: TimeInterval = Double(timeoutUnwrapped) + LDClient.start(config: config!, user: user, startWaitSeconds: startWaitSeconds) { timedOut in + if timedOut { + reject(self.ERROR_INIT, "SDK initialization timed out", nil) + } else { + resolve(nil) + } + } + } else { + LDClient.start(config: config!, user: user, completion: {() -> Void in resolve(nil)}) + } } } @@ -45,8 +63,8 @@ class LaunchdarklyReactNativeClient: RCTEventEmitter { let safeKey = mobileKey as! String var ldConfig = LDConfig(mobileKey: safeKey) - if config["baseUri"] != nil { - ldConfig.baseUrl = URL.init(string: config["baseUri"] as! String)! + if config["pollUri"] != nil { + ldConfig.baseUrl = URL.init(string: config["pollUri"] as! String)! } if config["eventsUri"] != nil { @@ -100,327 +118,356 @@ class LaunchdarklyReactNativeClient: RCTEventEmitter { if config["evaluationReasons"] != nil { ldConfig.evaluationReasons = config["evaluationReasons"] as! Bool } + + ldConfig.wrapperName = config["wrapperName"] as? String + ldConfig.wrapperVersion = config["wrapperVersion"] as? String + + if config["maxCachedUsers"] != nil { + ldConfig.maxCachedUsers = config["maxCachedUsers"] as! Int + } + + if config["diagnosticOptOut"] != nil { + ldConfig.diagnosticOptOut = config["diagnosticOptOut"] as! Bool + } + + if config["diagnosticRecordingIntervalMillis"] != nil { + ldConfig.diagnosticRecordingInterval = TimeInterval(config["diagnosticRecordingIntervalMillis"] as! Float / 1000) + } + + if config["allUserAttributesPrivate"] != nil { + ldConfig.allUserAttributesPrivate = config["allUserAttributesPrivate"] as! Bool + } + + ldConfig.autoAliasingOptOut = true return ldConfig } - private func userBuild(userConfig: NSDictionary) -> LDUser? { + private func userBuild(userDict: NSDictionary) -> LDUser? { var user = LDUser() - user.key = userConfig["key"] as! String + user.key = userDict["key"] as! String - if userConfig["name"] != nil { - user.name = userConfig["name"] as? String + if userDict["name"] != nil { + user.name = userDict["name"] as? String } - if userConfig["firstName"] != nil { - user.firstName = userConfig["firstName"] as? String + if userDict["firstName"] != nil { + user.firstName = userDict["firstName"] as? String } - if userConfig["lastName"] != nil { - user.lastName = userConfig["lastName"] as? String + if userDict["lastName"] != nil { + user.lastName = userDict["lastName"] as? String } - if userConfig["email"] != nil { - user.email = userConfig["email"] as? String + if userDict["email"] != nil { + user.email = userDict["email"] as? String } - if userConfig["anonymous"] != nil { - user.isAnonymous = userConfig["anonymous"] as! Bool + if userDict["anonymous"] != nil { + user.isAnonymous = userDict["anonymous"] as! Bool } - if userConfig["country"] != nil { - user.country = userConfig["country"] as? String + if userDict["country"] != nil { + user.country = userDict["country"] as? String + } + + if userDict["ip"] != nil { + user.ipAddress = userDict["ip"] as? String + } + + if userDict["avatar"] != nil { + user.avatar = userDict["avatar"] as? String } - if userConfig["privateAttributeNames"] != nil { - user.privateAttributes = userConfig["privateAttributeNames"] as? [String] + if userDict["privateAttributeNames"] != nil { + user.privateAttributes = userDict["privateAttributeNames"] as? [String] } - if let customAttributes = userConfig["custom"] as! [String: Any]? { + if let customAttributes = userDict["custom"] as! [String: Any]? { user.custom = customAttributes } return user } - @objc func boolVariationFallback(_ flagKey: String, fallback: ObjCBool, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { - resolve(LDClient.shared.variation(forKey: flagKey, fallback: fallback.boolValue) as Bool) + @objc func boolVariationDefaultValue(_ flagKey: String, defaultValue: ObjCBool, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { + resolve(LDClient.get()!.variation(forKey: flagKey, defaultValue: defaultValue.boolValue) as Bool) } - @objc func intVariationFallback(_ flagKey: String, fallback: Int, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { - resolve(LDClient.shared.variation(forKey: flagKey, fallback: fallback) as Int) + @objc func intVariationDefaultValue(_ flagKey: String, defaultValue: Int, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { + resolve(LDClient.get()!.variation(forKey: flagKey, defaultValue: defaultValue) as Int) } - @objc func floatVariationFallback(_ flagKey: String, fallback: CGFloat, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { - resolve(LDClient.shared.variation(forKey: flagKey, fallback: Double(fallback)) as Double) + @objc func floatVariationDefaultValue(_ flagKey: String, defaultValue: CGFloat, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { + resolve(LDClient.get()!.variation(forKey: flagKey, defaultValue: Double(defaultValue)) as Double) } - @objc func stringVariationFallback(_ flagKey: String, fallback: String, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { - resolve(LDClient.shared.variation(forKey: flagKey, fallback: fallback) as String) + @objc func stringVariationDefaultValue(_ flagKey: String, defaultValue: String, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { + resolve(LDClient.get()!.variation(forKey: flagKey, defaultValue: defaultValue) as String) } @objc func boolVariation(_ flagKey: String, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { - let boolFlagValue: Bool? = LDClient.shared.variation(forKey: flagKey) + let boolFlagValue: Bool? = LDClient.get()!.variation(forKey: flagKey) resolve(boolFlagValue) } @objc func intVariation(_ flagKey: String, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { - let intFlagValue: Int? = LDClient.shared.variation(forKey: flagKey) + let intFlagValue: Int? = LDClient.get()!.variation(forKey: flagKey) resolve(intFlagValue) } @objc func floatVariation(_ flagKey: String, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { - let floatFlagValue: Double? = LDClient.shared.variation(forKey: flagKey) + let floatFlagValue: Double? = LDClient.get()!.variation(forKey: flagKey) resolve(floatFlagValue) } @objc func stringVariation(_ flagKey: String, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { - let stringFlagValue: String? = LDClient.shared.variation(forKey: flagKey) + let stringFlagValue: String? = LDClient.get()!.variation(forKey: flagKey) resolve(stringFlagValue) } @objc func jsonVariationNone(_ flagKey: String, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { - let jsonFlagValue: Dictionary? = LDClient.shared.variation(forKey: flagKey) + let jsonFlagValue: Dictionary? = LDClient.get()!.variation(forKey: flagKey) resolve(jsonFlagValue) } - @objc func jsonVariationNumber(_ flagKey: String, fallback: Double, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { - resolve(LDClient.shared.variation(forKey: flagKey, fallback: fallback) as Double) + @objc func jsonVariationNumber(_ flagKey: String, defaultValue: Double, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { + resolve(LDClient.get()!.variation(forKey: flagKey, defaultValue: defaultValue) as Double) } - @objc func jsonVariationBool(_ flagKey: String, fallback: Bool, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { - resolve(LDClient.shared.variation(forKey: flagKey, fallback: fallback) as Bool) + @objc func jsonVariationBool(_ flagKey: String, defaultValue: Bool, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { + resolve(LDClient.get()!.variation(forKey: flagKey, defaultValue: defaultValue) as Bool) } - @objc func jsonVariationString(_ flagKey: String, fallback: String, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { - resolve(LDClient.shared.variation(forKey: flagKey, fallback: fallback) as String) + @objc func jsonVariationString(_ flagKey: String, defaultValue: String, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { + resolve(LDClient.get()!.variation(forKey: flagKey, defaultValue: defaultValue) as String) } - @objc func jsonVariationArray(_ flagKey: String, fallback: Array, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { - resolve(LDClient.shared.variation(forKey: flagKey, fallback: fallback) as Array) + @objc func jsonVariationArray(_ flagKey: String, defaultValue: Array, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { + resolve(LDClient.get()!.variation(forKey: flagKey, defaultValue: defaultValue) as Array) } - @objc func jsonVariationObject(_ flagKey: String, fallback: NSDictionary, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { - resolve(LDClient.shared.variation(forKey: flagKey, fallback: fallback.swiftDictionary) as NSDictionary) + @objc func jsonVariationObject(_ flagKey: String, defaultValue: NSDictionary, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { + resolve(LDClient.get()!.variation(forKey: flagKey, defaultValue: defaultValue.swiftDictionary) as NSDictionary) } - @objc func boolVariationDetailFallback(_ flagKey: String, fallback: ObjCBool, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { - let detail = LDClient.shared.variationDetail(forKey: flagKey, fallback: fallback.boolValue) + @objc func boolVariationDetailDefaultValue(_ flagKey: String, defaultValue: ObjCBool, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { + let detail = LDClient.get()!.variationDetail(forKey: flagKey, defaultValue: defaultValue.boolValue) let jsonObject: NSDictionary = [ - "value": detail.value, - "variationIndex": detail.variationIndex, - "reason": detail.reason + "value": (detail.value as Any?) ?? NSNull(), + "variationIndex": (detail.variationIndex as Any?) ?? NSNull(), + "reason": (detail.reason as Any?) ?? NSNull() ] resolve(jsonObject) } - @objc func intVariationDetailFallback(_ flagKey: String, fallback: Int, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { - let detail = LDClient.shared.variationDetail(forKey: flagKey, fallback: fallback) + @objc func intVariationDetailDefaultValue(_ flagKey: String, defaultValue: Int, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { + let detail = LDClient.get()!.variationDetail(forKey: flagKey, defaultValue: defaultValue) let jsonObject: NSDictionary = [ - "value": detail.value, - "variationIndex": detail.variationIndex, - "reason": detail.reason + "value": (detail.value as Any?) ?? NSNull(), + "variationIndex": (detail.variationIndex as Any?) ?? NSNull(), + "reason": (detail.reason as Any?) ?? NSNull() ] resolve(jsonObject) } - @objc func floatVariationDetailFallback(_ flagKey: String, fallback: CGFloat, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { - let detail = LDClient.shared.variationDetail(forKey: flagKey, fallback: Double(fallback)) + @objc func floatVariationDetailDefaultValue(_ flagKey: String, defaultValue: CGFloat, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { + let detail = LDClient.get()!.variationDetail(forKey: flagKey, defaultValue: Double(defaultValue)) let jsonObject: NSDictionary = [ - "value": detail.value, - "variationIndex": detail.variationIndex, - "reason": detail.reason + "value": (detail.value as Any?) ?? NSNull(), + "variationIndex": (detail.variationIndex as Any?) ?? NSNull(), + "reason": (detail.reason as Any?) ?? NSNull() ] resolve(jsonObject) } - @objc func stringVariationDetailFallback(_ flagKey: String, fallback: String, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { - let detail = LDClient.shared.variationDetail(forKey: flagKey, fallback: fallback) + @objc func stringVariationDetailDefaultValue(_ flagKey: String, defaultValue: String, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { + let detail = LDClient.get()!.variationDetail(forKey: flagKey, defaultValue: defaultValue) let jsonObject: NSDictionary = [ - "value": detail.value, - "variationIndex": detail.variationIndex, - "reason": detail.reason + "value": (detail.value as Any?) ?? NSNull(), + "variationIndex": (detail.variationIndex as Any?) ?? NSNull(), + "reason": (detail.reason as Any?) ?? NSNull() ] resolve(jsonObject) } @objc func boolVariationDetail(_ flagKey: String, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { - let detail: EvaluationDetail = LDClient.shared.variationDetail(forKey: flagKey) + let detail: LDEvaluationDetail = LDClient.get()!.variationDetail(forKey: flagKey) let jsonObject: NSDictionary = [ - "value": detail.value, - "variationIndex": detail.variationIndex, - "reason": detail.reason + "value": (detail.value as Any?) ?? NSNull(), + "variationIndex": (detail.variationIndex as Any?) ?? NSNull(), + "reason": (detail.reason as Any?) ?? NSNull() ] resolve(jsonObject) } @objc func intVariationDetail(_ flagKey: String, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { - let detail: EvaluationDetail = LDClient.shared.variationDetail(forKey: flagKey) + let detail: LDEvaluationDetail = LDClient.get()!.variationDetail(forKey: flagKey) let jsonObject: NSDictionary = [ - "value": detail.value, - "variationIndex": detail.variationIndex, - "reason": detail.reason + "value": (detail.value as Any?) ?? NSNull(), + "variationIndex": (detail.variationIndex as Any?) ?? NSNull(), + "reason": (detail.reason as Any?) ?? NSNull() ] resolve(jsonObject) } @objc func floatVariationDetail(_ flagKey: String, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { - let detail: EvaluationDetail = LDClient.shared.variationDetail(forKey: flagKey) + let detail: LDEvaluationDetail = LDClient.get()!.variationDetail(forKey: flagKey) let jsonObject: NSDictionary = [ - "value": detail.value, - "variationIndex": detail.variationIndex, - "reason": detail.reason + "value": (detail.value as Any?) ?? NSNull(), + "variationIndex": (detail.variationIndex as Any?) ?? NSNull(), + "reason": (detail.reason as Any?) ?? NSNull() ] resolve(jsonObject) } @objc func stringVariationDetail(_ flagKey: String, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { - let detail: EvaluationDetail = LDClient.shared.variationDetail(forKey: flagKey) + let detail: LDEvaluationDetail = LDClient.get()!.variationDetail(forKey: flagKey) let jsonObject: NSDictionary = [ - "value": detail.value, - "variationIndex": detail.variationIndex, - "reason": detail.reason + "value": (detail.value as Any?) ?? NSNull(), + "variationIndex": (detail.variationIndex as Any?) ?? NSNull(), + "reason": (detail.reason as Any?) ?? NSNull() ] resolve(jsonObject) } @objc func jsonVariationDetailNone(_ flagKey: String, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { - let detail: EvaluationDetail?> = LDClient.shared.variationDetail(forKey: flagKey) + let detail: LDEvaluationDetail?> = LDClient.get()!.variationDetail(forKey: flagKey) let jsonObject: NSDictionary = [ - "value": detail.value, - "variationIndex": detail.variationIndex, - "reason": detail.reason + "value": (detail.value as Any?) ?? NSNull(), + "variationIndex": (detail.variationIndex as Any?) ?? NSNull(), + "reason": (detail.reason as Any?) ?? NSNull() ] resolve(jsonObject) } - @objc func jsonVariationDetailNumber(_ flagKey: String, fallback: Double, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { - let detail = LDClient.shared.variationDetail(forKey: flagKey, fallback: fallback) + @objc func jsonVariationDetailNumber(_ flagKey: String, defaultValue: Double, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { + let detail = LDClient.get()!.variationDetail(forKey: flagKey, defaultValue: defaultValue) let jsonObject: NSDictionary = [ - "value": detail.value, - "variationIndex": detail.variationIndex, - "reason": detail.reason + "value": (detail.value as Any?) ?? NSNull(), + "variationIndex": (detail.variationIndex as Any?) ?? NSNull(), + "reason": (detail.reason as Any?) ?? NSNull() ] resolve(jsonObject) } - @objc func jsonVariationDetailBool(_ flagKey: String, fallback: Bool, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { - let detail = LDClient.shared.variationDetail(forKey: flagKey, fallback: fallback) + @objc func jsonVariationDetailBool(_ flagKey: String, defaultValue: Bool, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { + let detail = LDClient.get()!.variationDetail(forKey: flagKey, defaultValue: defaultValue) let jsonObject: NSDictionary = [ - "value": detail.value, - "variationIndex": detail.variationIndex, - "reason": detail.reason + "value": (detail.value as Any?) ?? NSNull(), + "variationIndex": (detail.variationIndex as Any?) ?? NSNull(), + "reason": (detail.reason as Any?) ?? NSNull() ] resolve(jsonObject) } - @objc func jsonVariationDetailString(_ flagKey: String, fallback: String, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { - let detail = LDClient.shared.variationDetail(forKey: flagKey, fallback: fallback) + @objc func jsonVariationDetailString(_ flagKey: String, defaultValue: String, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { + let detail = LDClient.get()!.variationDetail(forKey: flagKey, defaultValue: defaultValue) let jsonObject: NSDictionary = [ - "value": detail.value, - "variationIndex": detail.variationIndex, - "reason": detail.reason + "value": (detail.value as Any?) ?? NSNull(), + "variationIndex": (detail.variationIndex as Any?) ?? NSNull(), + "reason": (detail.reason as Any?) ?? NSNull() ] resolve(jsonObject) } - @objc func jsonVariationDetailArray(_ flagKey: String, fallback: Array, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { - let detail = LDClient.shared.variationDetail(forKey: flagKey, fallback: fallback) + @objc func jsonVariationDetailArray(_ flagKey: String, defaultValue: Array, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { + let detail = LDClient.get()!.variationDetail(forKey: flagKey, defaultValue: defaultValue) let jsonObject: NSDictionary = [ - "value": detail.value, - "variationIndex": detail.variationIndex, - "reason": detail.reason + "value": (detail.value as Any?) ?? NSNull(), + "variationIndex": (detail.variationIndex as Any?) ?? NSNull(), + "reason": (detail.reason as Any?) ?? NSNull() ] resolve(jsonObject) } - @objc func jsonVariationDetailObject(_ flagKey: String, fallback: NSDictionary, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { - let detail = LDClient.shared.variationDetail(forKey: flagKey, fallback: fallback.swiftDictionary) + @objc func jsonVariationDetailObject(_ flagKey: String, defaultValue: NSDictionary, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { + let detail = LDClient.get()!.variationDetail(forKey: flagKey, defaultValue: defaultValue.swiftDictionary) let jsonObject: NSDictionary = [ - "value": detail.value, - "variationIndex": detail.variationIndex, - "reason": detail.reason + "value": (detail.value as Any?) ?? NSNull(), + "variationIndex": (detail.variationIndex as Any?) ?? NSNull(), + "reason": (detail.reason as Any?) ?? NSNull() ] resolve(jsonObject) } @objc func trackNumber(_ eventName: String, data: NSNumber) -> Void { - try? LDClient.shared.trackEvent(key: eventName, data: data) + try? LDClient.get()!.track(key: eventName, data: data) } @objc func trackBool(_ eventName: String, data: ObjCBool) -> Void { - try? LDClient.shared.trackEvent(key: eventName, data: data.boolValue) + try? LDClient.get()!.track(key: eventName, data: data.boolValue) } @objc func trackString(_ eventName: String, data: String) -> Void { - try? LDClient.shared.trackEvent(key: eventName, data: data) + try? LDClient.get()!.track(key: eventName, data: data) } @objc func trackArray(_ eventName: String, data: NSArray) -> Void { - try? LDClient.shared.trackEvent(key: eventName, data: data) + try? LDClient.get()!.track(key: eventName, data: data) } @objc func trackObject(_ eventName: String, data: NSDictionary) -> Void { - try? LDClient.shared.trackEvent(key: eventName, data: data.swiftDictionary) + try? LDClient.get()!.track(key: eventName, data: data.swiftDictionary) } @objc func track(_ eventName: String) -> Void { - try? LDClient.shared.trackEvent(key: eventName) + try? LDClient.get()!.track(key: eventName) } - @objc func trackNumberMetricValue(_ eventName: String, data: NSNumber, metricValue: Double) -> Void { - try? LDClient.shared.trackEvent(key: eventName, data: data, metricValue: metricValue) + @objc func trackNumberMetricValue(_ eventName: String, data: NSNumber, metricValue: NSNumber) -> Void { + try? LDClient.get()!.track(key: eventName, data: data, metricValue: Double(truncating: metricValue)) } - @objc func trackBoolMetricValue(_ eventName: String, data: ObjCBool, metricValue: Double) -> Void { - try? LDClient.shared.trackEvent(key: eventName, data: data.boolValue, metricValue: metricValue) + @objc func trackBoolMetricValue(_ eventName: String, data: ObjCBool, metricValue: NSNumber) -> Void { + try? LDClient.get()!.track(key: eventName, data: data.boolValue, metricValue: Double(truncating: metricValue)) } - @objc func trackStringMetricValue(_ eventName: String, data: String, metricValue: Double) -> Void { - try? LDClient.shared.trackEvent(key: eventName, data: data, metricValue: metricValue) + @objc func trackStringMetricValue(_ eventName: String, data: String, metricValue: NSNumber) -> Void { + try? LDClient.get()!.track(key: eventName, data: data, metricValue: Double(truncating: metricValue)) } - @objc func trackArrayMetricValue(_ eventName: String, data: NSArray, metricValue: Double) -> Void { - try? LDClient.shared.trackEvent(key: eventName, data: data, metricValue: metricValue) + @objc func trackArrayMetricValue(_ eventName: String, data: NSArray, metricValue: NSNumber) -> Void { + try? LDClient.get()!.track(key: eventName, data: data, metricValue: Double(truncating: metricValue)) } - @objc func trackObjectMetricValue(_ eventName: String, data: NSDictionary, metricValue: Double) -> Void { - try? LDClient.shared.trackEvent(key: eventName, data: data.swiftDictionary, metricValue: metricValue) + @objc func trackObjectMetricValue(_ eventName: String, data: NSDictionary, metricValue: NSNumber) -> Void { + try? LDClient.get()!.track(key: eventName, data: data.swiftDictionary, metricValue: Double(truncating: metricValue)) } - @objc func trackMetricValue(_ eventName: String, metricValue: Double) -> Void { - try? LDClient.shared.trackEvent(key: eventName, metricValue: metricValue) + @objc func trackMetricValue(_ eventName: String, metricValue: NSNumber) -> Void { + try? LDClient.get()!.track(key: eventName, metricValue: Double(truncating: metricValue)) } @objc func setOffline(_ resolve: @escaping RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { - LDClient.shared.setOnline(false) { + LDClient.get()!.setOnline(false) { return resolve(true) } } @objc func isOffline(_ resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { - resolve(!LDClient.shared.isOnline) + resolve(!LDClient.get()!.isOnline) } @objc func setOnline(_ resolve: @escaping RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { - LDClient.shared.setOnline(true) { + LDClient.get()!.setOnline(true) { return resolve(true) } } @objc func flush() -> Void { - LDClient.shared.reportEvents() + LDClient.get()!.flush() } @objc func close(_ resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { - LDClient.shared.stop() + LDClient.get()!.close() resolve(true) } @objc func identify(_ options: NSDictionary, resolve: @escaping RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { - let user = userBuild(userConfig: options) + let user = userBuild(userDict: options) if let usr = user { - LDClient.shared.identify(user: usr) { + LDClient.get()!.identify(user: usr) { resolve(nil) } } else { @@ -430,7 +477,7 @@ class LaunchdarklyReactNativeClient: RCTEventEmitter { @objc func allFlags(_ resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { var allFlagsDict: [String: Any] = [:] - if let allFlags = LDClient.shared.allFlagValues { + if let allFlags = LDClient.get()!.allFlags { for (key, value) in allFlags { allFlagsDict[key] = value } @@ -445,7 +492,7 @@ class LaunchdarklyReactNativeClient: RCTEventEmitter { } else { return } - LDClient.shared.observe(keys: [flagKey], owner: flagChangeOwner, handler: { (changedFlags) in + LDClient.get()!.observe(keys: [flagKey], owner: flagChangeOwner, handler: { (changedFlags) in if changedFlags[flagKey] != nil && self.bridge != nil { self.sendEvent(withName: self.FLAG_PREFIX, body: ["flagKey": flagKey]) } @@ -459,19 +506,52 @@ class LaunchdarklyReactNativeClient: RCTEventEmitter { } else { return } - LDClient.shared.stopObserving(owner: owner) + LDClient.get()!.stopObserving(owner: owner) } @objc func unregisterFeatureFlagListener(_ flagKey: String) -> Void { unregisterListener(flagKey) } - @objc func isDisableBackgroundPolling(_ resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { - resolve(LDClient.shared.config.enableBackgroundUpdates) + @objc func getConnectionMode(_ resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { + let connectionInformation = LDClient.get()!.getConnectionInformation() + var connectionMode: String + switch connectionInformation.currentConnectionMode { + case .streaming: + connectionMode = "STREAMING" + case .polling: + connectionMode = "POLLING" + case .offline: + connectionMode = "OFFLINE" + case .establishingStreamingConnection: + connectionMode = "ESTABLISHING_STREAMING_CONNECTION" + } + resolve(connectionMode) } - - @objc func getConnectionInformation(_ resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { - resolve(LDClient.shared.getConnectionInformation()) + + // lastKnownFlagValidity is nil if either no connection has ever been successfully made or if the SDK has an active streaming connection. It will have a value if 1) in polling mode and at least one poll has completed successfully, or 2) if in streaming mode whenever the streaming connection closes. + @objc func getLastSuccessfulConnection(_ resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { + resolve(LDClient.get()!.getConnectionInformation().lastKnownFlagValidity ?? 0) + } + + @objc func getLastFailedConnection(_ resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { + resolve(LDClient.get()!.getConnectionInformation().lastFailedConnection ?? 0) + } + + @objc func getLastFailure(_ resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { + let connectionInformation = LDClient.get()!.getConnectionInformation() + var failureReason: String + switch connectionInformation.lastConnectionFailureReason { + case .unauthorized: + failureReason = "UNAUTHORIZED" + case .none: + failureReason = "NONE" + case .httpError: + failureReason = "HTTP_ERROR" + case .unknownError: + failureReason = "UNKNOWN_ERROR" + } + resolve(failureReason) } @objc func registerCurrentConnectionModeListener(_ listenerId: String) -> Void { @@ -481,7 +561,7 @@ class LaunchdarklyReactNativeClient: RCTEventEmitter { } else { return } - LDClient.shared.observeCurrentConnectionMode(owner: currentConnectionModeOwner, handler: { (connectionMode) in + LDClient.get()!.observeCurrentConnectionMode(owner: currentConnectionModeOwner, handler: { (connectionMode) in if self.bridge != nil { self.sendEvent(withName: self.CONNECTION_MODE_PREFIX, body: ["connectionMode": connectionMode]) } @@ -499,7 +579,7 @@ class LaunchdarklyReactNativeClient: RCTEventEmitter { } else { return } - LDClient.shared.observeAll(owner: flagChangeOwner, handler: { (changedFlags) in + LDClient.get()!.observeAll(owner: flagChangeOwner, handler: { (changedFlags) in if self.bridge != nil { self.sendEvent(withName: self.ALL_FLAGS_PREFIX, body: ["flagKeys": changedFlags.keys.description]) } @@ -509,6 +589,14 @@ class LaunchdarklyReactNativeClient: RCTEventEmitter { @objc func unregisterAllFlagsListener(_ listenerId: String) -> Void { unregisterListener(listenerId) } + + @objc func isInitialized(_ resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { + if LDClient.get() == nil { + resolve(false) + } else { + resolve(LDClient.get()!.isInitialized) + } + } } extension NSDictionary { diff --git a/ios/LaunchdarklyReactNativeClientBridge.m b/ios/LaunchdarklyReactNativeClientBridge.m index cdcbd3b..af3931a 100644 --- a/ios/LaunchdarklyReactNativeClientBridge.m +++ b/ios/LaunchdarklyReactNativeClientBridge.m @@ -3,15 +3,17 @@ @interface RCT_EXTERN_MODULE(LaunchdarklyReactNativeClient, RCTEventEmitter) -RCT_EXTERN_METHOD(configure:(NSDictionary *)config userConfig:(NSDictionary *)userConfig resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) +RCT_EXTERN_METHOD(configure:(NSDictionary *)config user:(NSDictionary *)user resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) -RCT_EXTERN_METHOD(boolVariationFallback:(NSString *)flagKey fallback:(BOOL *)fallback resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) +RCT_EXTERN_METHOD(configureWithTimeout:(NSDictionary *)config userConfig:(NSDictionary *)userConfig timeout:(NSInteger *)timeout resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) -RCT_EXTERN_METHOD(intVariationFallback:(NSString *)flagKey fallback:(NSInteger *)fallback resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) +RCT_EXTERN_METHOD(boolVariationDefaultValue:(NSString *)flagKey defaultValue:(BOOL *)defaultValue resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) -RCT_EXTERN_METHOD(floatVariationFallback:(NSString *)flagKey fallback:(CGFloat *)fallback resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) +RCT_EXTERN_METHOD(intVariationDefaultValue:(NSString *)flagKey defaultValue:(NSInteger *)defaultValue resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) -RCT_EXTERN_METHOD(stringVariationFallback:(NSString *)flagKey fallback:(NSString *)fallback resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) +RCT_EXTERN_METHOD(floatVariationDefaultValue:(NSString *)flagKey defaultValue:(CGFloat *)defaultValue resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(stringVariationDefaultValue:(NSString *)flagKey defaultValue:(NSString *)defaultValue resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) RCT_EXTERN_METHOD(boolVariation:(NSString *)flagKey resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) @@ -23,23 +25,23 @@ @interface RCT_EXTERN_MODULE(LaunchdarklyReactNativeClient, RCTEventEmitter) RCT_EXTERN_METHOD(jsonVariationNone:(NSString *)flagKey resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) -RCT_EXTERN_METHOD(jsonVariationNumber:(NSString *)flagKey fallback:(NSNumber *)fallback resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) +RCT_EXTERN_METHOD(jsonVariationNumber:(NSString *)flagKey defaultValue:(NSNumber *)defaultValue resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) -RCT_EXTERN_METHOD(jsonVariationBool:(NSString *)flagKey fallback:(BOOL *)fallback resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) +RCT_EXTERN_METHOD(jsonVariationBool:(NSString *)flagKey defaultValue:(BOOL *)defaultValue resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) -RCT_EXTERN_METHOD(jsonVariationString:(NSString *)flagKey fallback:(NSString *)fallback resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) +RCT_EXTERN_METHOD(jsonVariationString:(NSString *)flagKey defaultValue:(NSString *)defaultValue resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) -RCT_EXTERN_METHOD(jsonVariationArray:(NSString *)flagKey fallback:(NSArray *)fallback resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) +RCT_EXTERN_METHOD(jsonVariationArray:(NSString *)flagKey defaultValue:(NSArray *)defaultValue resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) -RCT_EXTERN_METHOD(jsonVariationObject:(NSString *)flagKey fallback:(NSDictionary *)fallback resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) +RCT_EXTERN_METHOD(jsonVariationObject:(NSString *)flagKey defaultValue:(NSDictionary *)defaultValue resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) -RCT_EXTERN_METHOD(boolVariationDetailFallback:(NSString *)flagKey fallback:(BOOL *)fallback resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) +RCT_EXTERN_METHOD(boolVariationDetailDefaultValue:(NSString *)flagKey defaultValue:(BOOL *)defaultValue resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) -RCT_EXTERN_METHOD(intVariationDetailFallback:(NSString *)flagKey fallback:(NSInteger *)fallback resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) +RCT_EXTERN_METHOD(intVariationDetailDefaultValue:(NSString *)flagKey defaultValue:(NSInteger *)defaultValue resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) -RCT_EXTERN_METHOD(floatVariationDetailFallback:(NSString *)flagKey fallback:(CGFloat *)fallback resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) +RCT_EXTERN_METHOD(floatVariationDetailDefaultValue:(NSString *)flagKey defaultValue:(CGFloat *)defaultValue resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) -RCT_EXTERN_METHOD(stringVariationDetailFallback:(NSString *)flagKey fallback:(NSString *)fallback resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) +RCT_EXTERN_METHOD(stringVariationDetailDefaultValue:(NSString *)flagKey defaultValue:(NSString *)defaultValue resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) RCT_EXTERN_METHOD(boolVariationDetail:(NSString *)flagKey resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) @@ -51,21 +53,21 @@ @interface RCT_EXTERN_MODULE(LaunchdarklyReactNativeClient, RCTEventEmitter) RCT_EXTERN_METHOD(jsonVariationDetailNone:(NSString *)flagKey resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) -RCT_EXTERN_METHOD(jsonVariationDetailNumber:(NSString *)flagKey fallback:(NSNumber *)fallback resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) +RCT_EXTERN_METHOD(jsonVariationDetailNumber:(NSString *)flagKey defaultValue:(NSNumber *)defaultValue resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) -RCT_EXTERN_METHOD(jsonVariationDetailBool:(NSString *)flagKey fallback:(BOOL *)fallback resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) +RCT_EXTERN_METHOD(jsonVariationDetailBool:(NSString *)flagKey defaultValue:(BOOL *)defaultValue resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) -RCT_EXTERN_METHOD(jsonVariationDetailString:(NSString *)flagKey fallback:(NSString *)fallback resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) +RCT_EXTERN_METHOD(jsonVariationDetailString:(NSString *)flagKey defaultValue:(NSString *)defaultValue resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) -RCT_EXTERN_METHOD(jsonVariationDetailArray:(NSString *)flagKey fallback:(NSArray *)fallback resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) +RCT_EXTERN_METHOD(jsonVariationDetailArray:(NSString *)flagKey defaultValue:(NSArray *)defaultValue resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) -RCT_EXTERN_METHOD(jsonVariationDetailObject:(NSString *)flagKey fallback:(NSDictionary *)fallback resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) +RCT_EXTERN_METHOD(jsonVariationDetailObject:(NSString *)flagKey defaultValue:(NSDictionary *)defaultValue resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) RCT_EXTERN_METHOD(trackBool:(NSString *)eventName data:(BOOL *)data) RCT_EXTERN_METHOD(trackArray:(NSString *)eventName data:(NSArray *)data) -RCT_EXTERN_METHOD(trackNumber:(NSString *)eventName data:(NSNumber *)data) +RCT_EXTERN_METHOD(trackNumber:(NSString *)eventName data:(NSNumber * _Nonnull)data) RCT_EXTERN_METHOD(trackString:(NSString *)eventName data:(NSString *)data) @@ -73,17 +75,17 @@ @interface RCT_EXTERN_MODULE(LaunchdarklyReactNativeClient, RCTEventEmitter) RCT_EXTERN_METHOD(track:(NSString *)eventName) -RCT_EXTERN_METHOD(trackBoolMetricValue:(NSString *)eventName data:(BOOL *)data metricValue:(NSNumber *)metricValue) +RCT_EXTERN_METHOD(trackBoolMetricValue:(NSString *)eventName data:(BOOL *)data metricValue:(NSNumber * _Nonnull)metricValue) -RCT_EXTERN_METHOD(trackArrayMetricValue:(NSString *)eventName data:(NSArray *)data metricValue:(NSNumber *)metricValue) +RCT_EXTERN_METHOD(trackArrayMetricValue:(NSString *)eventName data:(NSArray *)data metricValue:(NSNumber * _Nonnull)metricValue) -RCT_EXTERN_METHOD(trackNumberMetricValue:(NSString *)eventName data:(NSNumber *)data metricValue:(NSNumber *)metricValue) +RCT_EXTERN_METHOD(trackNumberMetricValue:(NSString *)eventName data:(NSNumber * _Nonnull)data metricValue:(NSNumber * _Nonnull)metricValue) -RCT_EXTERN_METHOD(trackStringMetricValue:(NSString *)eventName data:(NSString *)data metricValue:(NSNumber *)metricValue) +RCT_EXTERN_METHOD(trackStringMetricValue:(NSString *)eventName data:(NSString *)data metricValue:(NSNumber * _Nonnull)metricValue) -RCT_EXTERN_METHOD(trackObjectMetricValue:(NSString *)eventName data:(NSDictionary *)data metricValue:(NSNumber *)metricValue) +RCT_EXTERN_METHOD(trackObjectMetricValue:(NSString *)eventName data:(NSDictionary *)data metricValue:(NSNumber * _Nonnull)metricValue) -RCT_EXTERN_METHOD(trackMetricValue:(NSString *)eventName metricValue:(NSNumber *)metricValue) +RCT_EXTERN_METHOD(trackMetricValue:(NSString *)eventName metricValue:(NSNumber * _Nonnull)metricValue) RCT_EXTERN_METHOD(setOffline:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) @@ -103,10 +105,6 @@ @interface RCT_EXTERN_MODULE(LaunchdarklyReactNativeClient, RCTEventEmitter) RCT_EXTERN_METHOD(unregisterFeatureFlagListener:(NSString *)flagKey) -RCT_EXTERN_METHOD(isDisableBackgroundPolling:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) - -RCT_EXTERN_METHOD(getConnectionInformation:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) - RCT_EXTERN_METHOD(registerCurrentConnectionModeListener:(NSString *)listenerId) RCT_EXTERN_METHOD(unregisterCurrentConnectionModeListener:(NSString *)listenerId) @@ -115,4 +113,14 @@ @interface RCT_EXTERN_MODULE(LaunchdarklyReactNativeClient, RCTEventEmitter) RCT_EXTERN_METHOD(unregisterAllFlagsListener:(NSString *)listenerId) +RCT_EXTERN_METHOD(isInitialized:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(getConnectionMode:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(getLastSuccessfulConnection:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(getLastFailedConnection:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(getLastFailure:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) + @end diff --git a/test-types.ts b/test-types.ts index 31d99a4..f311656 100644 --- a/test-types.ts +++ b/test-types.ts @@ -2,12 +2,14 @@ // This file exists only so that we can run the TypeScript compiler in the CI build // to validate our index.d.ts file. The code will not actually be run. -import LDClient, { - LDClientConfig, +import LDClient, { + LDConnectionMode, + LDConfig, LDEvaluationDetail, LDEvaluationReason, + LDFailureReason, LDFlagSet, - LDUserConfig, + LDUser, } from 'launchdarkly-react-native-client-sdk'; async function tests() { @@ -20,12 +22,12 @@ async function tests() { 'f': [ 1, 2 ] }; - const configWithKeyOnly: LDClientConfig = { + const configWithKeyOnly: LDConfig = { mobileKey: '' }; - const configWithAllOptions: LDClientConfig = { + const configWithAllOptions: LDConfig = { mobileKey: '', - baseUri: '', + pollUri: '', streamUri: '', eventsUri: '', eventsCapacity: 1, @@ -39,9 +41,13 @@ async function tests() { offline: true, debugMode: true, evaluationReasons: true, + maxCachedUsers: 6, + diagnosticOptOut: true, + diagnosticRecordingIntervalMillis: 100000, + allUserAttributesPrivate: true, }; - const userWithKeyOnly: LDUserConfig = { key: 'user' }; - const user: LDUserConfig = { + const userWithKeyOnly: LDUser = { key: 'user' }; + const user: LDUser = { key: 'user', name: 'name', firstName: 'first', @@ -51,10 +57,14 @@ async function tests() { country: 'us', privateAttributeNames: [ 'name', 'email' ], custom: jsonObj, + avatar: 'avatar', + ip: '192.0.2.1', }; const client: LDClient = new LDClient(); + const timeoutClient: LDClient = new LDClient(); const configure: null = await client.configure(configWithAllOptions, user); + const configureWithTimeout: null = await timeoutClient.configure(configWithAllOptions, user, 10); const identify: null = await client.identify(user); const boolFlagValue: boolean = await client.boolVariation('key', false); @@ -94,7 +104,6 @@ async function tests() { const setOnline: boolean = await client.setOnline(); const isOffline: boolean = await client.isOffline(); const isInitialized: boolean = await client.isInitialized(); - const isDisableBackgroundPolling: boolean = await client.isDisableBackgroundPolling(); const callback = function(_: string): void { }; const registerFeatureFlagListener: void = client.registerFeatureFlagListener('key', callback); @@ -104,10 +113,15 @@ async function tests() { const registerCurrentConnectionModeListener: void = client.registerCurrentConnectionModeListener('id', callback); const unregisterCurrentConnectionModeListener: void = client.unregisterCurrentConnectionModeListener('id'); - const getConnectionInformation: any = await client.getConnectionInformation(); + const getConnectionMode: LDConnectionMode = await client.getConnectionMode(); + const getSuccessfulConnection: number | null = await client.getLastSuccessfulConnection(); + const getFailedConnection: number | null = await client.getLastFailedConnection(); + const getFailureReason: LDFailureReason | null = await client.getLastFailure(); const flush: void = await client.flush(); const close: void = await client.close(); + + const version: String = client.getVersion(); }; tests(); \ No newline at end of file