# Launch Darkly

{% hint style="success" %}
Using Appfigurate remote properties instead of Launch Darkly flag evaluation APIs directly allows for the following:

* locally change Launch Darkly flags without affecting your entire customer base.
* compile time type safety - Appfigurate remote properties are typed.
* avoids hardcoding duplicated flag names throughout your app.
* avoids hardcoding duplicated default values throughout your app.
* deleting a flag from Launch Darkly Console won't affect existing apps, they'll continue to use the default value provided in the [`reset`](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)reset) method.
  {% endhint %}

We assume you already have Appfigurate Library and [Launch Darkly](https://launchdarkly.com/) integrated into your app with the following example remote properties created in the [Launch Darkly console](https://app.launchdarkly.com/):

<figure><img src="https://1008176080-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fw1fcw3dvtSrfUh3YtO9Z%2Fuploads%2FFhSQhotk7nFkxxh54Fxr%2Fimage.png?alt=media&#x26;token=b67e1fce-bc66-4d20-a922-d92477864a4c" alt="" width="563"><figcaption></figcaption></figure>

### Add remote properties into your Configuration subclass

Update your configuration subclass to include your remote configuration properties. Provide default values for the properties in the overridden [`reset`](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)reset) method.  See also [Supported property types](https://docs.electricbolt.co.nz/configuration-subclasses/supported-property-types#remote-properties).

{% tabs %}
{% tab title="Swift" %}

> Swift Configuration example

<pre class="language-swift"><code class="lang-swift">import Foundation
import AppfigurateLibrary

@objcMembers class MyConfiguration: APLConfiguration {
...
    @RemoteBoolProperty(key: "alwaysDarkMode", description: "Force dark mode to be always set")
    var alwaysDarkMode: Bool

    @RemoteStringPropertyEdit(key: "appTitle", description: "Title of application")
    var appTitle: String

    @RemoteIntPropertyEdit(key: "bookingDuration", description: "Duration (days) for reservation bookings")
    var bookingDuration: Int

    @RemoteDoublePropertyEdit(key: "fontSize", description: "Size of font throughout app")
    var fontSize: Double
...
    override func reset() {
        alwaysDarkMode = false
        appTitle = "Holiday finder"
        bookingDuration = 30
        fontSize = 13.0
    }
...
<strong>}
</strong></code></pre>

{% endtab %}

{% tab title="Objective-C" %}

> Objective-C Configuration header example

```objectivec
@import Foundation;
@import AppfigurateLibrary;

@interface MyConfiguration : APLConfiguration
...
@property(nonatomic, assign) BOOL alwaysDarkMode;
@property(nonatomic, strong) NSString* appTitle;
@property(nonatomic, assign) NSInteger bookingDuration;
@property(nonatomic, assign) double fontSize;
...
@end

```

> Objective-C Configuration implementation example

```objectivec
#import "MyConfiguration.h"

@implementation MyConfiguration
...
REMOTE_BOOL_PROPERTY(alwaysDarkMode, @"alwaysDarkMode", @"Force dark mode to be always set");
REMOTE_STRING_PROPERTY_EDIT(appTitle, @"appTitle", @"Title of application");
REMOTE_INT_PROPERTY_EDIT(bookingDuration, @"bookingDuration", @"Duration (days) for reservation bookings");
REMOTE_DOUBLE_PROPERTY_EDIT(fontSize, @"fontSize", @"Size of font throughout app");
...
- (void) reset {
    self.alwaysDarkMode = NO;
    self.appTitle = @"Holiday finder";
    self.bookingDuration = 30;
    self.fontSize = 13.0;
}
...
@end
```

{% endtab %}
{% endtabs %}

### Provide remote configuration values to Appfigurate Library when requested

Appfigurate Library needs to be able to read the current remote flags from Launch Darkly. Copy and paste the following code into your app.

{% tabs %}
{% tab title="Swift" %}

> Swift example

```swift
APLFetchRemoteConfiguration { propertyKey, propertyType, defaultValue in
    if propertyType == .bool {
        return NSNumber(value: (LDClient.get()!.boolVariation(forKey: propertyKey, defaultValue: (defaultValue as! NSNumber).boolValue)))
    } else if propertyType == .int {
        return NSNumber(value: (LDClient.get()!.intVariation(forKey: propertyKey, defaultValue: (defaultValue as! NSNumber).intValue)))
    } else if propertyType == .double {
        return NSNumber(value: (LDClient.get()!.doubleVariation(forKey: propertyKey, defaultValue: (defaultValue as! NSNumber).doubleValue)))
    } else { // .string 
        return NSString(string: LDClient.get()!.stringVariation(forKey: propertyKey, defaultValue: (defaultValue as! String)))
    }
}
```

{% endtab %}

{% tab title="Objective-C" %}

> Objective-C example

```objectivec
APLFetchRemoteConfiguration(^NSObject* (NSString* propertyKey, APLRemotePropertyType propertyType, NSObject* defaultValue) {
    if (propertyType == APLRemotePropertyTypeBool) {
        return [NSNumber numberWithBool: [[LDClient get] boolVariationForKey: propertyKey defaultValue: [((NSNumber*) defaultValue) boolValue]]];
    } else if (propertyType == APLRemotePropertyTypeInt) {
        return [NSNumber numberWithInteger: [[LDClient get] integerVariationForKey: propertyKey defaultValue: [((NSNumber*) defaultValue) integerValue]]];
    } else if (propertyType == APLRemotePropertyTypeDouble) {
        return [NSNumber numberWithDouble: [[LDClient get] doubleVariationForKey: propertyKey defaultValue: [((NSNumber*) defaultValue) doubleValue]]];
    } else { // APLRemotePropertyTypeString 
        return [[LDClient get] stringVariationForKey: propertyKey defaultValue: (NSString*) defaultValue];
    }
});
```

{% endtab %}
{% endtabs %}

### Notify Appfigurate Library when Launch Darkly has received flags

We need to tell Appfigurate Library that Launch Darkly has received flags, so that it can keep your Configuration subclasses remote properties in sync.

{% tabs %}
{% tab title="Swift" %}
Add a call to `APLFlushRemoteConfiguration` in any existing Launch Darkly `start(config, startWaitSeconds, completion)` and `observeAll(owner, handler)` blocks.

> Swift example

```swift
LDClient.start(config: config, startWaitSeconds: 5.0) { timedOut in
    ...
    APLFlushRemoteConfiguration() // add this line
}
...
LDClient.get()?.observeAll(owner: self) { keys in
    ...
    APLFlushRemoteConfiguration() // add this line
}
```

{% endtab %}

{% tab title="Objective-C" %}
Add a call to `APLFlushRemoteConfiguration` in any existing Launch Darkly `startWithConfiguration:startWaitSeconds:completion:` and `observeAllKeysWithOwner:handler:` blocks.

> Objective-C example

```objectivec
[LDClient startWithConfiguration:config startWaitSeconds:5.0 completion:^(bool timedOut) {
    ...
    APLFlushRemoteConfiguration(); // add this line
}];
...
[[LDClient get] observeAllKeysWithOwner: self handler:^(NSDictionary<NSString *,LDChangedFlag *> *handler) {
    ...
    APLFlushRemoteConfiguration(); // add this line
}];
```

{% endtab %}
{% endtabs %}

### Complete initialisation example

{% tabs %}
{% tab title="Swift" %}

> Swift complete initialisation example

```swift
let config = LDConfig(mobileKey: "<YOUR-KEY>", autoEnvAttributes: .enabled)

// add this block of code
APLFetchRemoteConfiguration { propertyKey, propertyType, defaultValue in
    if propertyType == .bool {
        return NSNumber(value: (LDClient.get()!.boolVariation(forKey: propertyKey, defaultValue: (defaultValue as! NSNumber).boolValue)))
    } else if propertyType == .int {
        return NSNumber(value: (LDClient.get()!.intVariation(forKey: propertyKey, defaultValue: (defaultValue as! NSNumber).intValue)))
    } else if propertyType == .double {
        return NSNumber(value: (LDClient.get()!.doubleVariation(forKey: propertyKey, defaultValue: (defaultValue as! NSNumber).doubleValue)))
    } else {
        return NSString(string: LDClient.get()!.stringVariation(forKey: propertyKey, defaultValue: (defaultValue as! String)))
    }
}

LDClient.start(config: config, startWaitSeconds: 5.0) { timedOut in
    if !timedOut {
        APLFlushRemoteConfiguration() // add this line
    }
}
LDClient.get()?.observeAll(owner: self) { keys in
    APLFlushRemoteConfiguration() // add this line
}
```

{% endtab %}

{% tab title="Objective-C" %}

> Objective-C complete initialisation example

```objectivec
LDConfig* config = [[LDConfig alloc] initWithMobileKey: @"<YOUR-KEY>" autoEnvAttributes:AutoEnvAttributesEnabled];

// add this block of code 
APLFetchRemoteConfiguration(^NSObject* (NSString* propertyKey, APLRemotePropertyType propertyType, NSObject* defaultValue) {
    if (propertyType == APLRemotePropertyTypeBool) {
        return [NSNumber numberWithBool: [[LDClient get] boolVariationForKey: propertyKey defaultValue: [((NSNumber*) defaultValue) boolValue]]];
    } else if (propertyType == APLRemotePropertyTypeInt) {
        return [NSNumber numberWithInteger: [[LDClient get] integerVariationForKey: propertyKey defaultValue: [((NSNumber*) defaultValue) integerValue]]];
    } else if (propertyType == APLRemotePropertyTypeDouble) {
        return [NSNumber numberWithDouble: [[LDClient get] doubleVariationForKey: propertyKey defaultValue: [((NSNumber*) defaultValue) doubleValue]]];
    } else {
        return [[LDClient get] stringVariationForKey: propertyKey defaultValue: (NSString*) defaultValue];
    }
});

LDContext* context = [[[[LDContextBuilder alloc] initWithKey: @"<YOUR-CONTEXT>"] build] success];
[LDClient startWithConfiguration:config context:context startWaitSeconds:5.0 completion:^(bool timedOut) {
    if (!timedOut) {
        APLFlushRemoteConfiguration(); // add this line
    }
}];

[[LDClient get] observeAllKeysWithOwner: self handler: ^(NSDictionary<NSString *,LDChangedFlag *> *handler) {
    APLFlushRemoteConfiguration(); // add this line
}];
```

{% endtab %}
{% endtabs %}

### Best practice and usage

{% tabs %}
{% tab title="Swift" %}
Replace all calls to Launch Darkly `boolVariation(forKey:defaultValue:), intVariation(forKey:defaultValue:), doubleVariation(forKey:defaultValue), stringVariation(forKey:defaultValue:)`&#x20;

```swift
if (LDClient.get()!.boolVariation(forKey: "alwaysDarkMode", defaultValue: false)) {
    ...
```

with the following:

```swift
if (CONFIGURATION().alwaysDarkMode) {
    ...
```

{% endtab %}

{% tab title="Objective-C" %}
Replace all calls to Launch Darkly `boolVariationForKey:defaultValue:, intVariationForKey:defaultValue:, doubleVariationForKey:defaultValue, stringVariationForKey:defaultValue:`

```objectivec
if ([[LDClient get] boolVariationForKey: @"alwaysDarkMode" defaultValue: NO]) {
    ...
```

with the following:

```objectivec
if (CONFIGURATION.alwaysDarkMode) {
    ...
```

{% endtab %}
{% endtabs %}
