# Introducing Appfigurate™️ 5

## What is Appfigurate?

Appfigurate is a development platform that lets you securely change local and [remote](/getting-started/third-party-remote-configuration-providers) configuration properties, feature toggles and flags in [iOS](/getting-started/ios-native-app-integration), [watchOS](/getting-started/watchos-app-integration), [Flutter](/getting-started/flutter-integration) and [React Native](/getting-started/react-native-integration) apps and app extensions.

* Slow compilation and deployment times? Speed up development and testing by removing the edit‣compile‣deploy cycle when you just need to change your app's configuration properties and feature toggles.
* Multiple test environments? Reduce your Xcode project's complexity by removing custom build schemes. Build, deploy, and test just one app across multiple test regions.

## What makes up the Appfigurate Platform?

Appfigurate consists of the following:

* Appfigurate app for iOS Simulator&#x20;
* Appfigurate app for physical iOS devices.
* Appfigurate pre-packaged app for [real device cloud testing services](/appfigurate-se-user-guide/real-device-cloud-testing-services).
* AppfigurateSE macOS app, providing:
  * One-click installation to iOS Simulator
  * One-click installation to real-device cloud testing services
  * Manual encryption of strings
  * Xcode source editor extension for automated string encryption
* Static XCFramework library to link into your iOS/watchOS app and app extensions (via Swift Package Manager)
* Appfigurate Flutter Plugin - supporting iOS apps.
* Guides & API docs.
* Example apps.

## Supported languages and toolings

Appfigurate supports the following languages and toolings:

* **Xcode:** Swift and Objective-C for [iOS](/getting-started/ios-native-app-integration) and [watchOS](/getting-started/watchos-app-integration) apps and app extensions.
* **Android Studio (or IntelliJ):** Dart for iOS [Flutter ](/getting-started/flutter-integration)apps.
* **Visual Studio Code:** Dart for iOS [Flutter](/getting-started/flutter-integration) apps. JavaScript for iOS [React Native](/getting-started/react-native-integration) apps.

## What does it cost?

Appfigurate app for iOS Simulator, AppfigurateSE macOS, the libraries, docs and examples are made available to you at no cost. The Appfigurate app for physical iOS devices require a small *one-time* purchase (about the price of a cup or two of coffee).

## What happens to my data?

Appfigurate has no server or cloud component, your app metadata records (which include your private keys) are always under your control. We do use analytics on the website and in the apps, which is covered under our [privacy policy](/legal/privacy-policy). For physical device builds of Appfigurate, the app permission usage is [described here](/appfigurate-user-guide/permissions).

## How does Appfigurate work?

Appfigurate uses configuration payloads verified using digital signatures to ensure that only you can configure your app.&#x20;

Each app that you develop and test; as an iOS, watchOS, Flutter or React Native developer using Appfigurate is represented by a record that we call app metadata. App metadata records consist of a unique URL Scheme and 2048 bit RSA private key.

The URL Scheme is used to launch your app in order to retrieve, apply or reset configuration. The private key is used to digitally sign the configuration payloads. You embed the corresponding public key into your app.

If the configuration payload and digital signature match, then the configuration payload is applied to your app, and your app's configurable properties are updated at runtime. You may optionally configure your app to be restarted automatically, if some properties are only used during the initialization of your app.&#x20;

The configuration payload and digital signature are stored securely in your app's Keychain (Apple).

## How do I get started?

Jump to [Getting Started & Downloads](/getting-started/getting-started-and-downloads) for full information.


# Getting Started & Downloads

## Download and install apps

See what's new by reading the [Release History](/legal/release-history) of Appfigurate.

### AppfigurateSE macOS app

AppfigurateSE macOS app, providing:

* One-click installation to iOS Simulator
* One-click installation to real-device cloud testing services
* Manual encryption of strings
* Xcode source editor extension for automated string encryption

&#x20;<a href="https://github.com/electricbolt/appfiguratesdk/releases/download/5.0.0/AppfigurateSE-5.0.0-arm64.dmg" class="button primary" data-icon="cloud-arrow-down">Mac with Apple chip - 5.0.0</a>&#x20;

### Appfigurate app for physical iOS devices

The Appfigurate app for physical iOS devices is available from the Apple App Store:

<a href="https://itunes.apple.com/us/app/appfigurate/id1332575368?ls=1&#x26;mt=8" class="button primary" data-icon="apple">Download on the App Store - 5.0.0</a>

## Library integration

The steps required to integrate the Appfigurate Library into your app differ depending upon your app type:

* [iOS](/getting-started/ios-native-app-integration) native app or app extension (Swift or Objective-C).
* [watchOS](/getting-started/watchos-app-integration) app or app extension (Swift or Objective-C).
* [Flutter](/getting-started/flutter-integration) app (iOS).
* [React Native](/getting-started/react-native-integration) app (iOS).

## User guides

If you want to learn about the screens and functionality of Appfigurate app for iOS Simulator, or physical iOS devices, please read the [user guide](/appfigurate-user-guide/introduction).

AppfigurateSE macOS app has a [user guide](/appfigurate-se-user-guide/introduction).


# Examples

The following GitHub repositories are available to demonstrate the integration of Appfigurate Library into iOS and Mobile Flutter apps and app extensions.

## Prerequisites

You must have the following installed:

* [Appfigurate Simulator](/appfigurate-se-user-guide/setup-ios-simulator-app) app installed into one or more iOS Simulators (use the AppfigurateSE macOS app for easy one-click installation).
* The [example app's metadata records](/appfigurate-user-guide/install-example-apps).

## iOS

{% hint style="warning" %}
**Note:** You *cannot* have both the Objective-C and Swift examples installed in the same Simulator, as they share the same URL Scheme and app identifiers.

To swap between language examples you must delete the existing app from the Simulator and clear Xcode's DerivedData directory.
{% endhint %}

### Swift example

{% embed url="<https://github.com/electricbolt/appfiguratesdk-swiftexample>" %}

Contains the following integrations:

* iOS native app
* iOS native app extension
* iOS native app UI automation testing support
* watchOS app extension
* watchOS intent extension

#### Installation instructions

```bash
git clone https://github.com/electricbolt/appfiguratesdk-swiftexample.git
```

In Xcode, open `SwiftExample.xcodeproj`

Ensure Appfigurate package dependency has been downloaded. (File ‣ Packages ‣ Reset Package Caches).

Select `AppfigurateExample (Swift iOS)` target, and choose the iOS Simulator that you previously installed Appfigurate Simulator into. e.g.

![](/files/YQSodaTKF3yLzb96hq8K)

Tap Run.

## Flutter

{% embed url="<https://github.com/electricbolt/appfiguratesdk-flutter>" %}

Contains the following integrations:

* iOS app

#### Installation instructions

```bash
git clone https://github.com/electricbolt/appfiguratesdk-flutter.git
```

In Android Studio, open the directory containing the projects `pubspec.yaml`

Select `main.dart` Run/Debug Configuration, and choose the iOS Simulator that you previously installed Appfigurate Simulator into. e.g.

<div align="left"><figure><img src="/files/PygcYi7j54ZDrb9IxVF7" alt="" width="311"><figcaption></figcaption></figure></div>

Tap Run.


# Upgrade guide

For existing integrations of Appfigurate, read the instructions in the following upgrade guides.

See what's new by reading the [Release History](/legal/release-history) of Appfigurate.


# v1.4.0 to v2.2.0

## Runtime compatibility

Appfigurate app 2.2.0 is backwardly runtime compatible with apps and app extensions linked against Appfigurate library 1.4.0.

## Source compatibility

Appfigurate 2.2.0 is mostly source compatible with 1.4.0. Some minor changes are required:

## Info.plist changes

* Remove the `APLInstallDelegateMethods` key/value pair if it exists. Appfigurate Library no longer supports swizzling `UIApplicationDelegate` or `WKExtensionDelegate`. Instead you must programmatically call `APLApplicationDidFinishLaunchingWithOptions`, `APLApplicationDidFinishLaunching` and `APLApplicationOpenURL` at the appropriate times.

## Project changes

* Appfigurate Library is now distributed as a single static XCFramework rather than separate `libAppfigurateLibrary.a` (iOS) and `libAppfigurateLibraryWatch.a` (watchOS) static libraries.
* Delete the old `libAppfigurateLibrary.a` and/or `libAppfigurateLibraryWatch.a` files from your project. Remove any search paths that pertain to Appfigurate under the `App target` ‣ `Build Settings` ‣ `Library Search Paths/Header Search Paths`.
* Add `AppfiguateLibrary.xcframework` to your project by dragging the `.xcframework` bundle over the top of the project navigator into your projects `Frameworks` directory. You can also continue to use Swift Package Manager, Cocoapods or Carthage.

<figure><img src="/files/ZMmSzudXHGTWkW4LkQvj" alt=""><figcaption></figcaption></figure>

* Appfigurate Library requires Swift to be linked to your target. If your Objective-C project doesn't currently have any Swift source files, you'll need to create at least one empty `.swift` file.

## Objective-C changes

* References to `#import "AppfigurateLibrary.h"` need to be changed to `@import AppfigurateLibrary;`.
* The configuration property macros all contain an extra `restart` parameter. The `restart` parameter is after the `description` parameter in every macro. If your configuration property requires an app restart after changing, set the value to YES.

> SDK 1.4.0 examples (before)

```objectivec
BOOL_PROPERTY(logging, @"Enable debug logging to console");
DOUBLE_PROPERTY_SLIDER(maxDecibel, 60.5, 120.41, APLIconSliderVolume, @"Clipping - decibels");
STRING_PROPERTY_LIST(backgroundColorHex, @"Color of background", @{@"LightGray":@"#d3d3d3", @"White":@"#ffffff", @"Beige":@"f5fcdc"});
```

> SDK 2.2.0 examples (after)

```objectivec
BOOL_PROPERTY(logging, @"Enable debug logging to console", NO);
DOUBLE_PROPERTY_SLIDER(maxDecibel, 60.5, 120.41, APLIconSliderVolume, @"Clipping - decibels", NO);
STRING_PROPERTY_LIST(backgroundColorHex, @"Color of background", NO, @{@"LightGray":@"#d3d3d3", @"White":@"#ffffff", @"Beige":@"f5fcdc"});
```

* Add an implementation of the C `APLConfigurationClass` function (prototype available in `Appfigurate.h` header) to the bottom of your `APLConfiguration` subclass:

```objectivec
...
   self.debugLogging = YES;
   self.serverURL = @"https://www.yourappserver.com/api";
}

@end

Class APLConfigurationClass(void) {
    return [ExampleConfiguration class];
}
```


# v2.1.1 to v2.2.0

## Runtime compatibility

Appfigurate app 2.2.0 is backwardly runtime compatible with apps and app extensions linked against Appfigurate library 2.1.1.

## Source compatibility

Appfigurate 2.2.0 is mostly source compatible with 2.1.1. Some minor changes are required:

## Info.plist changes

* Remove the `APLConfigurationClass` key/value pair. In order to improve reliability and startup time of your app, Appfigurate Library no longer uses the `APLConfigurationClass` key/value pair to instantiate your `APLConfiguration` subclass. Instead it calls the `APLConfigurationClass` function that you implement (see Objective-C/Swift changes below):

## Objective-C/Swift changes

* Add a new C function `APLConfigurationClass` to the bottom of your APLConfiguration subclass:

> Objective-C example

```objectivec
Class APLConfigurationClass(void) {
    return [ExampleConfiguration class];
}
```

> Swift example

```swift
@_cdecl("APLConfigurationClass")
func APLConfigurationClass() -> AnyClass {
    return ExampleConfiguration.self
}
```

### Upgrading your APLConfiguration subclass from Objective-C ‣ Swift

One of the key features of Appfigurate 2.2.0 is support for Swift 5.3 using property wrappers. It is relatively simple to upgrade your `APLConfiguration` subclass from Objective-C to Swift:

#### Output Swift implementation

Using Appfigurate tap ≡ ‣ *App* ‣ Output Implementation under the SWIFT LIBRARY INTEGRATION section. The output will be similar to the following:

```swift
import Foundation
import AppfigurateLibrary

@objcMembers class Configuration: APLConfiguration {

    @BoolProperty(description: "Log debug output to console", restart: false)
    var debugLogging: Bool

    @StringPropertyListEdit(regex: #"https://[\w\.-]+\.yourappserver.com/.*"#, description: "Application server url", restart: false, values: ["Dev":"https://dev.yourappserver.com/api", "Prod":"https://www.yourappserver.com/api"])
    var serverURL: String

    override func allowInvalidSignatures() -> Bool {
        return !ENCRYPTED()
    }

    override func publicKey() -> String {
        // 41 36 87 71 0D 05
	return "-----BEGIN PUBLIC KEY-----\n" +
            "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4TZnKfGeXttN7Rr3eiAZ\n" +
            "PMEPsZvbo7lgIpMh6OjgBsoqkJJP0yXXLtpqsBCv8vm7RYqCn5+yfkiCQiXvkJBz\n" +
            "FSKmLF9EPR9l1H+32Id82dDuseD70D66puPUHjciEgmU18DpW2NVvTAykMwTEsiR\n" +
            "0h/ExBEhUe75qtwlVno8cMFbEfVtiGbKECvWIr122ED71T0Jt2Bcxqx1a7c1hPIV\n" +
            "RwLxIfWfE0+2rB9nJVPBgsTVPywibDvjio82FousyMDmvkAbMq5iyuyvJ0+5bATz\n" +
            "o12GEt5lSiQlCMzfmkWYBROMDCh27qGFVVo1XAUCVsMfsW9n4iQcoLAdUp/LI3B3\n" +
            "ywIDAQAB\n" +
            "-----END PUBLIC KEY-----\n"
    }

    override func reset() {
        debugLogging = true
        serverURL = "https://www.yourappserver.com/api"
    }
}

@_cdecl("APLConfigurationClass")
func APLConfigurationClass() -> AnyClass {
    return ExampleConfiguration.self
}
```

Delete the `debugLogging` and `serverURL` properties and port your Objective-C properties across one-by-one as per the following [Macros ‣ Property wrappers](#macros-property-wrappers) section below.

#### Macros ‣ Property wrappers

Appfigurate's swift property wrappers are named the same as the Objective-C macros making it easy to port:

<table><thead><tr><th width="383">Objective-C macro</th><th>Swift property wrapper</th></tr></thead><tbody><tr><td>BOOL_PROPERTY</td><td>@BoolProperty</td></tr><tr><td>STRING_PROPERTY_EDIT</td><td>@StringPropertyEdit</td></tr><tr><td>STRING_PROPERTY_LIST</td><td>@StringPropertyList</td></tr><tr><td>STRING_PROPERTY_LIST_EDIT</td><td>@StringPropertyListEdit</td></tr><tr><td>ENCRYPTED_STRING_PROPERTY_LIST_EDIT</td><td>@EncryptedStringPropertyListEdit</td></tr><tr><td>INT_PROPERTY_SLIDER</td><td>@IntPropertySlider</td></tr><tr><td>INT_PROPERTY_EDIT</td><td>@IntPropertyEdit</td></tr><tr><td>INT_PROPERTY_LIST</td><td>@IntPropertyList</td></tr><tr><td>INT_PROPERTY_LIST_EDIT</td><td>@IntPropertyListEdit</td></tr><tr><td>FLOAT_PROPERTY_SLIDER</td><td>@FloatPropertySlider</td></tr><tr><td>FLOAT_PROPERTY_EDIT</td><td>@FloatPropertyEdit</td></tr><tr><td>FLOAT_PROPERTY_LIST</td><td>@FloatPropertyList</td></tr><tr><td>FLOAT_PROPERTY_LIST_EDIT</td><td>@FloatPropertyListEdit</td></tr><tr><td>DOUBLE_PROPERTY_SLIDER</td><td>@DoublePropertySlider</td></tr><tr><td>DOUBLE_PROPERTY_EDIT</td><td>@DoublePropertyEdit</td></tr><tr><td>DOUBLE_PROPERTY_LIST</td><td>@DoublePropertyList</td></tr><tr><td>DOUBLE_PROPERTY_LIST_EDIT</td><td>@DoublePropertyListEdit</td></tr><tr><td>ACTION_METHOD</td><td>No Swift equivalent</td></tr></tbody></table>

> Objective-C Example

```objectivec
@interface Configuration : APLConfiguration
@property(nonatomic, assign) BOOL logging;
@end

@implementation Configuration
BOOL_PROPERTY(logging, @"Enable debug logging to console", NO);
...
```

> Swift Example

```swift
@objcMembers class Configuration: APLConfiguration {
    @BoolProperty(description: "Enable debug logging to console", restart: false)
    var logging: Bool
    ...
```

#### @objcMembers/@objc

You must either mark the entire `APLConfiguration` subclass with `@objcMembers` (recommended approach) or mark each configuration property individually with `@objc`. Omitting these markers will cause the properties to be unavailable for access by Appfigurate Library.

> @objcMembers Example

```swift
@objcMembers class Configuration: APLConfiguration {
    @BoolProperty(description: "Enable debug logging to console", restart: false)
    var logging: Bool
    ...
```

> @objc Example

```swift
class Configuration: APLConfiguration {
    @BoolProperty(description: "Enable debug logging to console", restart: false)
    @objc var logging: Bool
    ...
```


# v2.2.1 to v3.0.0

## Runtime compatibility

Appfigurate 3.0.0 is backwardly runtime compatible with apps and app extensions linked against Appfigurate library 2.2.1.

Upgrading to Appfigurate 3.0.0 from a previous version converts the database to a new format, therefore downgrading to an earlier version is no longer possible.

[Appfigurate Simulator for iOS](/appfigurate-se-user-guide/setup-ios-simulator-app) is now included in the AppfigurateSE macOS app, and have easy 1 click installation. You no longer have to build the Appfigurate Simulator app in Xcode to deploy.

AppfigurateSE macOS app is now a direct download instead of through the Mac App Store.

## Source compatibility

Appfigurate 3.0.0 is mostly source compatible with 2.2.1. Some minor changes are required:

* The `[APLConfiguration customIdentifier]` method is no longer supported and you should remove any subclass implementations.


# v3.2.1 to v4.0.0

## Runtime compatibility

Appfigurate app version 4.0.0 is backwardly runtime compatible with apps and app extensions linked against earlier versions of Appfigurate Library.

## iOS source compatibility

Appfigurate Library version 4.0.0 requires the following source code changes to your app:

* The [`allowInvalidSignatures`](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)allowInvalidSignatures) method must be overridden, as it no longer provides default behaviour. You must remove any calls to `super`, otherwise an [`AppfigurateLibraryException`](https://www.electricbolt.co.nz/api/Constants.html#/c:@AppfigurateLibraryException) will be thrown.
* The [`reset`](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)reset) method must be overridden. You must remove any calls to `super`, otherwise an [`AppfigurateLibraryException`](https://www.electricbolt.co.nz/api/Constants.html#/c:@AppfigurateLibraryException) will be thrown.
* The [`publicKey`](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)publicKey) method must be overridden. You must remove any calls to `super`, otherwise an [`AppfigurateLibraryException`](https://www.electricbolt.co.nz/api/Constants.html#/c:@AppfigurateLibraryException) will be thrown.
* A new `CONFIGURATION` function has been added to the Swift class - see [Output Source Code Snippets](/getting-started/ios-native-app-integration#swift).
* A new `CONFIGURATION` macro has been added to the Objective-C header - see [Output Source Code Snippets](/getting-started/ios-native-app-integration#objective-c).
* The `customIdentifier` method has been removed [(deprecated in 3.0.0)](/getting-started/upgrade-guide/v2.2.1-to-v3.0.0#source-compatibility).
* Default values of `NSString`/`String` properties are now an empty string `""` instead of `null`. You should continue to set a default value in the [`reset`](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)reset) method for your properties.
* Appfigurate no longer supports having an iOS and watchOS app using different public/private key pairs. Instead the watchOS app must be changed to use the iOS public/private key pair (in Appfigurate, remove your existing watchOS app, then duplicate the iOS app as a watchOS app). The `ENCRYPTED_STRING_IOS_WATCHOS` function/macro has been removed, replace with [`ENCRYPTED_STRING`](/appfigurate-se-user-guide/manual-encryption/encrypted_string-macro-function) function/macro instead.

**Before migration (abbreviated Swift example shown)**

```swift
@EncryptedStringPropertyListEdit(regex: "", description: "encrypted String - textfield, customizable list", encrypted: ENCRYPTED(), restart: false, values: [
    "Red":ENCRYPTED_STRING_IOS_WATCHOS("r","3/iZ...3ysB","dHRf...reoB"),
    "Green":ENCRYPTED_STRING_IOS_WATCHOS("g","PkQf...fdgB","B2l4...XOYB"),
    "Blue":ENCRYPTED_STRING_IOS_WATCHOS("b","jaIx...7poB","hksx...R5IB")])
var encrypted_string_Textfield_List: String

override func publicKey() -> String {
#if os(iOS)
    // 41 36 87 71 0D 05
    return "-----BEGIN PUBLIC KEY-----\n" +
        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4TZnKfGeXttN7Rr3eiAZ\n" +
        ...
        "ywIDAQAB\n" +
        "-----END PUBLIC KEY-----\n"
#elseif os(watchOS)
    // 0C 96 66 39 0C 04
    return "-----BEGIN PUBLIC KEY-----\n" +
        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArxgy6NU0IsJxLSZqcZzJ\n" +
        ...
        "QQIDAQAB\n" +
        "-----END PUBLIC KEY-----\n"
#endif
}
```

**After migration (abbreviated Swift example shown)**

```swift
@EncryptedStringPropertyListEdit(regex: "", description: "encrypted String - textfield, customizable list", encrypted: ENCRYPTED(), restart: false, values: [
    "Red":ENCRYPTED_STRING("r","3/iZ...3ysB"),
    "Green":ENCRYPTED_STRING("g","PkQf...fdgB"),
    "Blue":ENCRYPTED_STRING("b","jaIx...7poB")])
var encrypted_string_Textfield_List: String

override func publicKey() -> String {
    // 41 36 87 71 0D 05
    return "-----BEGIN PUBLIC KEY-----\n" +
        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4TZnKfGeXttN7Rr3eiAZ\n" +
        ...
        "ywIDAQAB\n" +
        "-----END PUBLIC KEY-----\n"
}
```

* Property list keys may no longer contain `{`  and `}`  characters, these characters are now used for specifying [environment tags](/configuration-subclasses/environment-tags).
* Cocoapods and Carthage are no longer supported, move to Swift Package Manager instead. The existing Cocoapods repo remains at 3.2.1 and will not be updated.

## Android source compatibility

{% hint style="danger" %}
Appfigurate app for physical **Android** devices, Appfigurate app for **Android** Emulator, **Android** pre-packaged apps for real device cloud testing services and the AppfigurateSE **Windows** app **have been withdrawn from sale and are no longer available,** due to low usage.
{% endhint %}

Appfigurate Library version 4.0.0 requires the following source code changes to your app:

* The [`allowInvalidSignatures`](https://www.electricbolt.co.nz/api/android/nz/co/electricbolt/appfiguratelibrary/Configuration.html#allowInvalidSignatures\(\)) method must be overridden, as it no longer provides default behaviour and is now marked `abstract`.
* The [`reset`](https://www.electricbolt.co.nz/api/android/nz/co/electricbolt/appfiguratelibrary/Configuration.html#reset\(\)) method must be overridden, and is now marked abstract. You must remove any calls to `super.reset()` .
* A new `CONFIGURATION` function has been added to the Kotlin and Java classes - see [Output Source Code Snippets](broken://pages/DogMPJZUANMuXzu8Acwk#output-source-code-snippets).
* Default values of `String` properties are now an empty string `""` instead of `null`. You should continue to set a default value in the [`reset`](https://www.electricbolt.co.nz/api/android/nz/co/electricbolt/appfiguratelibrary/Configuration.html#reset\(\)) method for your properties.
* Property list keys may no longer contain `{`  and `}`  characters, these characters are now used for specifying [environment tags](/configuration-subclasses/environment-tags).


# v4.0.0 to v5.0.0

## Runtime compatibility

Appfigurate app version 5.0.0 is backwardly runtime compatible with apps and app extensions linked against earlier versions of Appfigurate Library.

## iOS source compatibility

Appfigurate Library version 5.0.0 require the following source code changes to your app:

* &#x20;Existing Swift `APLConfiguration` subclasses *may* require you to add an initialiser.

New functionality:

* Displaying overridden configuration in a SwiftUI is now easier using the new [`.configurationView`](/additional-reading/displaying-overridden-configuration#swiftui) modifier.


# iOS native app integration

Integrating Appfigurate Library into iOS native apps

iOS native apps can be developed in both Swift and Objective-C.

For Flutter apps, jump to [Flutter integration](/getting-started/flutter-integration). For React Native apps, jump to [React Native integration](/getting-started/react-native-integration).

## Prerequisites

You must have the following:

* Xcode 26+
* iOS SDKs and associated Simulators.
* AppfigurateSE macOS app.
* Appfigurate Simulator app installed into one or more iOS Simulators (use the [AppfigurateSE macOS app](/appfigurate-se-user-guide/setup-ios-simulator-app) for easy one-click installation).
* Your iOS app must target iOS 15.0+ in order to link Appfigurate Library.

## Add AppfigurateLibrary.xcframework to the iOS app target

Choose one of the following integration methods (all of which are compatible with both Swift and Objective-C):

{% tabs %}
{% tab title="Swift Package Manager" %}
Add a package dependency `File ‣ Add Package Dependencies` with the URL `https://github.com/electricbolt/appfiguratesdk`
{% endtab %}

{% tab title="Manual" %}
Clone the github repo:

```bash
git clone 
https://github.com/electricbolt/appfiguratesdk.git
```

Drag and drop `AppfigurateLibrary.xcframework` into your project.
{% endtab %}
{% endtabs %}

## Add new app into Appfigurate Simulator

Run Appfigurate in the iOS Simulator.

Tap `≡` `Add app`.

Select app type `iOS`.

Enter a URL scheme that will be used by Appfigurate to launch your app in order to read or apply configuration. The URL scheme must be 4-64 ASCII characters in length and must be unique to your app. e.g. `YOUR-APP-URLSCHEME`

Tap `Add app`.

## Output source code snippets

Tap `≡` `YOUR-APP-URLSCHEME`.

The Edit app screen will be displayed.

{% tabs %}
{% tab title="Swift" %}
Under the `SWIFT/IOS LIBRARY INTEGRATION` section:

Tap `Output implementation` then tap `Clipboard`.
{% endtab %}

{% tab title="Objective-C" %}
Open the macOS `Console.app`. Select the iOS Simulator device in the left hand pane. Type `process:appfigurate` in the search box in the top right.

In Appfiguate Simulator app, under the `OBJ-C/IOS LIBRARY INTEGRATION` section:

Tap `Output header` then tap `Console`.

Tap `Output implementation` then tap `Console`.
{% endtab %}
{% endtabs %}

## Create APLConfiguration subclass

{% tabs %}
{% tab title="Swift" %}
In Xcode, tap `File` ‣ `New` ‣ `New "Configuration.swift"` file from Clipboard.

{% hint style="info" %}
**Note:** *your* public key copied into the clipboard in the [Output source code snippets](#output-source-code-snippets) section above **will** be different to the public key in the following example.
{% endhint %}

> Swift Configuration example

```swift
import Foundation
import AppfigurateLibrary

@inline(__always)
func CONFIGURATION() -> Configuration {
    return APLConfiguration.shared() as! Configuration
}

@objcMembers class Configuration: APLConfiguration {
    @BoolProperty(description: "Log debug output to console", restart: false)
    var debugLogging: Bool

    @StringPropertyListEdit(regex: #"https://[\w\.-]+\.yourappserver.com/.*"#, description: "Application server url", restart: false, values: ["Dev":"https://dev.yourappserver.com/api", "Prod":"https://www.yourappserver.com/api"])
    var serverURL: String

    override func allowInvalidSignatures() -> Bool {
        return !ENCRYPTED()
    }

    override func publicKey() -> String {
        // 41 36 87 71 0D 05
        return "-----BEGIN PUBLIC KEY-----\n" +
            "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4TZnKfGeXttN7Rr3eiAZ\n" +
            ...
            "ywIDAQAB\n" +
            "-----END PUBLIC KEY-----\n";
    }

    override func reset() {
        debugLogging = true
        serverURL = "https://www.yourappserver.com/api"
    }

}

@_cdecl("APLConfigurationClass")
func APLConfigurationClass() -> AnyClass {
    return Configuration.self
}
```

The [<mark style="color:blue;">`APLConfigurationClass`</mark>](https://www.electricbolt.co.nz/api/Functions.html#/c:@F@APLConfigurationClass) function with C calling convention must be implemented in your app otherwise a linker error will be issued. The recommended place to implement is at the bottom of your [<mark style="color:blue;">`APLConfiguration`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html) subclass.
{% endtab %}

{% tab title="Objective-C" %}
In Xcode, add a new Cocoa Touch class, subclassing [<mark style="color:blue;">`APLConfiguration`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html), e.g. `Configuration`

In your apps `Configuration.h` file, paste the .h header file output to the Console in the section [Output source code snippets](#output-source-code-snippets) above.

> Objective-C Configuration header example

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

#define CONFIGURATION ((Configuration*) [APLConfiguration sharedConfiguration])

@interface Configuration : APLConfiguration

@property(nonatomic, strong) NSString* serverURL;
@property(nonatomic, assign) BOOL debugLogging;

@end
```

In your apps `Configuration.m` file, paste the .m implementation file output to the Console in the section [Output source code snippets](#output-source-code-snippets) above.

{% hint style="info" %}
**Note:** *your* public key copied into the clipboard in the [Output source code snippets](#output-source-code-snippets) section above **will** be different to the public key in the following example.
{% endhint %}

> Objective-C Configuration implementation example

```objectivec
#import "Configuration.h"

@implementation Configuration

BOOL_PROPERTY(debugLogging, @"Log debug output to console", NO)
STRING_PROPERTY_LIST_EDIT(serverURL, @"https:\\/\\/[\\w\\.-]+\\.yourappserver.com/.*", @"URL of app server", NO, @{@"Dev":@"https://dev.yourappserver.com/api", @"Prod":@"https://www.yourappserver.com/api"});

- (BOOL) allowInvalidSignatures {
#if DEBUG
    return YES;
#else
    return NO;
#endif
}

- (NSString*) publicKey {
    // E4 8B B6 25 EE 01
    return @"-----BEGIN PUBLIC KEY-----\n" \
        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnD67fMex1KkP7kltlNaO\n" \
        ...
        "EQIDAQAB\n" \
        "-----END PUBLIC KEY-----\n";
}

- (void) reset {
    self.debugLogging = NO;
    self.serverURL = @"https://www.yourappserver.com/api";
}

@end

Class APLConfigurationClass(void) {
    return [Configuration class];
}
```

The [`APLConfigurationClass`](https://www.electricbolt.co.nz/api/Functions.html#/c:@F@APLConfigurationClass) function must be implemented in your app otherwise a linker error will be issued. The recommended place to implement is at the bottom of your [`APLConfiguration`](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html) subclass.
{% endtab %}
{% endtabs %}

## Edit Info.plist

In your apps `Info.plist` file (right click, `Open As` ‣ `Source Code`) include the following. Replace the text `YOUR-APP-URLSCHEME` with your own app's URL Scheme - the same value you added in the section [Add new app into Appfigurate Simulator](#add-new-app-into-appfigurate-simulator) above.

> `Info.plist` example

```xml
<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleURLName</key>
        <string>appfigurate.YOUR-APP-URLSCHEME</string>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>YOUR-APP-URLSCHEME</string>
        </array>
    </dict>
</array>
```

If you already have an existing `CFBundleURLTypes` array in your `Info.plist` file, then insert just the `<dict> ... </dict>` portion.

## UIApplicationDelegate

In your apps `AppDelegate.swift/.m` file, include calls to [<mark style="color:blue;">`APLApplicationOpenURL`</mark>](https://www.electricbolt.co.nz/api/Functions.html#/c:@F@APLApplicationOpenURL) and [<mark style="color:blue;">`APLDidFinishLaunchingWithOptions`</mark>](https://www.electricbolt.co.nz/api/Functions.html#/c:@F@APLApplicationDidFinishLaunchingWithOptions). Create any missing delegate methods as appropriate.

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

> Swift `UIApplicationDelegate` example

```swift
import AppfigurateLibrary

...

func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
    // When your app opens a URL while running or suspended in memory.
    return APLApplicationOpenURL(url)
}

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // After launch.
    APLApplicationDidFinishLaunchingWithOptions(launchOptions)
    return true
}
```

{% endtab %}

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

> Objective-C `UIApplicationDelegate` example

```objectivec
@import AppfigurateLibrary;

...

- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
    // When your app opens a URL while running or suspended in memory.
    return APLApplicationOpenURL(url);
}

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // After launch.
    APLApplicationDidFinishLaunchingWithOptions(launchOptions);
    return YES;
}
```

{% endtab %}
{% endtabs %}

## For apps using UIWindowSceneDelegate

If your app has a `UIWindowSceneDelegate`, in your apps `SceneDelegate.swift/m` file, include calls to [<mark style="color:blue;">`APLApplicationOpenURL`</mark>](https://www.electricbolt.co.nz/api/Functions.html#/c:@F@APLApplicationOpenURL). Create any missing delegate methods as appropriate.

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

> Swift `UIWindowSceneDelegate` example

```swift
import AppfigurateLibrary

...

func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
    // When your app opens a URL while running or suspended in memory.
    APLApplicationOpenURL(URLContexts.first?.url)
}

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    // After launch.
    APLApplicationOpenURL(connectionOptions.urlContexts.first?.url)
}
```

{% endtab %}

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

> Objective-C `UIWindowSceneDelegate` example

```objectivec
@import AppfigurateLibrary;

...

- (void)scene:(UIScene *)scene openURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts {
    // When your app opens a URL while running or suspended in memory.
    NSURL *url = [[[URLContexts allObjects] firstObject] URL];
    APLApplicationOpenURL(url);
}

- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions {
    // After launch.
    NSURL *url = [[[connectionOptions.URLContexts allObjects] firstObject] URL];
    APLApplicationOpenURL(url);
}
```

{% endtab %}
{% endtabs %}

## Test your iOS app

To test that you've successfully updated your app to use Appfigurate:

* Compile and run your app to the Simulator instance.
* Launch the Appfigurate Simulator app.
* Tap your applications row. The app will be run and made visible, it's configuration read, and then swap back to Appfigurate.
* Appfigurate's [Configure app](/appfigurate-user-guide/configure-app) screen will now be displayed. You can now change the `debugLogging` and `serverURL` properties. Tap `Apply⌄` to apply the configuration to your app.

Now jump to [Supported property types](/configuration-subclasses/supported-property-types).


# iOS app extension integration

Integrating Appfigurate Library into iOS app extensions

iOS app extensions can be developed in both Swift and Objective-C.&#x20;

Appfigurate Library works in most types of iOS app extensions.

## Prerequisites <a href="#prerequisites" id="prerequisites"></a>

You must perform the following before starting iOS app extension integration:

* [iOS native app integration](/getting-started/ios-native-app-integration) of Appfigurate Library.

## Add AppfigurateLibrary.xcframework to the iOS app extension target

In Xcode, tap on your iOS app extension target.

Tap the `General` tab. Tap the `+` button under the `Frameworks, Libraries` section.

Select `AppfigurateLibrary` package.

## Share the iOS app APLConfiguration subclass with the iOS app extension

Share the iOS app's [<mark style="color:blue;">`APLConfiguration`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html) subclass with the iOS app extension.&#x20;

Open your [<mark style="color:blue;">`APLConfiguration`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html) subclass (e.g. `Configuration.swift/m` file) into the Xcode editor.&#x20;

In the `Target Membership` inspection pane tick on your iOS app extension target.

## **Setup Keychain Sharing in the iOS app extension**

Tap on your iOS app extension target.

Tap the `Signing & Capabilities` tabs. Add a `Keychain Sharing` capability with a `Keychain Groups` value that will be common across the iOS app and iOS app extension.&#x20;

We recommend you have a `.shared` suffix on *your* Keychain Groups value.

<figure><img src="/files/v0Ei5gnEF3z6SfstPJXF" alt=""><figcaption></figcaption></figure>

## **Setup Keychain Sharing in the iOS app**

Tap on your iOS app target.&#x20;

Tap `Signing & Capabilities` tabs.&#x20;

Add a `Keychain Sharing` capability with the same `Keychain Groups` value you entered for the iOS app extension above.

<figure><img src="/files/bD05kUxXJf4XE3oARBLQ" alt=""><figcaption></figcaption></figure>

## Edit Info.plist in the iOS app

In your iOS app's `Info.plist` file (right click, `Open As` ‣ `Source Code`) include the `APLKeychainAccessGroup` key replacing the value `nz.co.electricbolt.appfigurateexample.shared` with *your* `Keychain Sharing`'s `Keychain Groups` value.

> `Info.plist` example

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
     <key>APLKeychainAccessGroup</key>
     <string>nz.co.electricbolt.appfigurateexample.shared</string>
     ...
```

{% hint style="warning" %}
If the `APLKeychainAccessGroup` key is not defined, then keychain access group functionality is not used. Do not specify an empty or otherwise invalid `APLKeychainAccessGroup` value.
{% endhint %}

{% hint style="success" %}
iOS app extensions automatically use the `Info.plist` file from the containing iOS app. You do not need to modify the iOS app extension's `Info.plist` file.
{% endhint %}

## Test your iOS app extension

To test that you've successfully updated your iOS app extension to use Appfigurate:

* Compile and run your app to the Simulator instance.
* Launch the Appfigurate Simulator app.
* Tap your applications row. The app will be run and made visible, it's configuration read, and then swap back to Appfigurate.
* Appfigurate's [Configure app](/appfigurate-user-guide/configure-app) screen will now be displayed. You can now change the `debugLogging` and `serverURL` properties. Tap `Apply⌄` to apply the configuration to your app.
* Invoke your app extension. It should be using the shared configuration applied to the app.


# watchOS app integration

Integrating Appfigurate Library into watchOS apps

watchOS apps can be developed in both Swift and Objective-C.

{% hint style="warning" %}
**Independent** watchOS apps are **not** supported, watchOS apps **must** be bundled inside a **companion** iOS app. (The companion iOS app's Appfigurate Library is used to proxy any configuration payloads applied to the watchOS app).
{% endhint %}

## Prerequisites

You must perform the following before starting watchOS native integration:

* [iOS native app integration](/getting-started/ios-native-app-integration) of Appfigurate Library.

In addition to the [iOS native app integration prerequisites](/getting-started/ios-native-app-integration#prerequisites), you must also have:

* watchOS SDKs and associated watchOS Simulators.
* Use paired iOS and watchOS Simulators.
* Appfigurate Simulator app installed into one or more paired iOS Simulators (use the [AppfigurateSE macOS app](/appfigurate-se-user-guide/setup-ios-simulator-app) for easy one-click installation).
* Your watchOS app must target watchOS 8.0+ in order to link Appfigurate Library.

## Add AppfigurateLibrary.xcframework to the watchOS app target

In Xcode:

* Tap on your watchOS app target.
* Tap the General tab. Tap the `+` button under the `Frameworks, Libraries, and Embedded Content` section.
* Select `AppfigurateLibrary` package.

## Add new app into Appfigurate Simulator

Run Appfigurate in the paired iOS Simulator.

Tap `≡` `YOUR-IOS-APP` app. The [Edit app](/appfigurate-user-guide/edit-app) screen will be displayed.

Tap `Duplicate row` row. The [Duplicate app](/appfigurate-user-guide/edit-app#duplicate-app) screen will be displayed.

Select app type `watchOS`.

Tap `Duplicate`.

## Share the iOS app APLConfiguration subclass with the watchOS app

Share the iOS app's [<mark style="color:blue;">`APLConfiguration`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html) subclass with the watchOS app extension.&#x20;

Open your [<mark style="color:blue;">`APLConfiguration`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html) subclass (e.g. `Configuration.swift/m` file) into the Xcode editor. In the `Target Membership` inspection pane tick on your watchOS app target.

## WKApplicationDelegate/WKExtensionDelegate

{% tabs %}
{% tab title="Swift (SwiftUI)" %}
For watchOS apps using SwiftUI, you must call [<mark style="color:blue;">`APLApplicationDidFinishLaunching()`</mark>](https://www.electricbolt.co.nz/api/Functions.html#/c:@F@APLApplicationDidFinishLaunching) in the [`WKApplicationDelegate`](https://developer.apple.com/documentation/watchkit/wkapplicationdelegate)'s [`applicationDidFinishLaunching`](https://developer.apple.com/documentation/watchkit/wkapplicationdelegate/applicationdidfinishlaunching\(\)) method.

> Swift `WKApplicationDelegate` example

```swift
import AppfigurateLibrary

...

class MyApplicationDelegate: NSObject, WKApplicationDelegate, ObservableObject {
    func applicationDidFinishLaunching() {
        APLApplicationDidFinishLaunching()
    }
}
```

Ensure you use the [`WKApplicationDelegateAdaptor`](https://developer.apple.com/documentation/swiftui/wkapplicationdelegateadaptor) property wrapper inside your [`App`](https://developer.apple.com/documentation/swiftui/app) declaration to tell SwiftUI about the delegate type:

> Swift `WKApplicationDelegateAdaptor` example

```swift
@main
struct MyApp: App {
    @WKApplicationDelegateAdaptor private var applicationDelegate: MyApplicationDelegate

    var body: some Scene { ... }
}
```

{% endtab %}

{% tab title="Swift (Storyboard)" %}
For watchOS apps using Storyboards, you must call [<mark style="color:blue;">`APLApplicationDidFinishLaunching()`</mark>](https://www.electricbolt.co.nz/api/Functions.html#/c:@F@APLApplicationDidFinishLaunching) in the [`WKExtensionDelegate`](https://developer.apple.com/documentation/watchkit/wkextensiondelegate)'s [`applicationDidFinishLaunching`](https://developer.apple.com/documentation/watchkit/wkextensiondelegate/applicationdidfinishlaunching\(\)) method.

> Swift `WKExtensionDelegate` example

```swift
import AppfigurateLibrary

...

func applicationDidFinishLaunching {
    APLApplicationDidFinishLaunching()
}
```

{% endtab %}

{% tab title="Objective-C" %}
You must call [<mark style="color:blue;">`APLApplicationDidFinishLaunching()`</mark>](https://www.electricbolt.co.nz/api/Functions.html#/c:@F@APLApplicationDidFinishLaunching) in the [`WKExtensionDelegate`](https://developer.apple.com/documentation/watchkit/wkextensiondelegate)'s [`applicationDidFinishLaunching`](https://developer.apple.com/documentation/watchkit/wkextensiondelegate/applicationdidfinishlaunching\(\)) method.

> Objective-C `WKExtensionDelegate` example

```objectivec
@import AppfigurateLibrary;

...

- (void) applicationDidFinishLaunching {
    APLApplicationDidFinishLaunching();
}
```

{% endtab %}
{% endtabs %}

## WCSessionDelegate

If your watch app has an existing `WCSessionDelegate`, then you must add a call to the [<mark style="color:blue;">`APLSessionDidReceiveMessage()`</mark>](https://www.electricbolt.co.nz/api/Functions.html#/c:@F@APLSessionDidReceiveMessage) method. If your watch app does not have a `WCSessionDelegate` then a default implementation is automatically provided.

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

> Swift `WCSessionDelegate` example

```swift
import AppfigurateLibrary

...

func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: @escaping ([String : Any]) -> Void) {
    let reply = APLSessionDidReceiveMessage(message)
    if reply != nil {
        replyHandler(reply)
    } else {
        ...
```

{% endtab %}

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

> Objective-C `WCSessionDelegate` example

```objectivec
@import AppfigurateLibrary;

...

- (void) session: (WCSession*) session didReceiveMessage: (NSDictionary<NSString*,id>*) message replyHandler: (void (^)(NSDictionary<NSString*,id>*)) replyHandler {
    NSDictionary* reply = APLSessionDidReceiveMessage(message);
    if (reply != nil) {
        replyHandler(reply);
    } else {
        ...

```

{% endtab %}
{% endtabs %}

## Test your watchOS app

To test that you've successfully updated your watchOS app to use Appfigurate:

* Compile and run your watchOS app to the paired Simulator instance.
* Launch the Appfigurate Simulator app.
* Tap your watchOS applications row.  The companion iOS app will be run and made visible. The companion iOS app will read the watchOS apps configuration, then swap back to Appfigurate.
* Appfigurate's [Configure app](/appfigurate-user-guide/configure-app) screen will now be displayed. You can now change the `debugLogging` and `serverURL` properties. Tap `Apply⌄` to apply the configuration to your watchOS app.

Now jump to [Supported property types](/configuration-subclasses/supported-property-types).


# watchOS app extension integration

Integrating Appfigurate Library into watchOS app extensions

watchOS apps can be developed in both Swift and Objective-C.

Appfigurate Library works in most types of watchOS app extensions.

## Prerequisites

You must perform the following before starting watchOS app extension integration:

* [watchOS app integration](/getting-started/watchos-app-integration) of Appfigurate Library.

## Add AppfigurateLibrary.xcframework to the watchOS app extension target

In Xcode, tap on your watchOS app extension target.

Tap the `General` tab. Tap the `+` button under the `Frameworks, Libraries` section.

Select `AppfigurateLibrary` package.

## Share the watchOS app APLConfiguration subclass with the watchOS app extension

Share the watchOS app's [<mark style="color:blue;">`APLConfiguration`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html) subclass with the watchOS app extension.&#x20;

Open your [<mark style="color:blue;">`APLConfiguration`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html) subclass (e.g. `Configuration.swift/m` file) into the Xcode editor.&#x20;

In the `Target Membership` inspection pane tick on your watchOS app extension target.

## **Setup Keychain Sharing in the watchOS app extension**

Tap on your watchOS app extension target. &#x20;

Tap the `Signing & Capabilities` tabs. Add a `Keychain Sharing` capability with a `Keychain Groups` value that will be common across the watchOS app and watchOS app extension.&#x20;

We recommend you have a `.shared` suffix on *your* Keychain Groups value.

<figure><img src="/files/v0Ei5gnEF3z6SfstPJXF" alt=""><figcaption></figcaption></figure>

## **Setup Keychain Sharing in the watchOS app**

Tap on your watchOS app target. Tap `Signing & Capabilities` tabs.&#x20;

Add a `Keychain Sharing` capability with the same `Keychain Groups` value you entered for the iOS app extension above.

<figure><img src="/files/bD05kUxXJf4XE3oARBLQ" alt=""><figcaption></figcaption></figure>

## Edit Info.plist in the watchOS app

In your watchOS app's `Info.plist` file (right click, `Open As` ‣ `Source Code`) include the `APLKeychainAccessGroup` key replacing the value `nz.co.electricbolt.appfigurateexample.shared` with *your* `Keychain Sharing`'s `Keychain Groups` value.

> `Info.plist` example

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
     <key>APLKeychainAccessGroup</key>
     <string>nz.co.electricbolt.appfigurateexample.shared</string>
     ...
```

{% hint style="warning" %}
If the `APLKeychainAccessGroup` key is not defined, then keychain access group functionality is not used. Do not specify an empty or otherwise invalid `APLKeychainAccessGroup` value.
{% endhint %}

{% hint style="success" %}
watchOS app extensions automatically use the `Info.plist` file from the containing watchOS app. You do not need to modify the watchOS app extension's `Info.plist` file.
{% endhint %}


# Flutter integration

Integrating Appfigurate Library into a Flutter app

For simplicity, this guide will assume your Flutter app deploys to both iOS (Swift) and Android (Kotlin).

## Prerequisites

You must have the following installed:

* Android Studio, IntelliJ or Visual Studio Code.
* Flutter 3.41.4+ SDK
* Xcode 26+
* iOS SDKs and associated Simulators.
* AppfigurateSE macOS app.
* Appfigurate Simulator app installed into one or more iOS Simulators (use the [AppfigurateSE macOS](/appfigurate-se-user-guide/setup-ios-simulator-app) app for easy one-click installation).
* Your iOS app must target iOS 15.0+ in order to include Appfigurate Flutter Plugin.

## Modify pubspec.yaml

In your app's `pubspec.yaml` add the following to the `dependencies` section:

```yaml
appfigurateflutter: ^5.0.0
```

Perform a `pub get` to ensure the Appfigurate Flutter Plugin is successfully installed.

## Update main.dart

In your apps `main.dart` file, initialize the Appfigurate Flutter Plugin:

> `main.dart` initialization example

```dart
import 'package:appfigurateflutter/appfigurateflutter.dart';

void main() async {  
    await AppfigurateLibrary.initialize();
    ...
```

## Create Dart APLNativeConfiguration subclass

In your app, add a new Dart class named `Configuration.dart`. Paste the following code:

> Dart Configuration example

```dart
import 'package:appfigurateflutter/appfigurateflutter.dart';

class Configuration extends APLNativeConfiguration { 
    factory Configuration() => _instance;
    
    static Configuration _instance = Configuration._internal();

    Configuration._internal();

    bool get debugLogging => nativeBool("debugLogging");
    String get serverUrl => nativeString("serverURL");
}
```

You can now use the `Configuration` class throughout your Flutter app as follows:

```dart
if (Configuration().debugLogging) ...
```

Now jump to [Flutter iOS](/getting-started/flutter-integration/flutter-ios).


# Flutter iOS

{% hint style="info" %}
Appfigurate Flutter Plugin 3.2.2+ supports Swift Package Manager (as well as the existing Cocoapods integration). Please read <https://docs.flutter.dev/packages-and-plugins/swift-package-manager/for-app-developers> to enable.
{% endhint %}

## Add new app into Appfigurate Simulator

Run Appfigurate in the iOS Simulator.

Tap `≡` `Add app`.

Select app type `iOS`.

Enter a URL scheme that will be used by Appfigurate to launch your app in order to read or apply configuration. The URL scheme must be 4-64 ASCII characters in length and must be unique to your app. e.g. `YOUR-APP-URLSCHEME`

Tap `Add app`.

## Output source code snippets

In Appfiguate Simulator app, under the `SWIFT/IOS LIBRARY INTEGRATION` section:

Tap `Output implementation` then tap `Clipboard`.

## Create APLConfiguration subclass

In Xcode, add a new Swift class, subclassing [<mark style="color:blue;">`APLConfiguration`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html). e.g. `Configuration`

In your apps `Configuration.swift` file, paste the implementation file in the clipboard from the section [Output source code snippets](#output-source-code-snippets) above.

{% hint style="info" %}
**Note:** *your* public key copied into the clipboard in the [Output source code snippets](#output-source-code-snippets) section above will be different to the public key in the following example.
{% endhint %}

> Swift Configuration example

```swift
import Foundation
import AppfigurateLibrary

@objcMembers class Configuration: APLConfiguration {

    @BoolProperty(description: "Log debug output to console", restart: false)
    var debugLogging: Bool

    @StringPropertyListEdit(regex: #"https://[\w\.-]+\.yourappserver.com/.*"#, description: "Application server url", restart: false, values: ["Dev":"https://dev.yourappserver.com/api", "Prod":"https://www.yourappserver.com/api"])
    var serverURL: String

    override func allowInvalidSignatures() -> Bool {
        return !ENCRYPTED()
    }

    override func publicKey() -> String {
        // 41 36 87 71 0D 05
        return "-----BEGIN PUBLIC KEY-----\n" +
            "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4TZnKfGeXttN7Rr3eiAZ\n" +
            ...
            "ywIDAQAB\n" +
            "-----END PUBLIC KEY-----\n";
    }

    override func reset() {
        debugLogging = true
        serverURL = "https://www.yourappserver.com/api"
    }

}

@_cdecl("APLConfigurationClass")
func APLConfigurationClass() -> AnyClass {
    return Configuration.self
}
```

The [<mark style="color:blue;">`APLConfigurationClass`</mark>](https://www.electricbolt.co.nz/api/Functions.html#/c:@F@APLConfigurationClass) function with C calling convention must be implemented in your app otherwise a linker error will be issued. The recommended place to implement is at the bottom of your [<mark style="color:blue;">`APLConfiguration`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html) subclass.

## Edit Info.plist

In your apps `Info.plist` file (right click, Open As ‣ Source Code) include the following. Replace the text `YOUR-APP-URLSCHEME` with your own app's URL Scheme - the same value you added in the section [Add new app into Appfigurate Simulator](#add-new-app-into-appfigurate-simulator) above.

> `Info.plist` example

```xml
<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleURLName</key>
        <string>appfigurate.YOUR-APP-URLSCHEME</string>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>YOUR-APP-URLSCHEME</string>
        </array>
    </dict>
</array>
```

If you already have an existing `CFBundleURLTypes` array in your `Info.plist` file, then insert just the `<dict> ... </dict>` portion.

## Test your iOS app

To test that you've successfully updated your app to use Appfigurate:

* Compile and run your app to the Simulator instance.
* Launch the Appfigurate Simulator app.
* Tap your applications row. The app will be run and made visible, it's configuration read, and then swap back to Appfigurate.
* Appfigurate's [Configure app](/appfigurate-user-guide/configure-app) screen will now be displayed. You can now change the `debugLogging` and `serverURL` properties. Tap `Apply⌄` to apply the configuration to your app.

Now jump to [Supported property types](/configuration-subclasses/supported-property-types).


# React Native integration

## Prerequisites

You must perform the following before starting React Native (tested version 0.75) integration:

* [iOS native integration](broken://pages/DogMPJZUANMuXzu8Acwk) of Appfigurate Library.

## Native module integration

Once you have performed iOS native integration (above), you can perform React Native integration.

Now jump to [iOS native module integration](/getting-started/react-native-integration/ios-native-module-integration).


# iOS native module integration

In your Xcode project create a new Objective-C class named `RCTAppfiguratePlugin`.

> Example `RCTAppfiguratePlugin.h` header file

```objectivec
#import <Foundation/Foundation.h>
#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>

NS_ASSUME_NONNULL_BEGIN

@interface RCTAppfigurateModule : RCTEventEmitter<RCTBridgeModule>
@end

NS_ASSUME_NONNULL_END
```

> Example `RCTAppfiguratePlugin.m` implementation file

```objectivec
#import "RCTAppfigurateModule.h"
@import AppfigurateLibrary;

@interface APLConfiguration ()
- (NSDictionary*) dictionaryFromConfiguration;
@end

@implementation RCTAppfigurateModule {
  NSDictionary* cachedConfiguration;
  BOOL observing;
}

RCT_EXPORT_MODULE();

- (instancetype) init {
  self = [super init];
  if (self) {
    cachedConfiguration = [[APLConfiguration sharedConfiguration] dictionaryFromConfiguration];
    APLAddConfigurationUpdatedBlock(^(NSNotification* notification) {
      self->cachedConfiguration = [[APLConfiguration sharedConfiguration] dictionaryFromConfiguration];
      if (self->observing) {
        NSObject* action = notification.userInfo[APLConfigurationUpdatedAction];
        if (action == nil) {
          action = [NSNull null];
        }
        [self sendEventWithName: @"APLConfigurationUpdated" body:
          @{@"APLConfigurationUpdatedAction": action}];
      }
    });
  }
  return self;
}

RCT_EXPORT_METHOD(nativeValue: (NSString*) propertyName resolver: (RCTPromiseResolveBlock) resolve rejecter: (RCTPromiseRejectBlock) reject) {
  resolve([cachedConfiguration objectForKey: propertyName]);
}

RCT_EXPORT_METHOD(description: (RCTPromiseResolveBlock) resolve rejecter: (RCTPromiseRejectBlock) reject) {
  resolve([[APLConfiguration sharedConfiguration] description]);
}

RCT_EXPORT_METHOD(modifications: (RCTPromiseResolveBlock) resolve rejecter: (RCTPromiseRejectBlock) reject) {
  resolve([[APLConfiguration sharedConfiguration] modifications]);
}

RCT_EXPORT_METHOD(version: (RCTPromiseResolveBlock) resolve rejecter: (RCTPromiseRejectBlock) reject) {
  resolve(APLVersion());
}

RCT_EXPORT_METHOD(setLogging: (BOOL) logging resolve: (RCTPromiseResolveBlock) resolve rejecter: (RCTPromiseRejectBlock) reject) {
  APLSetLogging(logging);
  resolve(nil);
}

RCT_EXPORT_METHOD(saveConfiguration: (RCTPromiseResolveBlock) resolve rejecter: (RCTPromiseRejectBlock) reject) {
  APLSaveConfiguration();
  resolve(nil);
}

RCT_EXPORT_METHOD(restoreConfiguration: (RCTPromiseResolveBlock) resolve rejecter: (RCTPromiseRejectBlock) reject) {
  APLRestoreConfiguration();
  resolve(nil);
}

- (void) startObserving {
  observing = YES;
}

- (void) stopObserving {
  observing = NO;
}

- (NSArray<NSString*>*) supportedEvents {
  return @[@"APLConfigurationUpdated"];
}

@end

```

The iOS native module is now integrated. Now jump to [JavaScript integration](/getting-started/react-native-integration/javascript-integration).


# JavaScript integration

To access AppfigurateLibrary from JavaScript, import `NativeModules` and `NativeEventEmitter`, then define `Appfigurate` and `AppfigurateEvents`:

```javascript
import {
  NativeModules,
  NativeEventEmitter,
} from 'react-native';

const {Appfigurate} = NativeModules;
const AppfigurateEvents = new NativeEventEmitter(NativeModules.Appfigurate);
```

You can now access your app's configuration using the following:

```javascript
let serverURL = await Appfigurate.nativeValue('serverURL') // dev.appfigurate.com
let debugLogging = await Appfigurate.nativeValue('debugLogging')); // false
```

To be notified when the configuration is changed at runtime:

```javascript
AppfigurateEvents.addListener('APLConfigurationUpdated', result => {
  // do something
});
```

See also [React Native API](/api/react-native-api) for the list of APIs available.


# Third party remote configuration providers

Appfigurate supports integration of most third party remote configuration providers so that you can *locally* change remote configuration properties and feature toggles without affecting your entire customer base.&#x20;

* [Firebase Remote Config](/getting-started/third-party-remote-configuration-providers/firebase-remote-config)
* [Launch Darkly](/getting-started/third-party-remote-configuration-providers/launch-darkly)
* [Other third party remote configuration providers](/getting-started/third-party-remote-configuration-providers/other-third-party-remote-configuration-providers) not listed above

{% hint style="success" %}
If your third party remote configuration provider is not listed above, and you'd like us to document it's integration, send an email to <support@electricbolt.co.nz>
{% endhint %}


# Firebase Remote Config

Tested with Firebase version 12.2.0 (iOS)

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

* locally change Firebase remote configuration without affecting your entire customer base.
* compile time type safety - Appfigurate remote properties are typed.
* avoids hardcoding duplicated flag names throughout your app.
* deleting a remote configuration property from Firebase 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 [Firebase](https://firebase.google.com/docs/remote-config) integrated into your app with the following example remote properties created in the [Firebase Console](https://console.firebase.google.com/):

<figure><img src="/files/7ex9Cil77Fl5Fp4cBj9N" 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](/configuration-subclasses/supported-property-types#remote-properties).

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

> Swift Configuration example

```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
    }
...
}
```

{% 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 configuration values from Firebase. Copy and paste the following code into your app.

The `defaultValue` parameter in the callback is not used with Firebase. See the [Apply default values for Firebase](#apply-default-values-to-firebase) section below.

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

> Swift example

```swift
APLFetchRemoteConfiguration { remoteKey, propertyType, defaultValue in
    if propertyType == .string {
        return self.remoteConfig.configValue(forKey: remoteKey).stringValue as NSObject
    } else if propertyType == .bool {
        return self.remoteConfig.configValue(forKey: remoteKey).boolValue as NSObject
    } else { // .int || .double
        return self.remoteConfig.configValue(forKey: remoteKey).numberValue as NSObject
    }
}
```

{% endtab %}

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

> Objective-C example

```objectivec
APLFetchRemoteConfiguration(^NSObject* (NSString* remoteKey, APLRemotePropertyType propertyType, NSObject* defaultValue) {
    if (propertyType == APLRemotePropertyTypeString) {
        return [self.remoteConfig configValueForKey: remoteKey].stringValue;
    } else if (propertyType == APLRemotePropertyTypeBool) {
        return [NSNumber numberWithBool: [self.remoteConfig configValueForKey: remoteKey].boolValue];
    } else { // APLRemotePropertyTypeInt || APLRemotePropertyTypeDouble
        return [self.remoteConfig configValueForKey: remoteKey].numberValue;
    }
});
```

{% endtab %}
{% endtabs %}

### Apply default values to Firebase

Apply default values to Firebase using the values you set in the [`reset`](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)reset) method of your configuration subclass. The default values are used by Firebase when the remote configuration has not yet been received.

{% tabs %}
{% tab title="Swift" %}
The [`APLConfiguration`](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html) class has a method `remoteDefaults` which provides a `Dictionary` of all the remote configuration property default values that were set in your overridden [`reset`](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)reset) method.

> Swift example

```swift
FirebaseApp.configure()
self.remoteConfig = RemoteConfig.remoteConfig()
...
// add the following line
self.remoteConfig.setDefaults(APLConfiguration.shared().remoteDefaults())
```

{% endtab %}

{% tab title="Objective-C" %}
The [`APLConfiguration`](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html) class has a method `remoteDefaults` which provides a `NSDictionary*` of all the remote configuration property default values that were set in your overridden [`reset`](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)reset) method.

> Objective-C example

```objectivec
[FIRApp configure];    
self.remoteConfig = [FIRRemoteConfig remoteConfig];
...
// add the following line
[self.remoteConfig setDefaults: [[APLConfiguration sharedConfiguration] remoteDefaults]];
```

{% endtab %}
{% endtabs %}

### Notify Appfigurate Library when Firebase has received remote configuration values

We need to tell Appfigurate Library that Firebase has received remote configuration, so that it can keep your Configuration subclass remote properties in sync.

{% tabs %}
{% tab title="Swift" %}
Add a call to `APLFlushRemoteConfiguration` in any existing Firebase Remote Config `activate(completion:)` blocks (inside `fetch(completionHandler:)` and optionally `addOnConfigUpdateListener(remoteConfigUpdateCompletion:`).

> Swift example

```swift
self.remoteConfig.fetch { status, error in
    if status == .success {
        self.remoteConfig.activate { changed, error in
            ...
            APLFlushRemoteConfiguration() // add this line
        }
    }
}
```

{% endtab %}

{% tab title="Objective-C" %}
Add a call to `APLFlushRemoteConfiguration` in any existing Firebase Remote Config `activateWithCompletion:` blocks (inside `fetchWithCompletionHandler:` and optionally `addOnConfigUpdateListener:`).

> Objective-C example

```objectivec
__weak __typeof__(self) weakSelf = self;
[self.remoteConfig fetchWithCompletionHandler: ^(FIRRemoteConfigFetchStatus status, NSError* error) {
    if (status == FIRRemoteConfigFetchStatusSuccess) {
        __typeof__(self) strongSelf = weakSelf;
        [strongSelf.remoteConfig activateWithCompletion: ^(BOOL changed, NSError* error) {
             ...
             APLFlushRemoteConfiguration(); // add this line
        }];
}];
```

{% endtab %}
{% endtabs %}

### Complete initialisation example

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

> Swift complete initialisation example

```swift
FirebaseApp.configure()
let remoteConfig = RemoteConfig.remoteConfig()

// add the following line
remoteConfig.setDefaults(APLConfiguration.shared().remoteDefaults())
 
// add this block of code
APLFetchRemoteConfiguration { propertyKey, propertyType, _ in
    if propertyType == .string {
        return self.remoteConfig.configValue(forKey: propertyKey).stringValue as NSObject
    } else if propertyType == .bool {
        return self.remoteConfig.configValue(forKey: propertyKey).boolValue as NSObject
    } else { // .int || .double
        return self.remoteConfig.configValue(forKey: propertyKey).numberValue as NSObject
    }
}
                        
remoteConfig.fetch { status, error in
    if status == .success {
        self.remoteConfig.activate { changed, error in
            APLFlushRemoteConfiguration() // add this line
        }
    }
}
remoteConfig.addOnConfigUpdateListener { configUpdate, error in
    if error == nil {
        self.remoteConfig.activate { changed, error in
            APLFlushRemoteConfiguration() // add this line
        }
    }
}
```

{% endtab %}

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

> Objective-C complete initialisation example

```objectivec
[FIRApp configure];
self.remoteConfig = [FIRRemoteConfig remoteConfig];

// add the following line
[self.remoteConfig setDefaults: [[APLConfiguration sharedConfiguration] remoteDefaults]];

// add this block of code
APLFetchRemoteConfiguration(^NSObject* (NSString* propertyKey, APLRemotePropertyType propertyType, NSObject* defaultValue) {
    if (propertyType == APLRemotePropertyTypeString)
        return [self.remoteConfig configValueForKey: propertyKey].stringValue;
    else if (propertyType == APLRemotePropertyTypeBool)
        return [NSNumber numberWithBool: [self.remoteConfig configValueForKey: propertyKey].boolValue];
    else // APLRemotePropertyTypeInt || APLRemotePropertyTypeDouble
        return [self.remoteConfig configValueForKey: propertyKey].numberValue;
});
    
__weak __typeof__(self) weakSelf = self;
[self.remoteConfig fetchWithCompletionHandler: ^(FIRRemoteConfigFetchStatus status, NSError* error) {
    if (status == FIRRemoteConfigFetchStatusSuccess) {
        __typeof__(self) strongSelf = weakSelf;
        [strongSelf.remoteConfig activateWithCompletion: ^(BOOL changed, NSError* error) {
            if (error == nil) {
                APLFlushRemoteConfiguration(); // add this line
            }
        }];
    }
}];
[self.remoteConfig addOnConfigUpdateListener: ^(FIRRemoteConfigUpdate* configUpdate, NSError* error) {
    if (error == nil) {
        __typeof__(self) strongSelf = weakSelf;
        [strongSelf.remoteConfig activateWithCompletion: ^(BOOL changed, NSError* error) {
            if (error == nil) {
                APLFlushRemoteConfiguration(); // add this line
            }
        }];
    }
}];
```

{% endtab %}
{% endtabs %}

### Best practice and usage

{% tabs %}
{% tab title="Swift" %}
Replace all calls to Firebase Remote Config `configValue(forKey:)`:

```swift
if remoteConfig.configValue(forKey: "alwaysDarkMode").boolValue {
   ...
```

with the following:

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

{% endtab %}

{% tab title="Objective-C" %}
Replace all calls to Firebase Remote Config `configValueForKey`:

```objectivec
if ([remoteConfig configValueForKey: @"alwaysDarkMode"].boolValue]) {
   ...
```

with the following:

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

{% endtab %}
{% endtabs %}


# Launch Darkly

Tested with Launch Darkly version 9.12.0 (iOS)

{% 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="/files/8OwmYZhQN9DLfhSjBAWi" 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](/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 %}


# Other third party remote configuration providers

## Check your third party remote configuration provider APIs for compatibility

The third party remote configuration providers' read-property function(s) must return a value synchronously  (cached or default value):

> ✓ Pseudocode examples -  ok

```swift
// ok - returns value immediately
var value = thirdPartyRemoteProvider.getBooleanValue("alwaysDarkMode");

// ok - returns value immediately
var value = thirdPartyRemoteProvider.getBooleanValue("alwaysDarkMode", false);
```

If your third party remote configuration provider's read-property function(s) are marked async, or take a callback/lambda/delegate, then it is incompatible with Appfigurate Library:

> ✗ Pseudocode examples - incompatible third party remote provider

```swift
// not compatible - asynchronous
var value = await thirdPartyRemoteProvider.getBooleanValue("alwaysDarkMode");

// not compatible - callback
thirdPartyRemoteProvider.getBooleanValue("alwaysDarkMode") { value in
    ...
};
```

Also check if your third party remote configuration provider has \`snapshot\` APIs, which should allow synchronous access to a 'snapshot in time' of remote properties (e.g. ConfigCat).

{% hint style="success" %}
Once you've confirmed your third party remote provider has the required synchronous read-property function(s),  you can start integration.&#x20;
{% endhint %}

## Integration of third party remote configuration providers

{% hint style="success" %}
Using Appfigurate remote properties instead of your third party remote configuration provider APIs directly allows for the following:

* locally change third party remote configuration provider properties without affecting your entire customer base.
* compile time type safety - Appfigurate remote properties are typed, third party remote configuration provider properties are not.
* avoids hardcoding duplicated property names throughout your app.
* avoids hardcoding duplicated default values throughout your app.
* deleting a property from your third party remote configuration provider 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 your third party remote provider integrated into your app with the following example remote properties created in the third party remote provider's Console:

| Remote property key | Remote property type |
| ------------------- | -------------------- |
| alwaysDarkMode      | boolean              |
| appTitle            | string               |
| bookingDuration     | integer              |
| fontSize            | double               |

### 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](/configuration-subclasses/supported-property-types#remote-properties).

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

> Swift Configuration example

```swift
import Foundation
import AppfigurateLibrary

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

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

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

    @RemoteDoublePropertyEdit(remoteKey: "fontSize", description: "Size of font throughout app")
    var fontSize: Double
...
    override func reset() {
        alwaysDarkMode = false
        appTitle = "Holiday finder"
        bookingDuration = 30
        fontSize = 13.0
    }
...
}
```

{% 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 configuration values from your third party remote configuration provider.

{% tabs %}
{% tab title="With default value parameter" %}
If your third party remote configuration provider's read-property function(s) have a default value parameter, then Appfigurate Library passes across the correct default value of the remote property to the callback:

> Pseudocode example

```swift
// add the following function call before initializing the third party remote provider.
APLFetchRemoteConfiguration { propertyKey, propertyType, defaultValue in
    if (propertyType == RemotePropertyTypeBool) {
        return this.thirdPartyRemoteProvider.getBooleanValue(propertyKey, defaultValue as Boolean);
    } else if (propertyType == RemotePropertyTypeInt) {
        return this.thirdPartyRemoteProvider.getIntegerValue(propertyKey, defaultValue as Int);
    } else if (propertyType == RemotePropertyTypeDouble {
        return this.thirdPartyRemoteProvider.getDoubleValue(propertyKey, defaultValue as Double);
    } else // RemotePropertyTypeString {
        return this.thirdPartyRemoteProvider.getStringValue(propertyKey, defaultValue as String);
    }
};
...
// initialize the third party remote provider
this.thirdPartyRemoteProvider = ThirdPartyRemoteProvider.initAndConfigure("1fa0b704-8e22-4314");
```

{% endtab %}

{% tab title="Without default value parameter" %}
If your third party remote configuration provider's read-property function(s) doesn't have a default value parameter, then it will likely require default values to be set at the start of your app. (e.g. Firebase does, Launch Darkly doesn't).&#x20;

We recommend you use the default values set in the [`reset`](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)reset) method of your configuration subclass. You can get the default values using the `remoteDefaults` method, which returns a `Dictionary` of property key/value pairs:

> Pseudocode example

```swift
// add the following function call before initializing the third party remote provider.
APLFetchRemoteConfiguration { propertyKey, propertyType, defaultValue in
    if (propertyType == RemotePropertyTypeBool) {
        return this.thirdPartyRemoteProvider.getBooleanValue(propertyKey);
    } else if (propertyType == RemotePropertyTypeInt) {
        return this.thirdPartyRemoteProvider.getIntegerValue(propertyKey);
    } else if (propertyType == RemotePropertyTypeDouble) {
        return this.thirdPartyRemoteProvider.getDoubleValue(propertyKey);
    } else { // RemotePropertyTypeString
        return this.thirdPartyRemoteProvider.getStringValue(propertyKey);
    }
};
...
// initialize third party remote provider
this.thirdPartyRemoteProvider = ThirdPartyRemoteProvider.initAndConfigure("1fa0b704-8e22-4314");
...
// initialize third party remote provider default values
var defaults = APLConfiguration.shared().remoteDefaults();
this.thirdPartyRemoteProvider.setDefaultBooleanValue(forKey: "alwaysDarkMode", value: defaults["alwaysDarkMode"]);
this.thirdPartyRemoteProvider.setDefaultIntegerValue(forKey: "bookingDuration", value: defaults["bookingDuration"]);
...
```

{% endtab %}
{% endtabs %}

## Notify Appfigurate Library when the third party remote configuration provider has received remote config

We need to tell Appfigurate Library that third party remote configuration provider has received remote configuration, so that it can keep your Configuration subclass remote properties in sync.

> Pseudocode example

```swift
remoteProvider.setOnFetchCallback { status, error in
    if (status == .receivedConfiguration) {
        ...
        APLFlushRemoteConfiguration(); // add this line
    }
}
```

## Best practice and usage

Replace all calls to your third party remote configuration providers read-property function(s):

> Pseudocode example

```swift
if (remoteProvider.getBooleanValue("alwaysDarkMode", defaultValue: false) {
    ...
```

with the following:

> Pseudocode example

```swift
if (APLConfiguration.shared().alwaysDarkMode) {
    ...
```


# Supported property types

## Local properties

Appfigurate supports the following configurable local property types:

<table><thead><tr><th width="140">Type</th><th>Swift</th><th>Obj-C</th><th>Dart</th><th>JavaScript</th></tr></thead><tbody><tr><td><strong>Boolean</strong></td><td><a href="/pages/7GnZhj8TgD53h3ZBfF9E">Bool</a></td><td><a href="/pages/7GnZhj8TgD53h3ZBfF9E">BOOL</a></td><td><a href="/pages/7GnZhj8TgD53h3ZBfF9E">bool</a></td><td><a href="/pages/7GnZhj8TgD53h3ZBfF9E">Boolean</a></td></tr><tr><td><strong>Integer</strong></td><td><a href="/pages/tfNIqH7i8l5LKq93bW7Y">Int</a></td><td><a href="/pages/tfNIqH7i8l5LKq93bW7Y">NSInteger</a></td><td><a href="/pages/tfNIqH7i8l5LKq93bW7Y">int</a></td><td><a href="/pages/P5LhAkSNkF8WozPNwnJC">Number</a></td></tr><tr><td><strong>Float</strong></td><td><a href="/pages/rIen158dA9hHW8bGTOG3">Float</a></td><td><a href="/pages/rIen158dA9hHW8bGTOG3">float</a></td><td><a href="/pages/P5LhAkSNkF8WozPNwnJC">double</a></td><td><a href="/pages/P5LhAkSNkF8WozPNwnJC">Number</a></td></tr><tr><td><strong>Double</strong></td><td><a href="/pages/P5LhAkSNkF8WozPNwnJC">Double</a></td><td><a href="/pages/P5LhAkSNkF8WozPNwnJC">double</a></td><td><a href="/pages/P5LhAkSNkF8WozPNwnJC">double</a></td><td><a href="/pages/P5LhAkSNkF8WozPNwnJC">Number</a></td></tr><tr><td><strong>Plain Strings</strong></td><td><a href="/pages/souSbgi4RmW3ieLP4o2V">String</a></td><td><a href="/pages/souSbgi4RmW3ieLP4o2V">NSString</a></td><td><a href="/pages/souSbgi4RmW3ieLP4o2V">String</a></td><td><a href="/pages/souSbgi4RmW3ieLP4o2V">String</a></td></tr><tr><td><strong>Encrypted Strings</strong></td><td><a href="/pages/uzTxgZJdLek8vQajDnrW">String</a></td><td><a href="/pages/uzTxgZJdLek8vQajDnrW">NSString</a></td><td><a href="/pages/uzTxgZJdLek8vQajDnrW">String</a></td><td><a href="/pages/uzTxgZJdLek8vQajDnrW">String</a></td></tr></tbody></table>

## Remote properties

Appfigurate supports the following configurable remote property types when integrated with a [third party remote configuration provider](/getting-started/third-party-remote-configuration-providers):

<table><thead><tr><th width="139">Type</th><th>Swift</th><th>Obj-C</th><th>Dart</th><th>JavaScript</th></tr></thead><tbody><tr><td><strong>Boolean</strong></td><td><a href="/pages/7GnZhj8TgD53h3ZBfF9E#swift-2">Bool</a></td><td><a href="/pages/7GnZhj8TgD53h3ZBfF9E#objective-c-2">BOOL</a></td><td><a href="/pages/7GnZhj8TgD53h3ZBfF9E#dart-2">bool</a></td><td><a href="/pages/7GnZhj8TgD53h3ZBfF9E#javascript-2">Boolean</a></td></tr><tr><td><strong>Integer</strong></td><td><a href="/pages/tfNIqH7i8l5LKq93bW7Y#swift-5">Int</a></td><td><a href="/pages/tfNIqH7i8l5LKq93bW7Y#objective-c-5">NSInteger</a></td><td><a href="/pages/tfNIqH7i8l5LKq93bW7Y#dart-5">int</a></td><td><a href="/pages/tfNIqH7i8l5LKq93bW7Y#javascript-5">Number</a></td></tr><tr><td><strong>Double</strong></td><td><a href="/pages/P5LhAkSNkF8WozPNwnJC#swift-5">Double</a></td><td><a href="/pages/P5LhAkSNkF8WozPNwnJC#objective-c-5">double</a></td><td><a href="/pages/P5LhAkSNkF8WozPNwnJC#dart-5">double</a></td><td><a href="/pages/P5LhAkSNkF8WozPNwnJC#javascript-5">Number</a></td></tr><tr><td><strong>Plain String</strong></td><td><a href="/pages/souSbgi4RmW3ieLP4o2V#swift-4">String</a></td><td><a href="/pages/souSbgi4RmW3ieLP4o2V#objective-c-4">NSString</a></td><td><a href="/pages/souSbgi4RmW3ieLP4o2V#dart-4">String</a></td><td><a href="/pages/souSbgi4RmW3ieLP4o2V#javascript-4">String</a></td></tr></tbody></table>


# Boolean

{% tabs %}
{% tab title="Swift" %}
`Bool` properties must be either `true` or `false`. The default value of a `Bool` property is `false`. You can change the default value of the property by assigning a new value in an overridden [<mark style="color:blue;">`reset`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)reset) method.
{% endtab %}

{% tab title="Objective-C" %}
`BOOL` properties must be either `YES` or `NO`. The default value of a `BOOL` property is `NO`. You can change the default value of the property by assigning a new value in an overridden [<mark style="color:blue;">`reset`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)reset) method.

`BOOL` properties should be declared in your [<mark style="color:blue;">`APLConfiguration`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html) subclass header file as follows:

```objectivec
@import AppfigurateLibrary;

@interface Configuration : APLConfiguration

@property(nonatomic, assign) BOOL propertyName;
...
```

{% endtab %}

{% tab title="Dart" %}
The flutter [<mark style="color:blue;">`APLNativeConfiguration`</mark>](https://pub.dev/documentation/appfigurateflutter/latest/appfigurateflutter/APLNativeConfiguration-class.html) class defers to the underlying platform [<mark style="color:blue;">`APLConfiguration`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html) (iOS) or [<mark style="color:blue;">`nz.co.electricbolt.appfiguratelibrary.Configuration`</mark>](https://www.electricbolt.co.nz/api/android/nz/co/electricbolt/appfiguratelibrary/Configuration.html) (Android) subclass to read property values.
{% endtab %}

{% tab title="JavaScript" %}
The React Native module `AppfigurateModule` class defers to the underlying platform [<mark style="color:blue;">`APLConfiguration`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html) (iOS) or [<mark style="color:blue;">`nz.co.electricbolt.appfiguratelibrary.Configuration`</mark>](https://www.electricbolt.co.nz/api/android/nz/co/electricbolt/appfiguratelibrary/Configuration.html) (Android) subclass to read property values.
{% endtab %}
{% endtabs %}

## **Local properties**

### **Switch UI**

Allows a boolean property to be changed in Appfigurate using a switch.&#x20;

<img src="/files/g98YO15rpMwjTNH70LkG" alt="" data-size="original">

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

> Swift prototype

```swift
@BoolProperty(description, restart)
var propertyName: Bool
```

If `restart` is `true`, then the app will be restarted if the property value changes.

> Swift example

```swift
import AppfigurateLibrary

@objcMembers class Configuration: APLConfiguration {
    
    @BoolProperty(description: "Enable debug logging to console", restart: false)
    var logging: Bool
    
    override func reset() {
        logging = true
    }
    ...
```

{% endtab %}

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

> Objective-C prototype

```objectivec
BOOL_PROPERTY(propertyName, description, restart)
```

If `restart` is `YES`, then the app will be restarted if the property value changes.

> Objective-C example

```objectivec
@import AppfigurateLibrary;

@interface Configuration : APLConfiguration

@property(nonatomic, assign) BOOL logging;

@end

@implementation Configuration

BOOL_PROPERTY(logging, @"Enable debug logging to console", NO);

- (void) reset {
    self.logging = YES;
}
...
```

{% endtab %}

{% tab title="Dart" %}

> Dart example

```dart
import 'package:appfigurateflutter/appfigurateflutter.dart';

public class Configuration extends APLNativeConfiguration {

    bool get logging => nativeBool('logging');
    ...
```

{% endtab %}

{% tab title="JavaScript" %}

> JavaScript example

```javascript
import {
  NativeModules,
} from 'react-native';

const {Appfigurate} = NativeModules;

...

let logging = await Appfigurate.nativeValue("logging"); // true or false
```

{% endtab %}
{% endtabs %}

## Remote properties

### Remote Switch UI

Allows a third party remote configuration provider's boolean property to be changed locally in Appfigurate using a switch. If the override tick box is ticked off, the third party remote configuration provider's value is displayed. If the override tick box is ticked on, then you can use Appfigurate to locally override the value.

![](/files/L5YLhZzYZluPdUSNMQJF)

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

> Swift prototype

```swift
@RemoteBoolProperty(remoteKey, description)
var propertyName: Bool
```

> Swift example

```swift
import AppfigurateLibrary

@objcMembers class Configuration: APLConfiguration {
    
    @RemoteBoolProperty(remoteKey: "alwaysDarkMode", description: "Force dark mode to be always set")
    var alwaysDarkMode: Bool
    
    override func reset() {
        alwaysDarkMode = false
    }
    ...
```

{% endtab %}

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

> Objective-C prototype

```objectivec
BOOL_PROPERTY(propertyName, remoteKey, description)
```

> Objective-C example

```objectivec
@import AppfigurateLibrary;

@interface Configuration : APLConfiguration

@property(nonatomic, assign) BOOL alwaysDarkMode;

@end

@implementation Configuration

BOOL_PROPERTY(alwaysDarkMode, @"alwaysDarkMode", @"Force dark mode to be always set");

- (void) reset {
    self.alwaysDarkMode = NO;
}
...
```

{% endtab %}

{% tab title="Dart" %}

> Dart example

```dart
import 'package:appfigurateflutter/appfigurateflutter.dart';

public class Configuration extends APLNativeConfiguration {

    bool get alwaysDarkMode => nativeBool('alwaysDarkMode');
    ...
```

{% endtab %}

{% tab title="JavaScript" %}

> JavaScript example

```javascript
import {
  NativeModules,
} from 'react-native';

const {Appfigurate} = NativeModules;

...

let alwaysDarkMode = await Appfigurate.nativeValue("alwaysDarkMode"); // true or false
```

{% endtab %}
{% endtabs %}


# Integer

{% tabs %}
{% tab title="Swift" %}
`Int` is platform dependent, 32-bit signed values on 32-bit CPUs (Apple Watch; arm64\_32) and 64-bit signed values on 64-bit CPUs (iPhone, iPad; arm64).

The default value of a `Int` is 0. You can change the default value of the property by assigning a new value in an overridden [<mark style="color:blue;">`reset`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)reset) method.
{% endtab %}

{% tab title="Objective-C" %}
`NSInteger` is platform dependent, 32-bit signed values on 32-bit CPUs (Apple Watch arm64\_32) and 64-bit signed values on 64-bit CPUs (iPhone, iPad arm64).

The default value of a `NSInteger` is 0. You can change the default value of the property by assigning a new value in an overridden [<mark style="color:blue;">`reset`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)reset) method.

`NSInteger` properties should be declared in your [<mark style="color:blue;">`APLConfiguration`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html) subclass header file as follows:

```objectivec
@import AppfigurateLibrary;

@interface Configuration : APLConfiguration

@property(nonatomic, assign) NSInteger propertyName;
...
```

{% endtab %}

{% tab title="Dart" %}
The flutter [<mark style="color:blue;">`APLNativeConfiguration`</mark>](https://pub.dev/documentation/appfigurateflutter/latest/appfigurateflutter/APLNativeConfiguration-class.html) class defers to the underlying platform [<mark style="color:blue;">`APLConfiguration`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html) (iOS) or [<mark style="color:blue;">`nz.co.electricbolt.appfiguratelibrary.Configuration`</mark>](https://www.electricbolt.co.nz/api/android/nz/co/electricbolt/appfiguratelibrary/Configuration.html) (Android) subclass to read property values.
{% endtab %}

{% tab title="JavaScript" %}
The React Native module `AppfigurateModule` class defers to the underlying platform [<mark style="color:blue;">`APLConfiguration`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html) (iOS) or [<mark style="color:blue;">`nz.co.electricbolt.appfiguratelibrary.Configuration`</mark>](https://www.electricbolt.co.nz/api/android/nz/co/electricbolt/appfiguratelibrary/Configuration.html) (Android) subclass to read property values.

React Native always converts the underlying Integer to a JavaScript Number - you should be aware of [JavaScript's 53 bit limitation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER).
{% endtab %}
{% endtabs %}

## **Local properties**

### **Slider UI**

Allows an integer property to be changed in Appfigurate using a slider between minimum and maximum values.&#x20;

![](/files/5xWoGG1ETfOesjTV9YtL)

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

> Swift prototype

```swift
@IntPropertySlider(min, max, icon, description, restart)
var propertyName: Int
```

You can customize the minimum and maximum images using the [<mark style="color:blue;">`icon`</mark>](https://www.electricbolt.co.nz/api/Enums/APLIconSlider.html) parameter. If `restart` is `true`, then the app will be restarted if the property value changes.

> Swift example

```swift
import AppfigurateLibrary

@objcMembers class Configuration: APLConfiguration {

    @IntPropertySlider(min: 50, max: 100, icon: .volume, description: "Limit volume level", restart: true)
    var volumeRange: Int
    
    override func reset() {
        volumeRange = 75
    }
    ...
```

{% endtab %}

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

> Objective-C prototype

```objectivec
INT_PROPERTY_SLIDER(propertyName, minValue, maxValue, icon, description, restart)
```

You can customize the minimum and maximum images using the [<mark style="color:blue;">`icon`</mark>](/configuration-subclasses/slider-icon-types) parameter. If `restart` is `YES`, then the app will be restarted if the property value changes.

> Objective-C example

```objectivec
@import AppfigurateLibrary;

@interface Configuration : APLConfiguration

@property(nonatomic, assign) NSInteger volumeRange;

@end

@implementation Configuration

INT_PROPERTY_SLIDER(volumeRange, 50, 100, APLIconSliderVolume, @"Limit volume level", NO);

- (void) reset {
    self.volumeRange = 75;
}
...
```

{% endtab %}

{% tab title="Dart" %}

> Dart prototype

```dart
int get propertyName => nativeInt('propertyName');
```

> Dart example

```dart
import 'package:appfigurateflutter/appfigurateflutter.dart';

public class Configuration extends APLNativeConfiguration {

    int get volumeRange => nativeInt('volumeRange');
    ...
```

{% endtab %}

{% tab title="JavaScript" %}

> JavaScript example

```javascript
import {
  NativeModules,
} from 'react-native';

const {Appfigurate} = NativeModules;

...

let volumeRange = await Appfigurate.nativeValue("volumeRange"); // Number
```

{% endtab %}
{% endtabs %}

### **Editable UI**

Allows an integer property to be changed in Appfigurate using a text field between minimum and maximum values, and an optional regular expression validating input.

![](/files/gaqzTIwKZfUfRoFwWxxh)

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

> Swift prototype

```swift
@IntPropertyEdit(min, max, regex, description, restart)
var propertyName: Int
```

If `restart` is `true`, then the app will be restarted if the property value changes.

> Swift example

```swift
import AppfigurateLibrary

@objcMembers class Configuration: APLConfiguration {

    @IntPropertyEdit(min: 1, max: 5, regex: "", description: "Maximum number of game levels", restart: false)
    var gameLevels: Int
    
    override func reset() {
        gameLevels = 3
    }
    ...
```

{% endtab %}

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

> Objective-C prototype

```objectivec
INT_PROPERTY_EDIT(propertyName, minValue, maxValue, regex, description, restart)
```

If `restart` is `YES`, then the app will be restarted if the property value changes.

> Objective-C example

```objectivec
@import AppfigurateLibrary;

@interface Configuration : APLConfiguration

@property(nonatomic, assign) NSInteger gameLevels;

@end

@implementation Configuration

INT_PROPERTY_EDIT(gameLevels, 1, 5, @"", @"Maximum number of game levels", NO);

- (void) reset {
    self.gameLevels = 3;
}
...
```

{% endtab %}

{% tab title="Dart" %}

> Dart prototype

```dart
int get propertyName => nativeInt('propertyName');
```

> Dart example

```dart
Dimport 'package:appfigurateflutter/appfigurateflutter.dart';

public class Configuration extends APLNativeConfiguration {

    int get gameLevels => nativeInt('gameLevels');
    ...
```

{% endtab %}

{% tab title="JavaScript" %}

> JavaScript example

```javascript
import {
  NativeModules,
} from 'react-native';

const {Appfigurate} = NativeModules;

...

let gameLevels = await Appfigurate.nativeValue("gameLevels"); // Number
```

{% endtab %}
{% endtabs %}

### **List UI**

Allows an integer property to be changed in Appfigurate by allowing the user to select from a predefined list of valid choices.&#x20;

![](/files/HgkhTozmoXKlkVdkDADm)

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

> Swift prototype

<pre class="language-swift"><code class="lang-swift"><strong>@IntPropertyList(description, restart, values)
</strong>var propertyName: Int
</code></pre>

If `restart` is `true`, then the app will be restarted if the property value changes.

> Swift example

```swift
import AppfigurateLibrary

@objcMembers class Configuration: APLConfiguration {

    @IntPropertyList(description: "Quality rating", restart: false, values: ["Low": 10, "Average": 50, "Excellent": 95])
    var rating: Int
    
    override func reset() {
        rating = 10
    }
    ...
```

{% endtab %}

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

> Objective-C prototype

```objectivec
INT_PROPERTY_LIST(propertyName, description, restart, ...)
```

If `restart` is `YES`, then the app will be restarted if the property value changes.

> Objective-C example

```objectivec
@import AppfigurateLibrary;

@interface Configuration : APLConfiguration

@property(nonatomic, assign) NSInteger rating;

@end

@implementation Configuration

INT_PROPERTY_LIST(rating, @"Quality rating", NO, @{@"Low": @10, @"Average": @50, @"Excellent": @95});

- (void) reset {
    self.rating = 10;
}
...
```

{% endtab %}

{% tab title="Dart" %}

> Dart prototype

```dart
int get propertyName => nativeInt('propertyName');
```

> Dart example

```dart
import 'package:appfigurateflutter/appfigurateflutter.dart';

public class Configuration extends APLNativeConfiguration {

    int get rating => nativeInt('rating');
    ...
```

{% endtab %}

{% tab title="JavaScript" %}

> JavaScript example

```javascript
import {
  NativeModules,
} from 'react-native';

const {Appfigurate} = NativeModules;

...

let rating = await Appfigurate.nativeValue("rating"); // Number
```

{% endtab %}
{% endtabs %}

### **Editable List UI**

Allows an integer property to be changed in Appfigurate by allowing the user to select from a predefined list of valid choices. The user can customize the list adding by additional values using a text field between minimum and maximum values, and an optional regular expression validating input.&#x20;

![](/files/fnjfM5dlEfjbGPXjgJsA)

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

> Swift prototype

```swift
@IntPropertyListEdit(min, max, regex, description, restart, values)
var propertyName: Int
```

If `restart` is `true`, then the app will be restarted if the property value changes.

> Swift example

```swift
import AppfigurateLibrary

@objcMembers class Configuration: APLConfiguration {

    @IntPropertyListEdit(min: 0, max: 365, regex: "^(0?[0-9]?[0-9]|[1-2][0-9][0-9]|3[0-5][0-9]|36[0-5])$", description: "Duration in days emails are available", restart: false, values: ["7 days": 7, "1 month": 30, "1 Year": 365])
    var availablityDuration: Int
    
    override func reset() {
        volumeRange = 7
    }
    ...
```

{% endtab %}

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

> Objective-C prototype

```objectivec
INT_PROPERTY_LIST_EDIT(propertyName, minValue, maxValue, regex, description, restart, ...)
```

If `restart` is `YES`, then the app will be restarted if the property value changes.

> Objective-C example

```objectivec
@import AppfigurateLibrary;

@interface Configuration : APLConfiguration

@property(nonatomic, assign) NSInteger availabilityDuration;

@end

@implementation Configuration

INT_PROPERTY_LIST_EDIT(availablityDuration, 0, 365, @"^(0?[0-9]?[0-9]|[1-2][0-9][0-9]|3[0-5][0-9]|36[0-5])$", @"Duration in days emails are available", NO, @{@"7 days": @7, @"1 month": @30, @"1 Year": @365});

- (void) reset {
    self.volumeRange = 7;
}
...
```

{% endtab %}

{% tab title="Dart" %}

> Dart prototype

```dart
int get propertyName => nativeInt('propertyName');
```

> Dart example

```dart
import 'package:appfigurateflutter/appfigurateflutter.dart';

public class Configuration extends APLNativeConfiguration {

    int get availabilityDuration => nativeInt('availabilityDuration');
    ...
```

{% endtab %}

{% tab title="JavaScript" %}

> JavaScript example

```javascript
import {
  NativeModules,
} from 'react-native';

const {Appfigurate} = NativeModules;

...

let availabilityDuration = await Appfigurate.nativeValue("availabilityDuration"); // Number
```

{% endtab %}
{% endtabs %}

## Remote properties

### Remote Editable UI

Allows a third party remote configuration provider's integer property to be changed locally in Appfigurate using a text field. If the override tick box is ticked off, the third party remote configuration provider's value is displayed. If the override tick box is ticked on, then you can use Appfigurate to locally override the value.\
\
&#x20;![](/files/FnfSppyDUYOAEuzFK88m)

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

> Swift prototype

```swift
@RemoteIntPropertyEdit(remoteKey, description)
var propertyName: Int
```

> Swift example

```swift
import AppfigurateLibrary

@objcMembers class Configuration: APLConfiguration {
    
    @RemoteIntPropertyEdit(remoteKey: "bookingDuration", description: "Duration (days) for reservation bookings")
    var bookingDuration: Int
    
    override func reset() {
        bookingDuration = 180
    }
    ...
```

{% endtab %}

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

> Objective-C prototype

<pre class="language-objectivec"><code class="lang-objectivec"><strong>REMOTE_INT_PROPERTY_EDIT(propertyName, remoteKey, description)
</strong></code></pre>

> Objective-C example

```objectivec
@import AppfigurateLibrary;

@interface Configuration : APLConfiguration

@property(nonatomic, assign) NSInteger bookingDuration;

@end

@implementation Configuration

REMOTE_INT_PROPERTY_EDIT(bookingDuration, @"bookingDuration", @"Duration (days) for reservation bookings");

- (void) reset {
    self.bookingDuration = 180;
}
...
```

{% endtab %}

{% tab title="Dart" %}

> Dart prototype

```dart
int get propertyName => nativeInt('propertyName');
```

> Dart example

```dart
Dimport 'package:appfigurateflutter/appfigurateflutter.dart';

public class Configuration extends APLNativeConfiguration {

    int get bookingDuration => nativeInt('bookingDuration');
    ...
```

{% endtab %}

{% tab title="JavaScript" %}

> JavaScript example

```javascript
import {
  NativeModules,
} from 'react-native';

const {Appfigurate} = NativeModules;

...

let bookingDuration = await Appfigurate.nativeValue("bookingDuration"); // Number
```

{% endtab %}
{% endtabs %}


# Float

{% tabs %}
{% tab title="Swift" %}
`Float` is a single-precision 4 byte floating-point type with an approximate range of 1.2E-38 to 3.4E+38.

The default value of a `Float` is 0.0. You can change the default value of the property by assigning a new value in an overridden [<mark style="color:blue;">`reset`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)reset) method.
{% endtab %}

{% tab title="Objective-C" %}
`float` is a single-precision 4 byte floating-point type with an approximate range of 1.2E-38 to 3.4E+38.

The default value of a `float` is 0.0. You can change the default value of the property by assigning a new value in an overridden [<mark style="color:blue;">`reset`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)reset) method.

`float` properties should be declared in your [<mark style="color:blue;">`APLConfiguration`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html) subclass header file as follows:

```objectivec
@import AppfigurateLibrary;

@interface Configuration : APLConfiguration

@property(nonatomic, assign) float propertyName;
...
```

{% endtab %}

{% tab title="Dart" %}
The flutter [<mark style="color:blue;">`APLNativeConfiguration`</mark>](https://pub.dev/documentation/appfigurateflutter/latest/appfigurateflutter/APLNativeConfiguration-class.html) class defers to the underlying platform [<mark style="color:blue;">`APLConfiguration`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html) (iOS) or [<mark style="color:blue;">`nz.co.electricbolt.appfiguratelibrary.Configuration`</mark>](https://www.electricbolt.co.nz/api/android/nz/co/electricbolt/appfiguratelibrary/Configuration.html) (Android) subclass to read property values.

As Dart doesn't have a single-precision `float` data type, you should map to a double-precision `double` data type instead:

| Dart property type | Swift property type | Objective-C property type | Kotlin property type | Java property type |
| ------------------ | ------------------- | ------------------------- | -------------------- | ------------------ |
| **double**         | Float               | float                     | Float                | float              |
| {% endtab %}       |                     |                           |                      |                    |

{% tab title="JavaScript" %}
The React Native module `AppfigurateModule` class defers to the underlying platform [<mark style="color:blue;">`APLConfiguration`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html) (iOS) or [<mark style="color:blue;">`nz.co.electricbolt.appfiguratelibrary.Configuration`</mark>](https://www.electricbolt.co.nz/api/android/nz/co/electricbolt/appfiguratelibrary/Configuration.html) (Android) subclass to read property values.

React Native always converts the underlying Float to a JavaScript Number - you should be aware of [JavaScript's 53 bit limitation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER).
{% endtab %}
{% endtabs %}

## **Local properties**

### **Slider UI**

Allows a float property to be changed in Appfigurate using a slider between minimum and maximum values.&#x20;

![](/files/ww0B3ztrp4xfzgJ8bo82)

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

> Swift prototype

```swift
@FloatPropertySlider(min, max, icon, description, restart)
var propertyName: Float
```

You can customize the minimum and maximum images using the [<mark style="color:blue;">`icon`</mark>](https://www.electricbolt.co.nz/api/Enums/APLIconSlider.html) parameter. If `restart` is `true`, then the app will be restarted if the property value changes.

> Swift example

```swift
import AppfigurateLibrary

@objcMembers class Configuration: APLConfiguration {

    @FloatPropertySlider(min: 60.5, max: 120.41, icon: .volume, description: "Clipping - decibels", restart: false)
    var maxDecibel: Float
    
    override func reset() {
        maxDecibel = 100.0
    }
    ...
```

{% endtab %}

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

> Objective-C Prototype

<pre class="language-objectivec"><code class="lang-objectivec"><strong>FLOAT_PROPERTY_SLIDER(propertyName, minValue, maxValue, icon, description, restart)
</strong></code></pre>

You can customize the minimum and maximum images using the [<mark style="color:blue;">`icon`</mark>](/configuration-subclasses/slider-icon-types) parameter. If `restart` is `YES`, then the app will be restarted if the property value changes.

> Objective-C example

```objectivec
@import AppfigurateLibrary;

@interface Configuration : APLConfiguration

@property(nonatomic, assign) float maxDecibel;

@end

@implementation Configuration

FLOAT_PROPERTY_SLIDER(maxDecibel, 60.5, 120.41, APLIconSliderVolume, @"Clipping - decibels", NO);

- (void) reset {
    self.maxDecibel = 100.0;
}
...
```

{% endtab %}

{% tab title="Dart" %}

> Dart prototype

```dart
double get propertyName => nativeDouble('propertyName');
```

> Dart example

```dart
import 'package:appfigurateflutter/appfigurateflutter.dart';

public class Configuration extends APLNativeConfiguration {

    double get maxDecibel => nativeDouble('maxDecibel');
    ...
```

{% endtab %}

{% tab title="JavaScript" %}

> JavaScript example

```javascript
import {
  NativeModules,
} from 'react-native';

const {Appfigurate} = NativeModules;

...

let maxDecibel = await Appfigurate.nativeValue("maxDecibel"); // Number
```

{% endtab %}
{% endtabs %}

### **Editable UI**

Allows a float property to be changed in Appfigurate using a text field between minimum and maximum values, and an optional regular expression validating input.

![](/files/X0VBu8lpOYXJVay5myUr)

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

> Swift prototype

```swift
@FloatPropertyEdit(min, max, regex, description, restart)
var propertyName: Float
```

If `restart` is `true`, then the app will be restarted if the property value changes.

> Swift example

```swift
import AppfigurateLibrary

@objcMembers class Configuration: APLConfiguration {

    @FloatPropertyEdit(min: -2.1, max: 4.1, regex: "", description: "Shot accuracy", restart: false)
    var shotAccuracy: Float
    
    override func reset() {
        shotAccuracy = 3.241
    }
    ...
```

{% endtab %}

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

> Objective-C prototype

```objectivec
FLOAT_PROPERTY_EDIT(propertyName, minValue, maxValue, regex, description, restart)
```

If `restart` is `YES`, then the app will be restarted if the property value changes.

> Objective-C example

```objectivec
@import AppfigurateLibrary;

@interface Configuration : APLConfiguration

@property(nonatomic, assign) float shotAccuracy;

@end

@implementation Configuration

FLOAT_PROPERTY_EDIT(shotAccuracy, -2.1, 4.1, @"", @"Shot accuracy", NO);

- (void) reset {
    self.shotAccuracy = 3.241;
}
...
```

{% endtab %}

{% tab title="Dart" %}

> Dart prototype

```dart
double get propertyName => nativeDouble('propertyName');
```

> Dart example

```dart
import 'package:appfigurateflutter/appfigurateflutter.dart';

public class Configuration extends APLNativeConfiguration {

    double get shotAccuracy => nativeDouble('shotAccuracy');
    ...
```

{% endtab %}

{% tab title="JavaScript" %}

> JavaScript example

```javascript
import {
  NativeModules,
} from 'react-native';

const {Appfigurate} = NativeModules;

...

let shotAccuracy = await Appfigurate.nativeValue("shotAccuracy"); // Number
```

{% endtab %}
{% endtabs %}

### **List UI**

Allows a float property to be changed in Appfigurate by allowing the user to select from a predefined list of valid choices.&#x20;

![](/files/hpnFYq3Tpvnz9KLb7czM)

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

> Swift prototype

```swift
@FloatPropertyList(description, restart, values)
var propertyName: Float
```

If `restart` is `true`, then the app will be restarted if the property value changes.

> Swift example

```swift
import AppfigurateLibrary

@objcMembers class Configuration: APLConfiguration {

    @FloatPropertyList(description: "Quality rating", restart: true, values: ["Low": 10.0, "Average": 50.0, "Excellent": 95.0])
    var rating: Float
    
    override func reset() {
        rating = 10.0
    }
    ...
```

{% endtab %}

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

> Objective-C prototype

```objectivec
FLOAT_PROPERTY_LIST(propertyName, description, restart, ...)
```

If `restart` is `YES`, then the app will be restarted if the property value changes.

> Objective-C example

```objectivec
@import AppfigurateLibrary;

@interface Configuration : APLConfiguration

@property(nonatomic, assign) float rating;

@end

@implementation Configuration

FLOAT_PROPERTY_LIST(rating, @"Quality rating", NO, @{@"Low": @10.0, @"Average": @50.0, @"Excellent": @95.0});

- (void) reset {
    self.rating = 10.0;
}
...
```

{% endtab %}

{% tab title="Dart" %}

> Dart prototype

```dart
double get propertyName => nativeDouble('propertyName');
```

> Dart example

```dart
import 'package:appfigurateflutter/appfigurateflutter.dart';

public class Configuration extends APLNativeConfiguration {

    double get rating => nativeDouble('rating');
    ...
```

{% endtab %}

{% tab title="JavaScript" %}

> JavaScript example

```javascript
import {
  NativeModules,
} from 'react-native';

const {Appfigurate} = NativeModules;

...

let rating = await Appfigurate.nativeValue("rating"); // Number
```

{% endtab %}
{% endtabs %}

### **Editable List UI**

Allows a float property to be changed in Appfigurate by allowing the user to select from a predefined list of valid choices. The user can customize the list adding by additional values using a text field between minimum and maximum values, and an optional regular expression validating input.&#x20;

![](/files/OHK5pQDpAYcYRaDdTKQC)

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

> Swift prototype

```swift
@FloatPropertyListEdit(min, max, regex, description, restart, values)
var propertyName: Float
```

If `restart` is `true`, then the app will be restarted if the property value changes.

> Swift example

```swift
import AppfigurateLibrary

@objcMembers class Configuration: APLConfiguration {

    @FloatPropertyListEdit(min: 0.0, max: 366.0, regex: #"^(0?[0-9]?[0-9]|[1-2][0-9][0-9]|3[0-5][0-9]|36[0-5])?(?:\.\d+)?$"#, description: "Forward server time (days)", restart: false, values: ["7 days": 7.0, "1 month": 30.0, "1 Year": 365.0])
    var forwardServerTime: Float
    
    override func reset() {
        forwardServerTime = 7.0
    }
    ...
```

{% endtab %}

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

> Objective-C prototype

```objectivec
FLOAT_PROPERTY_LIST_EDIT(propertyName, minValue, maxValue, regex, description, restart, ...)
```

If `restart` is `YES`, then the app will be restarted if the property value changes.

> Objective-C example

```objectivec
@import AppfigurateLibrary;

@interface Configuration : APLConfiguration

@property(nonatomic, assign) float forwardServerTime;

@end

@implementation Configuration

FLOAT_PROPERTY_LIST_EDIT(forwardServerTime, 0.0, 366.0, @"^(0?[0-9]?[0-9]|[1-2][0-9][0-9]|3[0-5][0-9]|36[0-5])?(?:\\.\\d+)?$", @"Forward server time (days)", NO, @{@"7 days": @7.0, @"1 month": @30.0, @"1 Year": @365.0});

- (void) reset {
    self.forwardServerTime = 7.0;
}
...
```

{% endtab %}

{% tab title="Dart" %}

> Dart prototype

```dart
double get propertyName => nativeDouble('propertyName');
```

> Dart example

```dart
import 'package:appfigurateflutter/appfigurateflutter.dart';

public class Configuration extends APLNativeConfiguration {

    double get forwardServerTime => nativeDouble('forwardServerTime');
    ...
```

{% endtab %}

{% tab title="JavaScript" %}

> JavaScript example

```javascript
import {
  NativeModules,
} from 'react-native';

const {Appfigurate} = NativeModules;

...

let forwardServerTime = await Appfigurate.nativeValue("forwardServerTime"); // Number
```

{% endtab %}
{% endtabs %}


# Double

{% tabs %}
{% tab title="Swift" %}
`Double` is a double-precision 8 byte floating-point type with an approximate range of 2.3E-308 to 1.7E+308.

The default value of a `Double` is 0.0. You can change the default value of the property by assigning a new value in an overridden [<mark style="color:blue;">`reset`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)reset) method.
{% endtab %}

{% tab title="Objective-C" %}
`double` is a double-precision 8 byte floating-point type with an approximate range of 2.3E-308 to 1.7E+308.

The default value of a `double` is 0.0. You can change the default value of the property by assigning a new value in an overridden [<mark style="color:blue;">`reset`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)reset) method.

`double` properties should be declared in your [<mark style="color:blue;">`APLConfiguration`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html) subclass header file as follows:

```objectivec
@import AppfigurateLibrary;

@interface Configuration : APLConfiguration

@property(nonatomic, assign) double propertyName;
...
```

{% endtab %}

{% tab title="Dart" %}
The flutter [<mark style="color:blue;">`APLNativeConfiguration`</mark>](https://pub.dev/documentation/appfigurateflutter/latest/appfigurateflutter/APLNativeConfiguration-class.html) class defers to the underlying platform [<mark style="color:blue;">`APLConfiguration`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html) (iOS) or [<mark style="color:blue;">`nz.co.electricbolt.appfiguratelibrary.Configuration`</mark>](https://www.electricbolt.co.nz/api/android/nz/co/electricbolt/appfiguratelibrary/Configuration.html) (Android) subclass to read property values.
{% endtab %}

{% tab title="JavaScript" %}
The React Native module `AppfigurateModule` class defers to the underlying platform [<mark style="color:blue;">`APLConfiguration`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html) (iOS) or [<mark style="color:blue;">`nz.co.electricbolt.appfiguratelibrary.Configuration`</mark>](https://www.electricbolt.co.nz/api/android/nz/co/electricbolt/appfiguratelibrary/Configuration.html) (Android) subclass to read property values.

React Native always converts the underlying Double to a JavaScript Number - you should be aware of [JavaScript's 53 bit limitation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER).
{% endtab %}
{% endtabs %}

## **Local properties**

### **Slider UI**

Allows a double property to be changed in Appfigurate using a slider between minimum and maximum values.

![](/files/ww0B3ztrp4xfzgJ8bo82)

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

> Swift prototype

```swift
@DoublePropertySlider(min, max, icon, description, restart)
var propertyName: Double
```

You can customize the minimum and maximum images using the [<mark style="color:blue;">`icon`</mark>](https://www.electricbolt.co.nz/api/Enums/APLIconSlider.html) parameter. If `restart` is `true`, then the app will be restarted if the property value changes.

> Swift example

```swift
import AppfigurateLibrary

@objcMembers class Configuration: APLConfiguration {

    @DoublePropertySlider(min: 60.5, max: 120.41, icon: .volume, description: "Clipping - decibels", restart: false)
    var maxDecibel: Double
    
    override func reset() {
        maxDecibel = 100.0
    }
    ...
```

{% endtab %}

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

> Objective-C prototype

<pre class="language-objectivec"><code class="lang-objectivec"><strong>DOUBLE_PROPERTY_SLIDER(propertyName, minValue, maxValue, icon, description, restart)
</strong></code></pre>

You can customize the minimum and maximum images using the [<mark style="color:blue;">`icon`</mark>](/configuration-subclasses/slider-icon-types) parameter. If `restart` is `YES`, then the app will be restarted if the property value changes.

> Objective-C example

```objectivec
@import AppfigurateLibrary;

@interface Configuration : APLConfiguration

@property(nonatomic, assign) double maxDecibel;

@end

@implementation Configuration

DOUBLE_PROPERTY_SLIDER(maxDecibel, 60.5, 120.41, APLIconSliderVolume, @"Clipping - decibels", NO);

- (void) reset {
    self.maxDecibel = 100.0;
}
...
```

{% endtab %}

{% tab title="Dart" %}

> Dart prototype

```dart
double get propertyName => nativeDouble('propertyName');
```

> Dart example

```dart
import 'package:appfigurateflutter/appfigurateflutter.dart';

public class Configuration extends APLNativeConfiguration {

    double get maxDecibel => nativeDouble('maxDecibel');
    ...
```

{% endtab %}

{% tab title="JavaScript" %}

> JavaScript example

```javascript
import {
  NativeModules,
} from 'react-native';

const {Appfigurate} = NativeModules;

...

let maxDecibel = await Appfigurate.nativeValue("maxDecibel"); // Number
```

{% endtab %}
{% endtabs %}

### **Editable UI**

Allows a double property to be changed in Appfigurate using a text field between minimum and maximum values, and an optional regular expression validating input.

![](/files/X0VBu8lpOYXJVay5myUr)

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

> Swift prototype

```swift
@DoublePropertyEdit(min, max, regex, description, restart)
var propertyName: Double
```

If `restart` is `true`, then the app will be restarted if the property value changes.

> Swift example

```swift
import AppfigurateLibrary

@objcMembers class Configuration: APLConfiguration {

    @DoublePropertyEdit(min: -2.1, max: 4.1, regex: "", description: "Shot accuracy", restart: false)
    var shotAccuracy: Double
    
    override func reset() {
        shotAccuracy = 3.241
    }
    ...
```

{% endtab %}

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

> Objective-C prototype

```objectivec
DOUBLE_PROPERTY_EDIT(propertyName, minValue, maxValue, regex, description, restart)
```

If `restart` is `YES`, then the app will be restarted if the property value changes.

> Objective-C example

```objectivec
@import AppfigurateLibrary;

@interface Configuration : APLConfiguration

@property(nonatomic, assign) double shotAccuracy;

@end

@implementation Configuration

DOUBLE_PROPERTY_EDIT(shotAccuracy, -2.1, 4.1, @"", @"Shot accuracy", NO);

- (void) reset {
    self.shotAccuracy = 3.241;
}
...
```

{% endtab %}

{% tab title="Dart" %}

> Dart prototype

```dart
double get propertyName => nativeDouble('propertyName');
```

> Dart example

```dart
import 'package:appfigurateflutter/appfigurateflutter.dart';

public class Configuration extends APLNativeConfiguration {

    double get shotAccuracy => nativeDouble('shotAccuracy');
    ...
```

{% endtab %}

{% tab title="JavaScript" %}

> JavaScript example

```javascript
import {
  NativeModules,
} from 'react-native';

const {Appfigurate} = NativeModules;

...

let shotAccuracy = await Appfigurate.nativeValue("shotAccuracy"); // Number
```

{% endtab %}
{% endtabs %}

### **List UI**

Allows a double property to be changed in Appfigurate by allowing the user to select from a predefined list of valid choices.&#x20;

![](/files/hpnFYq3Tpvnz9KLb7czM)

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

> Swift prototype

```swift
@DoublePropertyList(description, restart, values)
var propertyName: Double
```

If `restart` is `true`, then the app will be restarted if the property value changes.

> Swift example

```swift
import AppfigurateLibrary

@objcMembers class Configuration: APLConfiguration {

    @DoublePropertyList(description: "Quality rating", restart: true, values: ["Low": 10.0, "Average": 50.0, "Excellent": 95.0])
    var rating: Double
    
    override func reset() {
        qualityRating = 10.0
    }
    ...
```

{% endtab %}

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

> Objective-C prototype

```objectivec
DOUBLE_PROPERTY_LIST(propertyName, description, restart, ...)
```

If `restart` is `YES`, then the app will be restarted if the property value changes.

> Objective-C example

```objectivec
@import AppfigurateLibrary;

@interface Configuration : APLConfiguration

@property(nonatomic, assign) double rating;

@end

@implementation Configuration

DOUBLE_PROPERTY_LIST(rating, @"Quality rating", NO, @{@"Low": @10.0, @"Average": @50.0, @"Excellent": @95.0});

- (void) reset {
    self.rating = 10.0;
}
...
```

{% endtab %}

{% tab title="Dart" %}

> Dart prototype

```dart
double get propertyName => nativeDouble('propertyName');
```

> Dart example

```dart
import 'package:appfigurateflutter/appfigurateflutter.dart';

public class Configuration extends APLNativeConfiguration {

    double get rating => nativeDouble('rating');
    ...
```

{% endtab %}

{% tab title="JavaScript" %}

> JavaScript example

```javascript
import {
  NativeModules,
} from 'react-native';

const {Appfigurate} = NativeModules;

...

let rating = await Appfigurate.nativeValue("rating"); // Number
```

{% endtab %}
{% endtabs %}

### **Editable List UI**

Allows a double property to be changed in Appfigurate by allowing the user to select from a predefined list of valid choices. The user can customize the list adding by additional values using a text field between minimum and maximum values, and an optional regular expression validating input.&#x20;

![](/files/OHK5pQDpAYcYRaDdTKQC)

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

> Swift prototype

```swift
@DoublePropertyListEdit(min, max, regex, description, restart, values)
var propertyName: Double
```

If `restart` is `true`, then the app will be restarted if the property value changes.

> Swift example

```swift
import AppfigurateLibrary

@objcMembers class Configuration: APLConfiguration {

    @DoublePropertyListEdit(min: 0.0, max: 366.0, regex: #"^(0?[0-9]?[0-9]|[1-2][0-9][0-9]|3[0-5][0-9]|36[0-5])?(?:\.\d+)?$"#, description: "Forward server time (days)", restart: false, values: ["7 days": 7.0, "1 month": 30.0, "1 Year": 365.0])
    var forwardServerTime: Double
    
    override func reset() {
        forwardServerTime = 7.0
    }
    ...
```

{% endtab %}

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

> Objective-C prototype

```objectivec
DOUBLE_PROPERTY_LIST_EDIT(propertyName, minValue, maxValue, regex, description, restart, ...)
```

If `restart` is `YES`, then the app will be restarted if the property value changes.

> Objective-C example

```objectivec
@import AppfigurateLibrary;

@interface Configuration : APLConfiguration

@property(nonatomic, assign) double forwardServerTime;

@end

@implementation Configuration

DOUBLE_PROPERTY_LIST_EDIT(forwardServerTime, 0.0, 366.0, @"^(0?[0-9]?[0-9]|[1-2][0-9][0-9]|3[0-5][0-9]|36[0-5])?(?:\\.\\d+)?$", @"Forward server time (days)", NO, @{@"7 days": @7.0, @"1 month": @30.0, @"1 Year": @365.0});

- (void) reset {
    self.forwardServerTime = 7.0;
}
...
```

{% endtab %}

{% tab title="Dart" %}

> Dart prototype

```dart
double get propertyName => nativeDouble('propertyName');
```

> Dart example

```dart
import 'package:appfigurateflutter/appfigurateflutter.dart';

public class Configuration extends APLNativeConfiguration {

    double get forwardServerTime => nativeDouble('forwardServerTime');
    ...
```

{% endtab %}

{% tab title="JavaScript" %}

> JavaScript example

```javascript
import {
  NativeModules,
} from 'react-native';

const {Appfigurate} = NativeModules;

...

let forwardServerTime = await Appfigurate.nativeValue("forwardServerTime"); // Number
```

{% endtab %}
{% endtabs %}

## Remote properties

### **Remote Editable UI**

Allows a third party remote configuration provider's double property to be changed locally in Appfigurate using a text field. If the override tick box is ticked off, the third party remote configuration provider's value is displayed. If the override tick box is ticked on, then you can use Appfigurate to locally override the value.

![](/files/JNn7IjFN1xiSGLx4yM2o)

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

> Swift prototype

```swift
@RemoteDoublePropertyEdit(remoteKey, description, restart)
var propertyName: Double
```

> Swift example

```swift
import AppfigurateLibrary

@objcMembers class Configuration: APLConfiguration {

    @RemoteDoublePropertyEdit(remoteKey: "fontSize", description: "Size of font throughout app")
    var fontSize: Double
    
    override func reset() {
        fontSize = 13.0
    }
    ...
```

{% endtab %}

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

> Objective-C prototype

<pre class="language-objectivec"><code class="lang-objectivec"><strong>REMOTE_DOUBLE_PROPERTY_EDIT(propertyName, remoteKey, description)
</strong></code></pre>

> Objective-C example

```objectivec
@import AppfigurateLibrary;

@interface Configuration : APLConfiguration

@property(nonatomic, assign) double fontSize;

@end

@implementation Configuration

REMOTE_DOUBLE_PROPERTY_EDIT(fontSize, @"Size of font throughout app");

- (void) reset {
    self.fontSize = 13.0;
}
...
```

{% endtab %}

{% tab title="Dart" %}

> Dart prototype

```dart
double get propertyName => nativeDouble('propertyName');
```

> Dart example

```dart
import 'package:appfigurateflutter/appfigurateflutter.dart';

public class Configuration extends APLNativeConfiguration {

    double get fontSize => nativeDouble('fontSize');
    ...
```

{% endtab %}

{% tab title="JavaScript" %}

> JavaScript example

```javascript
import {
  NativeModules,
} from 'react-native';

const {Appfigurate} = NativeModules;

...

let fontSize = await Appfigurate.nativeValue("fontSize"); // Number
```

{% endtab %}
{% endtabs %}


# Plain String

{% tabs %}
{% tab title="Swift" %}
The default value of a `String` is "". You **must** change the default value of the property by assigning a new value in an overridden [<mark style="color:blue;">`reset`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)reset) method.
{% endtab %}

{% tab title="Objective-C" %}
The default value of a `NSString` is @"". You **must** change the default value of the property by assigning a new value in an overridden [<mark style="color:blue;">`reset`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)reset) method.&#x20;

`NSString` properties should be declared in your [<mark style="color:blue;">`APLConfiguration`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html) subclass header file as follows:

```objectivec
@import AppfigurateLibrary;

@interface Configuration : APLConfiguration

@property(nonatomic, strong) NSString* propertyName;
...
```

{% endtab %}

{% tab title="Dart" %}
The flutter [<mark style="color:blue;">`APLNativeConfiguration`</mark>](https://pub.dev/documentation/appfigurateflutter/latest/appfigurateflutter/APLNativeConfiguration-class.html) class defers to the underlying platform [<mark style="color:blue;">`APLConfiguration`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html) (iOS) or [<mark style="color:blue;">`nz.co.electricbolt.appfiguratelibrary.Configuration`</mark>](https://www.electricbolt.co.nz/api/android/nz/co/electricbolt/appfiguratelibrary/Configuration.html) (Android) subclass to read property values.
{% endtab %}

{% tab title="JavaScript" %}
The React Native module `AppfigurateModule` class defers to the underlying platform [<mark style="color:blue;">`APLConfiguration`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html) (iOS) or [<mark style="color:blue;">`nz.co.electricbolt.appfiguratelibrary.Configuration`</mark>](https://www.electricbolt.co.nz/api/android/nz/co/electricbolt/appfiguratelibrary/Configuration.html) (Android) subclass to read property values.
{% endtab %}
{% endtabs %}

This page describes plain textual strings. Appfigurate also supports [encrypted strings](/configuration-subclasses/supported-property-types/encrypted-string).

## **Local properties**

### **Editable UI**

Allows a string property to be changed in Appfigurate using a text field with an optional regular expression validating input.

![](/files/jcYuX6OlRVqfEMdCzIIj)

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

> Swift prototype

<pre class="language-swift"><code class="lang-swift">@StringPropertyEdit(regex, description, restart)
var propertyName: <a data-footnote-ref href="#user-content-fn-1">String</a>
</code></pre>

> Swift example

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

@objcMembers class Configuration: APLConfiguration {

    @StringPropertyEdit(regex: "", description: "Overridden session username", restart: false)
    var usernameOverride: <a data-footnote-ref href="#user-content-fn-1">String</a>
    
    override func reset() {
        usernameOverride = "thomas52"
    }
    ...
</code></pre>

{% endtab %}

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

> Objective-C prototype

```objectivec
STRING_PROPERTY_EDIT(propertyName, regex, description, restart)
```

> Objective-C example

```objectivec
@import AppfigurateLibrary;

@interface Configuration : APLConfiguration

@property(nonatomic, strong) NSString* usernameOverride;

@end

@implementation Configuration

STRING_PROPERTY_EDIT(usernameOverride, @"", @"Overridden session username", NO);

- (void) reset {
    self.usernameOverride = @"thomas52";
}
...
```

{% endtab %}

{% tab title="Dart" %}

> Dart prototype

```dart
String get propertyName => nativeString('propertyName');
```

> Dart example

```dart
import 'package:appfigurateflutter/appfigurateflutter.dart';

public class Configuration extends APLNativeConfiguration {

    String get usernameOverride => nativeString('usernameOverride');
    ...
```

{% endtab %}

{% tab title="JavaScript" %}

> JavaScript example

```javascript
import {
  NativeModules,
} from 'react-native';

const {Appfigurate} = NativeModules;

...

let usernameOverride = await Appfigurate.nativeValue("usernameOverride");
```

{% endtab %}
{% endtabs %}

### **List UI**

Allows a string property to be changed in Appfigurate by allowing the user to select from a predefined list of valid choices.&#x20;

![](/files/rlqxD0hHOhhkJUw2zkBG)

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

> Swift prototype

<pre class="language-swift"><code class="lang-swift">@StringPropertyList(description, restart, values)
var propertyName: <a data-footnote-ref href="#user-content-fn-1">String</a>
</code></pre>

> Swift example

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

@objcMembers class Configuration: APLConfiguration {

    @StringPropertyList(description: "Color of background", restart: false, values: ["LightGray":"#d3d3d3", "White":"#ffffff", "Beige":"f5fcdc"])
    var backgroundColorHex: <a data-footnote-ref href="#user-content-fn-1">String</a>
    
    override func reset() {
        backgroundColorHex = "#d3d3de"
    }
    ...
</code></pre>

{% endtab %}

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

> Objective-C prototype

```objectivec
STRING_PROPERTY_LIST(propertyName, description, restart, ...)
```

> Objective-C example

```objectivec
@import AppfigurateLibrary;

@interface Configuration : APLConfiguration

@property(nonatomic, strong) NSString* backgroundColorHex;

@end

@implementation Configuration

STRING_PROPERTY_LIST(backgroundColorHex, @"Color of background", NO, @{@"LightGray": @"#d3d3d3", @"White": @"#ffffff", @"Beige": @"f5fcdc"});

- (void) reset {
    self.backgroundColorHex = "#d3d3d3";
}
...
```

{% endtab %}

{% tab title="Dart" %}

> Dart prototype

```dart
String get propertyName => nativeString('propertyName');
```

> Dart example

```dart
import 'package:appfigurateflutter/appfigurateflutter.dart';

public class Configuration extends APLNativeConfiguration {

    String get backgroundColorHex => nativeString('backgroundColorHex');
    ...
```

{% endtab %}

{% tab title="JavaScript" %}

> JavaScript example

```javascript
import {
  NativeModules,
} from 'react-native';

const {Appfigurate} = NativeModules;

...

let backgroundColorHex = await Appfigurate.nativeValue("backgroundColorHex");
```

{% endtab %}
{% endtabs %}

### **Editable List UI**

Allows a string property to be changed in Appfigurate by allowing the user to select from a predefined list of valid choices. The user can customize the list adding by additional values using a text field and an optional regular expression validating input.&#x20;

![](/files/CLM2mWvQmhzApP0xRdHS)

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

> Swift prototype

<pre class="language-swift"><code class="lang-swift">@StringPropertyListEdit(regex, description, restart, values)
var propertyName: <a data-footnote-ref href="#user-content-fn-1">String</a>
</code></pre>

> Swift example

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

@objcMembers class Configuration: APLConfiguration {

    @StringPropertyListEdit(regex: "^#([a-f0-9]{6})$", description: "Color of foreground", restart: false, values: ["Black":"#000000", "MistyRose":"#ffe4e1", "LightBlue":"add8e6"])
    var foregroundColorHex: <a data-footnote-ref href="#user-content-fn-1">String</a>
    
    override func reset() {
        foregroundColorHex = "#ffe4e1"
    }
    ...
</code></pre>

{% endtab %}

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

> Objective-C prototype

```objectivec
STRING_PROPERTY_LIST_EDIT(propertyName, regex, description, restart, ...)
```

> Objective-C example

```objectivec
@import AppfigurateLibrary;

@interface Configuration : APLConfiguration

@property(nonatomic, strong) NSString* foregroundColorHex;

@end

@implementation Configuration

STRING_PROPERTY_LIST_EDIT(foregroundColorHex, @"^#([a-f0-9]{6})$", @"Color of foreground", NO, @{@"Black": @"#000000", @"MistyRose": @"#ffe4e1", @"LightBlue": @"add8e6"});

- (void) reset {
    self.foregroundColorHex = @"#ffe4e1";
}
...
```

{% endtab %}

{% tab title="Dart" %}

> Dart prototype

```dart
String get propertyName => nativeString('propertyName');
```

> Dart example

```dart
import 'package:appfigurateflutter/appfigurateflutter.dart';

public class Configuration extends APLNativeConfiguration {

    String get foregroundColorHex => nativeString('foregroundColorHex');
    ...
```

{% endtab %}

{% tab title="JavaScript" %}

> JavaScript example

```javascript
import {
  NativeModules,
} from 'react-native';

const {Appfigurate} = NativeModules;

...

let foregroundColorHex = await Appfigurate.nativeValue("foregroundColorHex");
```

{% endtab %}
{% endtabs %}

## **Remote properties**

### **Remote Editable UI**

Allows a third party remote configuration provider's string property to be changed locally in Appfigurate using a text field. If the override tick box is ticked off, the third party remote configuration provider's value is displayed. If the override tick box is ticked on, then you can use Appfigurate to locally override the value.

&#x20;![](/files/D7ThCqru5Z40LGq4B6Fm)&#x20;

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

> Swift prototype

```swift
@RemoteStringPropertyEdit(remoteKey, description, restart)
var propertyName: String
```

> Swift example

```swift
import AppfigurateLibrary

@objcMembers class Configuration: APLConfiguration {

    @RemoteStringPropertyEdit(remoteKey: "appTitle", description: "Title of application")
    var appTitle: String
    
    override func reset() {
        appTitle = "Holiday finder"
    }
    ...
```

{% endtab %}

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

> Objective-C prototype

<pre class="language-objectivec"><code class="lang-objectivec"><strong>REMOTE_STRING_PROPERTY_EDIT(propertyName, remoteKey, description)
</strong></code></pre>

> Objective-C example

```objectivec
@import AppfigurateLibrary;

@interface Configuration : APLConfiguration

@property(nonatomic, assign) String appTitle;

@end

@implementation Configuration

REMOTE_STRING_PROPERTY_EDIT(appTitle, @"Title of application");

- (void) reset {
    self.appTitle = @"Holiday finder";
}
...
```

{% endtab %}

{% tab title="Dart" %}

> Dart prototype

```dart
String get propertyName => nativeString('propertyName');
```

> Dart example

```dart
import 'package:appfigurateflutter/appfigurateflutter.dart';

public class Configuration extends APLNativeConfiguration {

    String get appTitle => nativeString('appTitle');
    ...
```

{% endtab %}

{% tab title="JavaScript" %}

> JavaScript example

```javascript
import {
  NativeModules,
} from 'react-native';

const {Appfigurate} = NativeModules;

...

let appTitle = await Appfigurate.nativeValue("appTitle");
```

{% endtab %}
{% endtabs %}

[^1]: You should leave this as String, and not String! or String?. Any value assigned to this instance variable will be ignored, instead set the default value in the reset method.


# Encrypted String

{% tabs %}
{% tab title="Swift" %}
The default value of an encrypted `String` is "". You **must** change the default value of the property by assigning a new plain text value in an overridden [<mark style="color:blue;">`reset`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)reset) method.
{% endtab %}

{% tab title="Objective-C" %}
The default value of a `NSString` is @"". You **must** change the default value of the property by assigning a new plain text value in an overridden [<mark style="color:blue;">`reset`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)reset) method.

`NSString` properties should be declared in your [<mark style="color:blue;">`APLConfiguration`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html) subclass header file as follows:

```objectivec
@import AppfigurateLibrary;

@interface Configuration : APLConfiguration

@property(nonatomic, strong) NSString* propertyName;
...
```

{% endtab %}

{% tab title="Dart" %}
The flutter [<mark style="color:blue;">`APLNativeConfiguration`</mark>](https://pub.dev/documentation/appfigurateflutter/latest/appfigurateflutter/APLNativeConfiguration-class.html) class defers to the underlying platform [<mark style="color:blue;">`APLConfiguration`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html) (iOS) or [<mark style="color:blue;">`nz.co.electricbolt.appfiguratelibrary.Configuration`</mark>](https://www.electricbolt.co.nz/api/android/nz/co/electricbolt/appfiguratelibrary/Configuration.html) (Android) subclass to read property values.
{% endtab %}

{% tab title="JavaScript" %}
The React Native module `AppfigurateModule` class defers to the underlying platform [<mark style="color:blue;">`APLConfiguration`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html) (iOS) or [<mark style="color:blue;">`nz.co.electricbolt.appfiguratelibrary.Configuration`</mark>](https://www.electricbolt.co.nz/api/android/nz/co/electricbolt/appfiguratelibrary/Configuration.html) (Android) subclass to read property values.
{% endtab %}
{% endtabs %}

This page describes encrypted strings. Appfigurate also supports [plain textual strings](/configuration-subclasses/supported-property-types/plain-string).

## **Local properties**

### **Editable List UI**

Allows a string property to be changed in Appfigurate by allowing the user to select from a predefined list of valid choices. The user can customize the list adding by additional values using a text field and an optional regular expression validating input.&#x20;

![](/files/Fp8NneMH9xnO0cTXXoqq)

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

> Swift @EncryptedStringPropertyListEdit prototype

<pre class="language-swift"><code class="lang-swift">@EncryptedStringPropertyListEdit(regex, description, encrypted, restart, values)
var propertyName: <a data-footnote-ref href="#user-content-fn-1">String</a>
</code></pre>

The `encrypted` parameter of `EncryptedStringPropertyListEdit` must be the result of calling the `ENCRYPTED()` function. e.g.

```swift
... description: "url", encrypted: ENCRYPTED(), restart: ...
```

> Swift @EncryptedStringPropertyListEdit example

<pre class="language-swift"><code class="lang-swift">    @EncryptedStringPropertyListEdit(regex: #"https://[\w\.-]+\.appfigurate.io/.*"#, description: "url", encrypted: ENCRYPTED(), restart: false, values: [
        "Dev":ENCRYPTED_STRING("https://dev.appfigurate.io/list","H7o9Lgqd4RgMJ...rtJQGh8DiiSAQ=="),
        "Test":ENCRYPTED_STRING("https://test.appfigurate.io/list","PL+UtWB9WHuO7...aX5BdNqEcbmQE="),
        "Prod":ENCRYPTED_STRING("https://m.appfigurate.io/list","IxrJFFUarMg6p...E7OWYOC2uJ1AQ==")])
    var url: <a data-footnote-ref href="#user-content-fn-1">String</a>
    
    override func reset() {
        url = "https://m.appfigurate.io/list"
    }
    ...
</code></pre>

### **ENCRYPTED\_STRING function**

> Swift ENCRYPTED\_STRING prototype

```swift
func ENCRYPTED_STRING(_ plaintext: String, _ ciphertext: String) -> String
```

For an iOS app (or watchOS and it's companion iOS app) where the `APLConfiguration` subclass has one public key use the `ENCRYPTED_STRING` function for each list item in the `@EncryptedStringPropertyListEdit`.

The first parameter of the `ENCRYPTED_STRING` function is the plaintext, and will be used by `DEBUG` builds. The second parameter of the `ENCRYPTED_STRING` macro is the ciphertext, and will be used by `RELEASE` builds. To generate the ciphertext, use the [source editor extension for Xcode](/appfigurate-se-user-guide/xcode-source-editor-extension) or [AppfigurateSE app](/appfigurate-se-user-guide/manual-encryption).

> Swift ENCRYPTED\_STRING example

```swift
    @EncryptedStringPropertyListEdit(regex: #"https://[\w\.-]+\.appfigurate.io/.*"#, description: "url", encrypted: ENCRYPTED(), restart: false, values: [
        "Dev":ENCRYPTED_STRING("https://dev.appfigurate.io/list","H7o9Lgqd4RgMJ...rtJQGh8DiiSAQ=="),
        "Test":ENCRYPTED_STRING("https://test.appfigurate.io/list","PL+UtWB9WHuO7...aX5BdNqEcbmQE="),
        "Prod":ENCRYPTED_STRING("https://m.appfigurate.io/list","IxrJFFUarMg6p...E7OWYOC2uJ1AQ==")])
    var url: String
```

### **ENCRYPTED function**

> Swift ENCRYPTED prototype

```swift
func ENCRYPTED() -> Bool
```

Used in conjunction with the `@EncryptedStringPropertyListEdit` property wrapper. The `encrypted` parameter of `@EncryptedStringPropertyListEdit` must be the result of calling the `ENCRYPTED()` function. e.g.

```swift
... description: "url", encrypted: ENCRYPTED(), restart: ...
```

The function returns `true` if the calling app has been compiled in RELEASE mode or `false` if the calling app has been compiled in DEBUG mode.

### ENCRYPTED function & Swift compiler ‣ Optimizing for Size

{% hint style="warning" %}
**Warning:** The ENCRYPTED function will behave incorrectly if the apps build settings `Swift compiler - Code generation ‣ Optimization Level` is set to `Optimize for Size` (-Osize). The default Swift compiler optimization levels are `No Optimization` for `DEBUG` builds and `Optimize for Speed` -Ospeed for `RELEASE` builds. If you must use -Osize, then implement your own `isEncrypted()` style function as follows:
{% endhint %}

> Swift isEncrypted example

```swift
func isEncrypted() {
#if DEBUG
	return false
#else
	return true
#end
}
```

Then call your own `isEncrypted()` function instead of the provided `ENCRYPTED()` function e.g.

```swift
... description: "url", encrypted: isEncrypted(), restart: ...
```

{% endtab %}

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

> Objective-C ENCRYPTED\_STRING\_PROPERTY\_LIST\_EDIT prototype

```objectivec
ENCRYPTED_STRING_PROPERTY_LIST_EDIT(propertyName, regex, description, restart, ...)
```

> Objective-C ENCRYPTED\_STRING\_PROPERTY\_LIST\_EDIT example

```objectivec
@import AppfigurateLibrary;

@interface Configuration : APLConfiguration

@property(nonatomic, strong) NSString* url;

@end

@implementation Configuration

ENCRYPTED_STRING_PROPERTY_LIST_EDIT(url, @"https://[\\w\\.-]+\\.appfigurate.io/.*", @"url", NO, @{
    @"Dev": ENCRYPTED_STRING(@"https://dev.appfigurate.io/list",@"H7o9Lgqd4RgMJ...rtJQGh8DiiSAQ=="), 
    @"Test": ENCRYPTED_STRING(@"https://test.appfigurate.io/list",@"PL+UtWB9WHuO7...aX5BdNqEcbmQE="), 
    @"Prod": ENCRYPTED_STRING(@"https://m.appfigurate.io/list",@"IxrJFFUarMg6p...E7OWYOC2uJ1AQ==")});
    
- (void) reset {
    self.url = @"https://m.appfigurate.io/list";
}
...
```

### **ENCRYPTED\_STRING macro**

> Objective-C ENCRYPTED\_STRING prototype

```objectivec
ENCRYPTED_STRING(plaintext, ciphertext)
```

For an iOS app (or watchOS and it's companion iOS app) where the `APLConfiguration` subclass has one public key use the `ENCRYPTED_STRING` macro for each list item in the `ENCRYPTED_STRING_PROPERTY_LIST_EDIT`.

The first parameter of the `ENCRYPTED_STRING` macro is the plain text, and will be used by `DEBUG` builds. The second parameter of the `ENCRYPTED_STRING` macro is the ciphertext, and will be used by `RELEASE` builds. To generate the ciphertext, use the [source editor extension for Xcode](/appfigurate-se-user-guide/xcode-source-editor-extension) or [AppfigurateSE app](/appfigurate-se-user-guide/manual-encryption).

> Objective-C ENCRYPTED\_STRING example

```objectivec
ENCRYPTED_STRING_PROPERTY_LIST_EDIT(url, @"https://[\\w\\.-]+\\.appfigurate.io/.*", @"url", NO, @{
    @"Dev":ENCRYPTED_STRING(@"https://dev.appfigurate.io/list",@"H7o9Lgqd4RgMJ...rtJQGh8DiiSAQ=="), 
    @"Test":ENCRYPTED_STRING(@"https://test.appfigurate.io/list",@"PL+UtWB9WHuO7...aX5BdNqEcbmQE="), 
    @"Prod":ENCRYPTED_STRING(@"https://m.appfigurate.io/list",@"IxrJFFUarMg6p...E7OWYOC2uJ1AQ==")});
```

{% endtab %}

{% tab title="Dart" %}

> Dart prototype

```dart
String get propertyName => nativeString('propertyName');
```

> Dart example

```dart
import 'package:appfigurateflutter/appfigurateflutter.dart';

public class Configuration extends APLNativeConfiguration {

    String get url => nativeString('url');
    ...
```

{% endtab %}

{% tab title="JavaScript" %}

> JavaScript example

```javascript
import {
  NativeModules,
} from 'react-native';

const {Appfigurate} = NativeModules;

...

let url = await Appfigurate.nativeValue("url");
```

{% endtab %}
{% endtabs %}

## Encryption best practice

### **iOS and watchOS**

Using `ENCRYPTED_STRING` guarantees that the plaintext of the list item values will not be included in the resulting application binary, only the ciphertext. The ciphertext can only be decrypted by Appfigurate using the correct private key.

You can verify that the plain text is not included in the RELEASE application binary using the macOS `strings` tool as follows:

> Terminal

```bash
> cd ~/Library/Developer/Xcode/DerivedData/AppfigurateWorkspace-esajjxyazigjunfteipthjfiobut/Build/Products/Release-iphoneos/AppfigurateExample (iOS).app
> strings AppfigurateExample | grep "https://dev.appfigurate.io/list"     // no results
> strings AppfigurateExample | grep "H7o9Lgqd4RgMJ...rtJQGh8DiiSAQ=="     // prints H7o9Lgqd4RgMJ...rtJQGh8DiiSAQ==
```

It is best practice to use `ENCRYPTED_STRING` to encrypt sensitive information such as server urls (e.g. internal test environments), rather than expose them as plain text.

### Flutter

Ensure you have read the encryption best practices for [iOS and watchOS](#ios-and-watchos) sections.

### React Native

Ensure you have read the encryption best practices for [iOS and watchOS](#ios-and-watchos) sections.

[^1]: You should leave this as String, and not String! or String?. Any value assigned to this instance variable will be ignored, instead set the default value in the reset method.


# Custom executable actions

## Action UI

Allows the custom executable action to be executed by Appfigurate. Any configuration properties you modify in the action method are persisted. Actions appear below properties in the Appfigurate UI.

![](/files/XKu1RTF0iQvmoEsiepT0)

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

> Swift prototype

As you cannot annotate a Swift method with a property wrapper, your action methods must be specified manually.

Replace `XXX` with the name of your action (e.g. `freshInstall`)

```swift
func XXXDescription() -> String
func XXXAction()
func XXXRestart() -> Bool
```

If you return `true` from your `XXXRestart()` method, then the app will be restarted after the action method is executed.

> Swift example

```swift
@objcMembers class Configuration: APLConfiguration {

    func freshInstallDescription() -> String {
        return "Resets application to fresh install state"
    }
    
    func freshInstallAction() {
        let defs = UserDefaults.standard
        let dict = defs.dictionaryRepresentation()
        for key in dict.keys {
            defs.removeObject(forKey: key)
        }
        defs.synchronize()
    }

    func freshInstallRestart() -> Bool {
        return false
    }
    ...
```

{% endtab %}

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

> Objective-C prototype

```objectivec
ACTION_METHOD(actionName, description, restart) { ... }
```

If `restart` is `YES` then the app will be restarted after the action method is executed.

> Objective-C example

```objectivec
@implementation Configuration

ACTION_METHOD(freshInstall, "Resets application to fresh install state", NO) {
    NSUserDefaults* defs = [NSUserDefaults standardUserDefaults];
    NSDictionary* dict = [defs dictionaryRepresentation];
    for (id key in dict) {
        [defs removeObjectForKey: key];
    }
    [defs synchronize];
}
...
```

{% endtab %}

{% tab title="Dart" %}
When an action is executed in the underlying native [<mark style="color:blue;">`APLConfiguration`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html) (iOS) or [<mark style="color:blue;">`nz.co.electricbolt.appfiguratelibrary.Configuration`</mark>](https://www.electricbolt.co.nz/api/android/nz/co/electricbolt/appfiguratelibrary/Configuration.html) (Android) subclass, Flutter is also notified. Override the [<mark style="color:blue;">`actionExecuted(action)`</mark>](https://pub.dev/documentation/appfigurateflutter/latest/appfigurateflutter/APLNativeConfiguration/actionExecuted.html) method in [<mark style="color:blue;">`APLNativeConfiguration`</mark>](https://pub.dev/documentation/appfigurateflutter/latest/appfigurateflutter/APLNativeConfiguration-class.html) as follows to receive the action:

> Dart example

```dart
import 'package:appfigurateflutter/appfigurateflutter.dart';

class ExampleConfiguration extends APLNativeConfiguration {
  factory ExampleConfiguration() => _instance;

  static final ExampleConfiguration _instance = ExampleConfiguration._internal();

  ExampleConfiguration._internal();

  @override
  void actionExecuted(String action) {
    if (action == 'freshInstall') {
      ...
    }
  }
...
```

{% endtab %}

{% tab title="JavaScript" %}
When an action is executed in the underlying native [`APLConfiguration`](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html) (iOS) or [`nz.co.electricbolt.appfiguratelibrary.Configuration`](https://www.electricbolt.co.nz/api/android/nz/co/electricbolt/appfiguratelibrary/Configuration.html) (Android) subclass, React Native is also notified. Subscribe to an event emitter to receive the action:

> JavaScript example

```javascript
import {
  NativeModules,
  NativeEventEmitter,
} from 'react-native';

const {Appfigurate} = NativeModules;
const AppfigurateEvents = new NativeEventEmitter(NativeModules.Appfigurate);

AppfigurateEvents.addListener('APLConfigurationUpdated', result => {
  if (result.APLConfigurationUpdatedAction == "freshInstall") {
    ...
  }
});
```

{% endtab %}
{% endtabs %}


# Slider icon types

The following Slider icon types are available for use in the Objective-C `INT_PROPERTY_SLIDER`, `FLOAT_PROPERTY_SLIDER` and `DOUBLE_PROPERTY_SLIDER` macros, and Swift, Kotlin and Java `@IntPropertySlider`, `@FloatPropertySlider` and `@DoublePropertySlider` annotations.

## Slider icon enumerations

### Speed

<img src="/files/7jSGM7ZGZfZcvDqFdm3Y" alt="" data-size="line"> ... <img src="/files/sZ4vLSmvmftMaLDP27Bs" alt="" data-size="line">

Tortise (minimum) and Hare (maximum) images

| Swift  | Objective-C        |
| ------ | ------------------ |
| .speed | APLIconSliderSpeed |

### Capacity

<img src="/files/AN0CbC5WI29JPisAgsZb" alt="" data-size="line"> ... <img src="/files/Qsdi56w1K7q84T1ZMm8q" alt="" data-size="line">

Battery empty (minimum) and Battery full (maximum) images

| Swift     | Objective-C           |
| --------- | --------------------- |
| .capacity | APLIconSliderCapacity |

### Numeric

<img src="/files/KJkyKMzUI3mNvofrUwch" alt="" data-size="line"> ... <img src="/files/gUkRfLf73w26bKhewK36" alt="" data-size="line">

0 (minimum) and 1 (maximum) images

| Swift    | Objective-C          |
| -------- | -------------------- |
| .numeric | APLIconSliderNumeric |

### Brightness

<img src="/files/Eg52HZJmCR0VNCNHNLi7" alt="" data-size="line"> ... <img src="/files/PoQmtCAC5DWGI9f7ZdIz" alt="" data-size="line">

Dim (minimum) and Bright (maximum) images

| Swift       | Objective-C             |
| ----------- | ----------------------- |
| .brightness | APLIconSliderBrightness |

### Volume

<img src="/files/XVzyAqfya4hYciqzxRZm" alt="" data-size="line"> ... <img src="/files/vrdFzJHr9S4mrSINBsmZ" alt="" data-size="line">

Low volume (minimum) and High volume (maximum) images

| Swift   | Objective-C         |
| ------- | ------------------- |
| .volume | APLIconSliderVolume |


# Environment tags

Environment tags provide a quick and easy way to change multiple configuration properties at the same time. Environment tags are optional and can be specified in any List or Editable List configuration properties, in the key of any key-value pairs.

For example, you may have the following development environments: DEV, TEST, PVT (and Production which would be the default reset state of the app). Environment tags in the key of key-value pairs are not case sensitive, but we recommend using uppercase. Environment tags are wrapped in `{` `}` characters.

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

```swift
import AppfigurateLibrary

@objcMembers class Configuration: APLConfiguration {

    @IntPropertyList(description: "Quality rating", restart: false, values: [
        "{DEV,TEST}Low": 10, 
        "{PVT}Average": 50, 
        "Excellent": 95
    ])
    var rating: Int
    
    override func environmentTags() -> [String] {
        return ["Dev", "Test", "PVT"]
    }
    
    override func reset() {
        rating = 10
    }
    ...
```

{% endtab %}

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

```objectivec
@import AppfigurateLibrary;

@interface Configuration : APLConfiguration

@property(nonatomic, assign) NSInteger rating;

@end

@implementation Configuration

INT_PROPERTY_LIST(rating, @"Quality rating", NO, @{
    @"{DEV,TEST}Low": @10, 
    @"{PVT}Average": @50, 
    @"Excellent": @95
});

- (NSArray<NSString*>*) environmentTags {
    return @{@"Dev", @"Test", @"PVT"};
}

- (void) reset {
    self.rating = 10;
}
...
```

{% endtab %}
{% endtabs %}

The list returned by the `environmentTags` method is used for validation of key usage on startup of your app, and to display the list of environment tags in the UI, in the order and casing specified:

<figure><img src="/files/7WU3jvQ28kSYEhoqNTep" alt="" width="375"><figcaption><p>iOS</p></figcaption></figure>

Tapping any of the segments in Appfigurate will automatically change all of the List or Editable List  configuration properties to the appropriate value.

If you change any List or Editable List value to a conflicting value (e.g. you had selected PVT, but have now selected a single List value that is marked as DEV), then Appfigurate will deselect any highlighted segment:

<figure><img src="/files/a7YuSdNaR3wgCsA32N2S" alt="" width="375"><figcaption><p>iOS</p></figcaption></figure>


# Info.plist options

If you have an iOS app (including Flutter or React Native) or a watchOS app, then the following additional `Info.plist` options are available.

## **APLLogging**

To enable debug logging in the Appfigurate library early on, add the `APLLogging` key to the `Info.plist` file. The `APLLogging` key is optional.

> `Info.plist` example

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
     <key>APLLogging</key>
     <true/>
     ...
```

The default value of `APLLogging` is false. You should not need to enable this to true unless you are having trouble integrating the library. It is best practice not to ship an app with this key set to true.

You can also call [<mark style="color:blue;">`APLSetLogging`</mark>](https://www.electricbolt.co.nz/api/Functions.html#/c:@F@APLSetLogging) function to achieve the same result in code.

## **Keychain accessibility**

Appfigurate Library stores any configuration payload and digital signature (that may have been applied by Appfigurate) securely in your app's Keychain.

The `APLKeychainAccessibility` key affects the access level of that configuration payload and digital signature. Best practice is to use the most restrictive option that is compatible with your app (and app extensions). The `APLKeychainAccessibility` key is optional.

| Value                            | Description                                                                                                                                                                                                                                                                                                                                                                                                                  |
| -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `WhenUnlocked`                   | Configuration can only be accessed while the device is unlocked. Will migrate to a new device when restored from an encrypted backup.                                                                                                                                                                                                                                                                                        |
| `AfterFirstUnlock`               | Configuration can only be accessed once the device has been unlocked after a restart. Will migrate to a new device when restored from an encrypted backup.                                                                                                                                                                                                                                                                   |
| ~~`Always`~~                     | <p>Configuration can always be accessed regardless of the lock state of the device. Will migrate to a new device when restored from an encrypted backup.</p><p></p><p><strong>Deprecated</strong> for general apps, use an accessibility level that provides some user protection, such as <code>WhenUnlocked</code>. <strong>Acceptable</strong> for VoIP apps that can be launched before the user unlocks the device.</p> |
| `WhenPasscodeSetThisDeviceOnly`  | Configuration can only be accessed while the device is unlocked.                                                                                                                                                                                                                                                                                                                                                             |
| `WhenUnlockedThisDeviceOnly`     | Configuration can only be accessed while the device is unlocked.                                                                                                                                                                                                                                                                                                                                                             |
| `AfterFirstUnlockThisDeviceOnly` | Configuration can only be accessed once the device has been unlocked after a restart.                                                                                                                                                                                                                                                                                                                                        |
| ~~`AlwaysThisDeviceOnly`~~       | <p>Configuration can always be accessed regardless of the lock state of the device. </p><p></p><p><strong>Deprecated</strong> for general apps, use an accessibility level that provides some user protection, such as <code>WhenUnlockedThisDeviceOnly</code>. </p><p><strong>Acceptable</strong> for VoIP apps which can be launched before the user unlocks the device.</p>                                               |

> `Info.plist` example

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
     <key>APLKeychainAccessibility</key>
     <string>AfterFirstUnlock</string>
     ...
```

The default value of `APLKeychainAccessibility` is `Always`.

## Info.plist key availability by platform

<table><thead><tr><th width="288">Key</th><th>iOS app</th><th>iOS app extension</th><th>watchOS app</th><th>watchOS app extension</th></tr></thead><tbody><tr><td><code>APLLogging</code></td><td>✓</td><td>1</td><td>✓</td><td>2</td></tr><tr><td><code>APLKeychainAccessibility</code></td><td>✓</td><td>1</td><td>✓</td><td>2</td></tr><tr><td><code>APLKeychainAccessGroup</code></td><td>✓</td><td>1</td><td>✓</td><td>2</td></tr></tbody></table>

1=iOS app extensions automatically use the `Info.plist` file from the containing app. You do not need to modify the iOS app extension's `Info.plist` file.

2=watchOS app extensions automatically use the `Info.plist` file from the containing watch app. You do not need to modify the watchOS app extension's `Info.plist` file.

## Standard Info.plist behaviour

### App name

The standard key `CFBundleDisplayName` (if specified) will be used to populate the name of the app in Appfigurate. If not specified, then the `CFBundleName` value is used.

### App version

The standard key `CFBundleShortVersionString` (if specified) will be used to populate the version of the app in Appfigurate. If not specified, then the `CFBundleVersion` value is used.

### App icon

To populate the icon of the app in Appfigurate, the following search order is used: `CFBundleIcons`,`CFBundleIconFiles`,`CFBundleIconFile`.

For iOS, the search order of app icon sizes is: 60x60\@2x, 60x60\@3x, 76x76\@2x, 83.5x83.5\@2x, 60x60\@1x, 76x76\@1x

For watchOS, the search order of app icon sizes is: 40x40\@2x


# Displaying overridden  configuration

It is useful to display the currently applied overridden configuration in your app. We suggest you display this on your app's home screen.

The provided [<mark style="color:blue;">`APLConfigurationLabel`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html) for UIKit (Swift, Objective-C), `.configurationView` modifier for SwiftUI and [<mark style="color:blue;">`APLConfigurationLabel`</mark>](https://pub.dev/documentation/appfigurateflutter/latest/appfigurateflutter/APLConfigurationLabel-class.html) (Dart) displays overridden configuration drawn in a vertical orientation attached to the left hand side of the screen as follows:

<img src="/files/Zyc1nLFYbfxKXVYKYRef" alt="" data-size="original">

{% tabs %}
{% tab title="Swift (UIKit)" %}
You can add the [<mark style="color:blue;">`APLConfigurationLabel`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfigurationLabel.html) to your view controller in a `viewDidLoad` like this:

```swift
override func viewDidLoad() {
    let label = APLConfigurationLabel(frame: .zero)
    label.center = CGPoint(x: 7, y: self.navigationController!.view.bounds.size.height / 2)
    self.navigationController!.view.addSubview(label)
}
```

{% endtab %}

{% tab title="SwiftUI" %}
You can add the `.configurationView` modifier to your SwiftUI view like this:

```swift
NavigationStack {
  ...your content...
}
.configurationView()
```

This `.configurationView` modifier is available for both iOS and watchOS.
{% endtab %}

{% tab title="Objective-C (UIKit)" %}
You can add the [<mark style="color:blue;">`APLConfigurationLabel`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfigurationLabel.html) to your view controller in a `viewDidLoad` like this:

```objectivec
- (void) viewDidLoad {
    APLConfigurationLabel* label = [[APLConfigurationLabel alloc] initWithFrame: CGRectZero];
    label.center = CGPointMake(7, self.navigationController.view.bounds.size.height / 2);
    [self.navigationController.view addSubview: label];
}
```

{% endtab %}

{% tab title="Dart (Flutter)" %}
Make the `body` of your page a [<mark style="color:blue;">`APLConfiguationLabel`</mark>](https://pub.dev/documentation/appfigurateflutter/latest/appfigurateflutter/APLConfigurationLabel-class.html), and set it's `child` to the original `body`:

```dart
...
  @override
  Widget build(BuildContext context) {
    var media = MediaQuery.of(context);
    var padding = media.padding.left == 0.0 ? 8.0 : media.padding.left;

    return Scaffold(
      appBar: AppBar(
        title: Text('Example'),
      ),
      body: APLConfigurationLabel(child:
        ListView(
...
```

{% endtab %}
{% endtabs %}

## Creating your own label

If `APLConfigurationLabel` is not sufficient for your needs, you can create your own label as follows:

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

> Swift [<mark style="color:blue;">`APLAddConfigurationUpdatedListener`</mark>](https://www.electricbolt.co.nz/api/Functions.html#/c:@F@APLAddConfigurationUpdatedListener) example

```swift
extension MyLogonController: APLConfigurationUpdated {

    override func viewDidLoad() {
        APLAddConfigurationUpdatedListener(self)
    }

    func configurationUpdated(_ notification: Notification?) {
        label.text = APLConfiguration.shared().modifications
    }
...
```

{% endtab %}

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

> Objective-C [<mark style="color:blue;">`APLAddConfigurationUpdatedListener`</mark>](https://www.electricbolt.co.nz/api/Functions.html#/c:@F@APLAddConfigurationUpdatedListener) example

```objectivec
@interface MyLogonController () <APLConfigurationUpdated>
@end

@implementation MyLogonController

- (void) viewDidLoad {
    APLAddConfigurationUpdatedListener(self);
}

- (void) configurationUpdated: (NSNotification*) notification {
    label.text = [[APLConfiguration sharedConfiguration] modifications];
}
...
```

{% endtab %}

{% tab title="Dart" %}

> Dart [<mark style="color:blue;">`APLAddConfigurationUpdatedListener`</mark>](https://pub.dev/documentation/appfigurateflutter/latest/appfigurateflutter/APLAddConfigurationUpdatedListener.html) example

```dart
class _MyLabelState extends State<MyLabel> {

  @override
  void initState() {
    super.initState();
    APLAddConfigurationUpdatedListener(configurationUpdated);
  }

  @override
  void dispose() {
    APLRemoveConfigurationUpdatedListener(configurationUpdated);
    super.dispose();
  }

  void configurationUpdated(String? action) {
    setState(() {});
  }
  
  @override
  Widget build(BuildContext context) {
    return Text('${APLNativeConfiguration.sharedConfiguration().modifications()}');
  }
...
```

{% endtab %}
{% endtabs %}


# Security

Read the following for [best practices](/additional-reading/security/best-practice), [encryption](/additional-reading/security/encryption), [export compliance](/additional-reading/security/export-compliance), [App Store compliance](/additional-reading/security/app-store-compliance), [PrivacyInfo.xcprivacy](/additional-reading/security/privacyinfo.xcprivacy) and [rotating your private key](/additional-reading/security/rotating-your-private-key).


# Best practice

## Protecting your app metadata records

Appfigurate generates a unique 2048-bit RSA private key as part of an app's metadata record that is used for signing configuration payloads. To protect app metadata records you should follow these best practices:

### **iOS Simulator**

We rely upon you and your Mac's security. You Mac should be up-to-date with all security patches applied, firewall enabled, FileVault on. You should use a strong password on boot and screen unlock.

### **Physical devices**

Ensure the device has a strong passcode (six-digit) or custom alphanumeric code. Enable Erase Data after ten failed passcode attempts. <https://support.apple.com/en-us/HT204060>.

You should [enable PIN](/appfigurate-user-guide/settings/passcode-lock#passcode-on-launch) and [TouchID/FaceID/Biometrics](/appfigurate-user-guide/settings/passcode-lock#biometrics-on-launch) to unlock Appfigurate each time it is run, or to restrict your app's metadata from being exported. The database will be deleted after 5 incorrect PIN attempts.

### Real device cloud testing services

For supported real device cloud testing services, your pre-packaged Appfigurate instance is uploaded to the vendors servers and you have no control over storage of your apps. We recommend [using wrong app metadata records for development](#using-wrong-app-metadata-records-for-development) in this scenario so your real app metadata record is never revealed.

## Using wrong app metadata records for development

{% hint style="success" %}
In a large development team it may be beneficial to restrict the distribution of real app metadata records to selected team members. Other team members can continue to test debug builds using the following instructions:
{% endhint %}

In Appfigurate [add a new app](/appfigurate-user-guide/add-app) using the real URL Scheme. The newly created app metadata record will have a completely different (wrong) private key from the real app metadata record.

Ensure your app's [<mark style="color:blue;">`APLConfiguration.allowInvalidSignatures`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)allowInvalidSignatures) method returns `YES` for debug builds and `NO` for release builds. **Do not** replace the real public key in the [<mark style="color:blue;">`APLConfiguration.publicKey`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)publicKey) method with the newly generated (wrong) public key.

<table><thead><tr><th width="274.3333333333333"> </th><th width="218">App is debug build (allowInvalidSignatures returns YES)</th><th>App is release build (allowInvalidSignatures returns NO)</th></tr></thead><tbody><tr><td><strong>Real app metadata record</strong> (Selected team members only)</td><td><mark style="color:green;">✓ Configuration applied</mark></td><td><mark style="color:green;">✓ Configuration applied</mark></td></tr><tr><td><strong>Wrong app metadata record</strong> (Other team members)</td><td><mark style="color:green;">✓ Configuration applied</mark></td><td><mark style="color:red;">✕ Configuration not applied</mark></td></tr></tbody></table>

## Sharing app metadata records between team members

If you need to share an app metadata record (with the real private key) across devices in your team, you should limit the ability to re-export the app's metadata record on the new device. You can do this when [exporting from your device](/appfigurate-user-guide/edit-app#export-share-app) by leaving the `Exportable` switch off. When the app metadata record is imported into the new device, it will not be exportable from that device, and it will expire after 180 days.

## Release builds

When including Appfigurate library into release builds (App Store/TestFlight Beta/Enterprise/Google Play/Alpha track/Beta track), you should ensure that:

1. Your [<mark style="color:blue;">`APLConfiguration`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html) subclass doesn't contain any non-public information that could be used for malicious purposes. e.g. internal server names. You should consider using [encrypted strings](/configuration-subclasses/supported-property-types/encrypted-string).
2. If you have overridden the default behaviour of [<mark style="color:blue;">`APLConfiguration.allowInvalidSignatures`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)allowInvalidSignatures), that you've checked it returns the correct value for debug and release builds. To test this, use Appfigurate to read the current configuration of your app. If a release build reports `Debug build - App allows invalid signatures` then your implementation of [<mark style="color:blue;">`APLConfiguration.allowInvalidSignatures`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)allowInvalidSignatures) is incorrect.
3. You can test applying configuration using a wrong signature to a release build by long-pressing the `Apply⌄` button, then tapping the `Apply invalid to this device` action. Your app will be launched with a configuration payload signed by an invalid key. If the configuration is successfully applied, then your implementation of [<mark style="color:blue;">`APLConfiguration.allowInvalidSignatures`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)allowInvalidSignatures) is incorrect.

## Rotate your private key

You should consider [rotating your private key](/additional-reading/security/rotating-your-private-key) occasionally.


# Encryption

## Database encryption

### iOS Simulator

For Appfigurate Simulator, we rely upon you and your Mac's security to secure your app metadata records. The database that stores your app metadata records is not encrypted and is located in the host macOS filesystem:

> `~/Library/Application Support/Appfigurate/`

This allows us to share your app metadata records across all iOS Simulators where Appfigurate is installed.

### Physical devices

For device builds (installed from the [Apple App Store](https://apps.apple.com/us/app/appfigurate/id1332575368?ls=1)), the database that stores your app metadata records is encrypted on device.

### Real device cloud testing services

When pre-packaging the local database that stores your app metadata records into the Appfigurate iOS, the database is stored unencrypted in the resulting ipa or apk file. We recommend using wrong app metadata records for development in this scenario.

## App metadata exports & backup

Exported app metadata is secured using the following:

* Passphrase derivation function uses 12-rounds of bcrypt.
* App metadata blob is encrypted with AES256.


# Export compliance

`AppfigurateLibrary.xcframework` (iOS/watchOS) and `appfigurateflutter` (Flutter plugin) (that you integrate into your apps) do not include or use any cryptographic routines as stated in Category 5 Part 2 of the U.S. Encryption and Export Administration Regulations (EAR).


# App Store compliance

## Hidden or undocumented features

To ensure you remain in compliance with [App Store Review Guidelines](https://developer.apple.com/app-store/review/guidelines/#performance), you must not include any hidden or undocumented features in your app; your apps functionality should be made clear to App Review (section 2.3.1). Appfigurate must not be used to enable hidden functionality; you may only use it to change runtime configuration for testing purposes (e.g. change web service URL to a point to a production verification test URL instead of production).

Note that all configuration applied to a device will automatically revert to default values after 7 days.

## Describing use of required reason API

`AppfigurateLibrary.xcframework` (that you integrate into your apps) does not use any of the APIs listed in the Apple documentation [Describing use of required reason API](https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_use_of_required_reason_api) (You do not need to supply any values into the `NSPrivacyAccessedAPITypeReasons` array).


# PrivacyInfo.xcprivacy

The iOS and watchOS AppfigurateLibrary.xcframework includes an embedded `PrivacyInfo.xcprivacy` file. The file is effectively empty as Appfigurate Library does not have any Tracking component or use any APIs as per the Apple <https://developer.apple.com/documentation/bundleresources/privacy_manifest_files> documentation.

> Current `PrivacyInfo.xcprivacy` file contents

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>NSPrivacyTracking</key>
    <false/>
    <key>NSPrivacyTrackingDomains</key>
    <array/>
    <key>NSPrivacyCollectedDataTypes</key>
    <array/>
    <key>NSPrivacyAccessedAPITypes</key>
    <array/>
</dict>
</plist>

```


# Rotating your private key

You should rotate your private key occasionally. When you do this depends upon several factors:

* Your companies security policy restricting the length of time private key material is valid for.
* Your mobile app deprecation policy.
* Your app metadata being exposed (data loss).
* Development team members leaving your employment.

{% hint style="info" %}
The instructions here assume you are using Appfigurate in an iOS Simulator, and Xcode and Swift for your app implementation. Android Emulator and real devices will differ subtly.
{% endhint %}

## Appfigurate Simulator&#x20;

### 1. Generate a new app metadata record

Tap the burger menu `≡`, `Add app`.

Select your app type, e.g. `iOS`. Tap the `Next` button to continue.

For the URL Scheme, we suggest appending a version number, or incrementing the version number to differentiate the old and new app key versions. e.g. `quickstart` ‣ `quickstart1`.

Tap `Add` to generate a random 2048 bit RSA private key.

### 2. Output the new public key

Tap the burger menu `≡`, `quickstart1`

The edit app screen is displayed. Scroll to the correct library integration section, depending upon your app's implementation language (e.g Swift, Objective-C, Kotlin, Java).

Tap `Output Implementation`, then tap `Console`.

The output will appear in the macOS `Console.app`. Select the iOS Simulator device in the left hand pane. Type `process:appfigurate` in the search box in the top right. Copy the output public key method. e.g.

{% hint style="info" %}
**Note:** your public key output to the Console in will be different to the public key in the following example.
{% endhint %}

```swift
   override func publicKey() -> String {
        // E4 8B B6 25 EE 01
        return "-----BEGIN PUBLIC KEY-----\n" +
            "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnD67fMex1KkP7kltlNaO\n" +
            ...
            "EQIDAQAB\n" +
            "-----END PUBLIC KEY-----\n";
    }
```

## Xcode

### 3. Update your APLConfiguration subclass' publicKey method

Open your [<mark style="color:blue;">`APLConfiguration`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html) subclass file in Xcode.

Paste the new [<mark style="color:blue;">`publicKey`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)publicKey) method output to the console over the top of the old [<mark style="color:blue;">`publicKey`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)publicKey) method.

### 4. Update Encrypted Strings

If you have any [Encrypted Strings](/configuration-subclasses/supported-property-types/encrypted-string) in your subclass, using the [Xcode source editor extension](/appfigurate-se-user-guide/xcode-source-editor-extension) is the easiest way to re-encrypt them all at once with the new public key.&#x20;

{% hint style="info" %}
If you have any Encrypted Strings and you neglect to perform this step, you wont be able to configure any app's built in `Release` mode. (Strings encrypted with the old public key will fail to be decrypted with the new private key).
{% endhint %}

In Xcode, Tap `Editor` menu ‣ `Appfigurate SE` ‣ `Encrypt strings`. All instances of `ENCRYPTED_STRING` will be updated automatically with the generated ciphertext using the new public key.

### 5. Update Info.plist URL Scheme

In your apps `Info.plist` file (right click, Open As ‣ Source Code). Change `CFBundleURLSchemes` value from `quickstart` to `quickstart1`.

```xml
<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleURLName</key>
        <string>appfigurate.quickstart</string>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>quickstart1</string>
        </array>
    </dict>
</array>
```

Build and run your app.

{% hint style="success" %}
All key rotation tasks are now complete. You can now use the `quickstart` row in Appfigurate to configure any earlier version of your app, and `quickstart1` row to configure the new version of your app.
{% endhint %}


# iOS native app automation testing

{% hint style="info" %}
Appfigurate doesn't currently support automation testing watchOS, Flutter or React Native apps.
{% endhint %}

Appfigurate can change the configuration of an iOS app being automation tested using `XCTestCase`.

## Examples

An example UI testing bundle `AppfigurateExampleUITests` is available in both [Objective-C example](/getting-started/examples#objective-c-example) and [Swift example](/getting-started/examples#swift-example) GitHub repositories. To test, ensure `AppfigurateExample (ObjC/Swift iOS)` is selected as the scheme, then long tap the run button to show more options and tap `Test` (⌘U).

<img src="/files/vmjBATBxJq73nMFF8CfA" alt="" data-size="original">

## Testing your app

### Add APLConfiguration subclass to UI testing bundle

Tap on your [<mark style="color:blue;">`APLConfiguration`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html) subclass in the project navigator. In File inspector ‣ Target membership, tick on your UI testing bundle.

![](/files/QUvP0jLVa6Jr494ZlZC3)

### Ensure allowInvalidSignatures returns YES/true

In your [<mark style="color:blue;">`APLConfiguration`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html) subclass, confirm that your [<mark style="color:blue;">`allowInvalidSignatures`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)allowInvalidSignatures) method returns `YES`/`true` when running automation tests. (Test schemes are by default run with a DEBUG build).

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

```swift
@objcMembers class ExampleConfiguration: APLConfiguration {
...
  override func allowInvalidSignatures() -> Bool {
    return !ENCRYPTED()
  }
...
```

{% endtab %}

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

```objectivec
@implementation ExampleConfiguration
...
- (BOOL) allowInvalidSignatures {
#if DEBUG
    return YES;
#else
    return NO;
#endif
}
...
```

{% endtab %}
{% endtabs %}

### Add an UIInterruptionMonitor

When the configuration is applied to the application on launch, the standard Appfigurate 'Configuration applied' alert is displayed. It is recommended you add an `UIInterruptionMonitor` to your `XCTestCase` to automatically dismiss this alert. A good place to do this in the `setUp` method.

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

```swift
addUIInterruptionMonitor(withDescription: "Appfigurate") { (element) -> Bool in
  if (element.elementType == .alert) {
    if (element.buttons["OK"].exists) {
      element.buttons["OK"].tap()
      return true
    } else if (element.buttons["Ignore"].exists) {
      element.buttons["Ignore"].tap()
      return true
    }
  }
  return false
}
```

{% endtab %}

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

```objectivec
[self addUIInterruptionMonitorWithDescription: @"Appfigurate" handler: ^(XCUIElement *element) {
    if (element.elementType == XCUIElementTypeAlert) {
        if ([element.buttons[@"OK"] exists]) {
            [element.buttons[@"OK"] tap];
            return YES;
        } else if ([element.buttons[@"Ignore"] exists]) {
            [element.buttons[@"Ignore"] tap];
            return YES;
        }
    }
    return NO;
}];
```

{% endtab %}
{% endtabs %}

### Create instance of APLConfiguration subclass

* Get an instance of your [<mark style="color:blue;">`APLConfiguration`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html) subclass in your `XCTestCase`. A good place to do this in the `setUp` method.
* Set the properties required to be applied to your app to allow it to be tested correctly.

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

```swift
let c = APLConfiguration.shared() as! ExampleConfiguration
c.boolean = false
c.string_Textfield = "thursday"
```

{% endtab %}

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

<pre class="language-objectivec"><code class="lang-objectivec"><strong>ExampleConfiguration* c = (ExampleConfiguration*) [APLConfiguration sharedConfiguration];
</strong><strong>c.boolean = NO;
</strong><strong>c.string_Textfield = @"thursday";
</strong></code></pre>

{% endtab %}
{% endtabs %}

### Apply configuration to XCUIApplication

* Apply the result of [<mark style="color:blue;">`APLConfiguration`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html) [<mark style="color:blue;">`automationLaunchArguments`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)automationLaunchArguments) method to `XCUIApplication launchArguments` property.
* Launch your app.

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

```swift
let app = XCUIApplication()
app.launchArguments = c.automationLaunchArguments()
app.launch()
```

{% endtab %}

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

```objectivec
XCUIApplication* app = [XCUIApplication new];
app.launchArguments = [c automationLaunchArguments];
[app launch];
```

{% endtab %}
{% endtabs %}

Once your app is launched, the configuration you set in [Create instance of APLConfiguration subclass ](#create-instance-of-aplconfiguration-subclass)will be applied before executing each test case.

### Additional automation launch methods

See also [<mark style="color:blue;">`automationLaunchArgumentsReset`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)automationLaunchArgumentsReset) and [<mark style="color:blue;">`automationLaunchArgumentsWithAction`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)automationLaunchArgumentsWithAction) methods of [<mark style="color:blue;">`APLConfiguration`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html).

## Applying configuration at runtime to the app under test

You can apply configuration to the app after it has launched using the [<mark style="color:blue;">`automationSendConfiguration`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)automationSendConfiguration), [<mark style="color:blue;">`automationSendConfigurationReset`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)automationSendConfigurationReset) and [<mark style="color:blue;">`automationSendConfigurationWithAction`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)automationSendConfigurationWithAction:) methods of [<mark style="color:blue;">`APLConfiguration`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html). You can read the configuration from the app under test using the [<mark style="color:blue;">`automationSendReadConfiguration`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)automationSendReadConfiguration) method.

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

```swift
func testSendConfig() {
  app.launchArguments = c.automationLaunchArgumentsReset()
  app.launch()
  c.boolean = true
  c.automationSendConfiguration()
  ...
```

{% endtab %}

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

```objectivec
- (void)testSendConfig {
    app.launchArguments = [c automationLaunchArgumentsReset];
    [app launch];
    c.boolean = YES;
    [c automationSendConfiguration];
    ...
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
Under the hood, the [<mark style="color:blue;">`automationSendConfiguration`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)automationSendConfiguration), [<mark style="color:blue;">`automationSendConfigurationReset`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)automationSendConfigurationReset), [<mark style="color:blue;">`automationSendConfigurationWithAction`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)automationSendConfigurationWithAction:) and [<mark style="color:blue;">`automationSendReadConfiguration`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)automationSendReadConfiguration) methods use the [<mark style="color:blue;">`APLAutomationSendMessage`</mark>](https://www.electricbolt.co.nz/api/Functions.html#/c:@F@APLAutomationSendMessage) and [<mark style="color:blue;">`APLAutomationMessageReceivedBlock`</mark>](https://www.electricbolt.co.nz/api/Functions.html#/c:@F@APLAutomationMessageReceivedBlock) functions introduced in the [next section](#invoking-functionality-in-the-app-under-test-at-runtime).
{% endhint %}

## Invoking functionality in the app under test at runtime

Appfigurate allows you to invoke functionality in the app under test, at runtime, from your `XCTestCase`. Example use cases:

* Setting mocked HTTP responses for HTTP requests in the app.
* Share mocked objects between the `XCTestCase` and app.
* Read and set the internal state of the app.

### Instrument the app under test

Use the [<mark style="color:blue;">`APLAutomationMessageReceivedBlock`</mark>](https://www.electricbolt.co.nz/api/Functions.html#/c:@F@APLAutomationMessageReceivedBlock) function in the app under test to declare a callback. The callback will be invoked anytime a message is received from the `XCTestCase`. Best practice is to wrap the function in `#if DEBUG` to ensure it's not included in an App Store build.

{% tabs %}
{% tab title="Swift" %}
App under test example

```swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
  APLApplicationDidFinishLaunchingWithOptions(launchOptions)
        
#if DEBUG
  APLAutomationMessageReceivedBlock { message, plist in
    let window = UIApplication.shared.windows.filter {$0.isKeyWindow}.first!
            
    if (message == "SetDarkMode") {
      let bool = plist as! Bool
      window.overrideUserInterfaceStyle = bool ? .dark : .light
    } else if message == "GetDarkMode" {
      let bool = window.traitCollection.userInterfaceStyle == .dark
      return bool
    }
    return nil
  }
#endif

  return true
}
```

{% endtab %}

{% tab title="Objective-C" %}
App under test example

```objectivec
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    APLApplicationDidFinishLaunchingWithOptions(launchOptions);
    
#if DEBUG
    APLAutomationMessageReceivedBlock(^id _Nullable(NSString * _Nonnull message, id  _Nullable plist) {
        UIWindow* window;
        for (window in [UIApplication sharedApplication].windows) {
            if ([window isKeyWindow]) {
                break;
            }
        }
            
        if ([message isEqualToString: @"SetDarkMode"]) {
            window.overrideUserInterfaceStyle = [plist boolValue] ? UIUserInterfaceStyleDark : UIUserInterfaceStyleLight;
        } else if ([message isEqualToString: @"GetDarkMode"]) {
            return [NSNumber numberWithBool: window.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark];
        }
        return nil;
    });
#endif
    return YES;
}
```

{% endtab %}
{% endtabs %}

### Invoke functionality from your XCTestCase

Use the [<mark style="color:blue;">`APLAutomationSendMessage`</mark>](https://www.electricbolt.co.nz/api/Functions.html#/c:@F@APLAutomationSendMessage) function in your `XCTestCase` to send a message and wait for a reply from the app under test.

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

<pre class="language-swift"><code class="lang-swift"><strong>func testSendMessageToApplicationUnderTest() {
</strong>  app.launchArguments = c.automationLaunchArgumentsReset()
  app.launch()
  APLAutomationSendMessage("SetDarkMode", true, 3.0)
  XCTAssertTrue(APLAutomationSendMessage("GetDarkMode", nil, 3.0) as! Bool)
}
</code></pre>

{% endtab %}

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

```objectivec
- (void)testSendMessageToApplicationUnderTest {
    app.launchArguments = [config automationLaunchArgumentsReset];
    [app launch];
    APLAutomationSendMessage(@"SetDarkMode", @YES, 3.0);
    XCTAssertTrue([APLAutomationSendMessage(@"GetDarkMode", nil, 3.0) boolValue]);
}
```

{% endtab %}
{% endtabs %}

{% hint style="success" %}
The [<mark style="color:blue;">`APLAutomationSendMessage`</mark>](https://www.electricbolt.co.nz/api/Functions.html#/c:@F@APLAutomationSendMessage) and [<mark style="color:blue;">`APLAutomationMessageReceivedBlock`</mark>](https://www.electricbolt.co.nz/api/Functions.html#/c:@F@APLAutomationMessageReceivedBlock) functions are compatible with both iOS Simulators and physical iOS devices.
{% endhint %}

{% hint style="info" %}
The [<mark style="color:blue;">`APLAutomationSendMessage`</mark>](https://www.electricbolt.co.nz/api/Functions.html#/c:@F@APLAutomationSendMessage) `plist` parameter accepts any object that is [property list compatible](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/PropertyLists/Introduction/Introduction.html#//apple_ref/doc/uid/10000048i), or `nil`. This also applies to the result returned from [<mark style="color:blue;">`APLAutomationMessageReceivedBlock`</mark>](https://www.electricbolt.co.nz/api/Functions.html#/c:@F@APLAutomationMessageReceivedBlock). The size of the property list once serialized, must be less than 65535 bytes, otherwise an [<mark style="color:blue;">`AppfigurateLibraryException`</mark>](https://www.electricbolt.co.nz/api/Constants.html#/c:@AppfigurateLibraryException) will be thrown from the `XCTestCase`. In practice, keep your property lists to just a few hundred bytes. Larger property lists will result in significant transmission time (tens of seconds).
{% endhint %}


# React Native API

Tested version 0.75

## function nativeValue(propertyName)

```javascript
let value = await Appfigurate.nativeValue('propertyName');
```

Introspect the native app's [<mark style="color:blue;">`APLConfiguration`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html) subclass for the `propertyName` specified. The possible types returned are: [Boolean](/configuration-subclasses/supported-property-types/boolean), [String](/configuration-subclasses/supported-property-types/plain-string) or [Number](/configuration-subclasses/supported-property-types/double). See [supported property types](/configuration-subclasses/supported-property-types) for exact conversion semantics.

## function setLogging(value)

```javascript
await Appfigurate.setLogging(true);
```

When true, Appfigurate library debugging messages will be output to the console. The default is false.

It is best practice to distribute applications via TestFlight / App Store with logging set to false. See also [APLLogging](/additional-reading/info.plist-options#apllogging) key in the [Info.plist](/additional-reading/info.plist-options) file.

## function version()

```javascript
await Appfigurate.version();
```

Returns the version of the Appfigurate library in the format `major.minor.patch`. e.g. `5.1.2`

## function description()

```javascript
await Appfigurate.description();
```

Returns the textual representation of all properties.

Property names are shortened to camel case and appended with the value, except for `NSString` (Obj-C) / `String` (Swift) properties which omit the property name.

e.g. `debugLog=5` would be returned as `DL=5`.

## function modifications()

```javascript
await Appfigurate.modifications();
```

Returns the textual representation of all properties that have non default (overridden) values.

Property names are shortened to camel case and appended with the non default value, except for `NSString` (Obj-C) / `String` (Swift) properties which omit the property name.

e.g. `userInteractionTimeout=60.0` would be returned as `UIT=60.0`.

## function saveConfiguration()

```javascript
await Appfigurate.saveConfiguration();
```

Saves the configuration persisted in the Keychain into temporary storage.

Some apps have functionality to erase the Keychain to reset apps back to *factory defaults*, which has the side effect of *removing* any Appfigurate configuration persisted in the Keychain.

## function restoreConfiguration()

```javascript
await Appfigurate.restoreConfiguration();
```

Restores the configuration from temporary storage back into the Keychain.

## function addListener(...)

Registers a callback that will be invoked every time Appfigurate has updated the configuration of the app. The `result` parameter is a `Map` with a single entry. The entries key is `'APLConfigurationUpdatedAction'` . The entries value is either `null` or a `String`. If `null`, then configuration was applied. If a `String`, then the an action was executed, and the value will be the name of the action.

```javascript
AppfigurateEvents.addListener('APLConfigurationUpdated', result => {
    // do something
});
```


# Introduction

The Appfigurate app is available on a variety of device types:  iOS Simulator and physical iOS devices. Throughout this user guide, an iOS Simulator is used, but the functionality across all device types is generally equivalent. Any differences will be highlighted.

Now jump to [Main menu](/appfigurate-user-guide/main-menu).


# Main menu

To display the main menu, tap the burger icon ≡, or drag the left edge of the screen towards the right.

<div align="left"><figure><img src="/files/EHJayn2ojGKBjRZklhJY" alt="" width="375"><figcaption></figcaption></figure></div>

The following menu items are available:

[Select app](/appfigurate-user-guide/select-app)

[Add app](/appfigurate-user-guide/add-app)

[Import app](/appfigurate-user-guide/import-app)

[Install example apps](/appfigurate-user-guide/install-example-apps)

[Settings](/appfigurate-user-guide/settings)

If you have one or more app metadata records stored in the local database, then additional menu item rows are displayed, one for each app. Tapping these rows will display the [Edit app](/appfigurate-user-guide/edit-app) screen for that app.


# Select app

Starting Appfigurate for the first time, or when you have no app metadata records in your local database will result in the following screen.

<div align="left"><figure><img src="/files/HBH729qANp4wyWSZef2O" alt="" width="375"><figcaption></figcaption></figure></div>

If you have one or more apps:

<div align="left"><figure><img src="/files/NNcACcXV47u8cuBczYy0" alt="" width="375"><figcaption></figcaption></figure></div>

A placeholder icon and title will be used until you configure the app for the first time. Once configured, the app metadata record will be updated and contain the app's actual icon, name and version number extracted from the app's [Info.plist](https://docs.electricbolt.co.nz/appfigurate-user-guide/pages/Hy9WUB9I915MyPlbnGzj#standard-info.plist-behaviour).

![](/files/oYDqaWG8Qkp0Vow4Rt2w)

## Configuring an app

Tap on an app row to configure the app. If the app is installed on the device, then:

* **iOS:** the app will be run and made visible, it's configuration read, and then swap back to Appfigurate. You *may* be prompted with a dialog in the app to 'Open Appfigurate'. Click `Yes`. (This dialog will be displayed only once).
* **watchOS:** the companion iOS app will be run and made visible. The companion iOS app will read the watchOS apps configuration, then swap back to Appfigurate. You *may* be prompted with a dialog in the app to 'Open Appfigurate'. Click `Yes`. (This dialog will be displayed only once).

If the app is not installed on the device, then a dialog will be displayed with text similar to "The app *Example* is not installed on this *iPhone Simulator*".

If the configuration was read successfully, the [Configure app](/appfigurate-user-guide/configure-app) screen is displayed.

{% hint style="success" %}
Very occasionally, your app will remain visible while trying to read the configuration, instead of swapping back to Appfigurate. In this instance, manually swap back to Appfigurate and try again. The second time will succeed.
{% endhint %}

{% hint style="info" %}
If you're attempting to configure a watchOS app, it must must be actively running in the foreground (the screen will appear at peak brightness) - the easiest way to confirm is to simply touch the screen before attempting to read or apply configuration. If the screen is dimmed, the watch is in a low power mode and the watch won't be able to communicate with it's paired iOS device.&#x20;

The following error dialog will be displayed in your companion iOS app when low power mode is in effect:

![](/files/GnubrdwpPDVtIdI6yxbt)
{% endhint %}

## Editing an app

Swipe the row from left to right to display an Edit button. Tapping the Edit button will display the [Edit app](/appfigurate-user-guide/edit-app) screen.

![](/files/q1kppLwreYmvBRVY5gUV)

## Deleting an app

Swipe the row from right to left to display a Delete button. Tapping the Delete button will first confirm that you want to delete the app, then delete the app's metadata record from the local database.&#x20;

![](/files/XTFIipmtm7VRgCl9xuhR)

{% hint style="danger" %}
**Caution:** If you haven't got a backup of the app's metadata record, then once it's deleted the app metadata record (specifically the private key) is gone forever.
{% endhint %}


# Add app

(not available in real device cloud testing services)

The Add app screen starts the flow that allows you to add a new app with a randomly generated private key.

{% hint style="info" %}
**Hint:** If you already have an existing iOS app metadata record, and you want your watchOS app to have the same public/private keypair, then use the [Duplicate app](/appfigurate-user-guide/edit-app#duplicate-app) function instead.&#x20;
{% endhint %}

<div align="left"><figure><img src="/files/WDbwHdUOHQXVDufSVfuH" alt="" width="375"><figcaption></figcaption></figure></div>

Choose one of the following:

* **iOS:** If you have an iOS app (or iPadOS) app and optionally an app extension.
* **watchOS:** If you have a companion watchOS app. The URL scheme of the watchOS app must be the same as the containing iOS app, as the iOS app is used to proxy the configuration payloads to the watchOS app.

Tap the `Next` button to continue.

Enter the unique URL Scheme that will be used to launch your app. Whilst we suggest using the auto-generated value, you can enter any 4-20 character string.

* **iOS:** The URL Scheme you enter here will need to be entered into your app's [Info.plist CFBundleURLTypes](https://docs.electricbolt.co.nz/appfigurate-user-guide/pages/15YH8YZPl3k8bnWiWxdw#edit-info.plist).
* **watchOS:** The URL Scheme you enter here just needs to match the URL Scheme you use for the containing iOS app, as your iOS app is used to proxy the configuration payloads to the watchOS app.

<div align="left"><figure><img src="/files/dr2i2fPxXKVDsuyMaEll" alt="" width="375"><figcaption></figcaption></figure></div>

Tap `Add` to generate a random 2048 bit RSA private key. This can take a few seconds. Once generated your app metadata record will be saved into the local database and the [Select app](/appfigurate-user-guide/select-app) screen displayed.


# Import app

(not available in real device cloud testing services)

The Import app screen allows you to import one or more app metadata records that were previously exported or backed up. You will need to know the passphrase that was used to export or back up the app metadata record(s) in order to decrypt and import them.

<div align="left"><figure><img src="/files/ohUqjFeMrxqQe02CPCcP" alt="" width="375"><figcaption></figcaption></figure></div>

The available import methods differ depending on your device type:

## Import method types

### Clipboard

You can import app metadata record(s) via the system clipboard, by selecting the contents of a previously exported `.apc` file.

### macOS file system

On a iOS Simulator, you can select a `.apc` file from the host macOS file system.

<div align="left"><figure><img src="/files/RQCxbZW9pGdKsswzEHry" alt="" width="375"><figcaption></figcaption></figure></div>

### iOS Document Picker

On an iOS Simulator or physical device you use the system document picker to select a `.apc` file from the device's local file system or iCloud Drive.

<div align="left"><figure><img src="/files/4hbl8Lxusst6sZDDzGFf" alt="" width="375"><figcaption></figcaption></figure></div>

### Drag and drop

On an iOS Simulator you can drag a `.apc` file from the host macOS file system on top of the iOS Simulator. Appfigurate will display the Import app screen automatically with an additional row `Shared with Appfigurate`.&#x20;

### QR Code

On a physical iOS device, you can use the camera to take a photo to import a QR code that is displayed on another Appfigurate instance that is using the export function.

## Select apps to import

Once you have selected a .apc file to import, the following screen will be displayed, allowing you to tick one or more apps to selectively import. You will not be able to import an app with the same type (iOS, watchOS) and URL Scheme of one that already exists in the local database.

<div align="left"><figure><img src="/files/Pb7ucXwlBZCKXVfQJ2qr" alt="" width="375"><figcaption></figcaption></figure></div>

Tapping `Next` will prompt you to enter the passphrase to decrypt the app metadata record(s).

<div align="left"><figure><img src="/files/Jcy1Aan9gELjZz69ltkh" alt="" width="375"><figcaption></figcaption></figure></div>

Entering the correct passphrase and tapping `Import` will import the apps into the local database and display the [Select app](/appfigurate-user-guide/select-app) screen.


# Install example apps

(not available in real device cloud testing services)

The [example apps](/getting-started/examples) provided in the SDK require the correct app metadata records to be installed in Appfigurate. Tap `Install example apps`. The iOS and watchOS example app metadata records will be stored into the local database.&#x20;

{% hint style="danger" %}
**Security warning**. Do not under *any* circumstances use the example app metadata record for your own apps. Always create your own app metadata record using [Add app](/appfigurate-user-guide/add-app).
{% endhint %}


# Settings

(not available in real device cloud testing services)

The Settings screen displays additional options:

[Passcode Lock](/appfigurate-user-guide/settings/passcode-lock)

[Restore](/appfigurate-user-guide/settings/restore)

[Backup](/appfigurate-user-guide/settings/backup)

[Delete all apps and settings](/appfigurate-user-guide/settings/delete-all-apps-and-settings)

Additional settings are available outside of the app:

[Analytics](/appfigurate-user-guide/settings/analytics)


# Passcode Lock

(not available in real device cloud testing services, iOS Simulator builds)

{% hint style="info" %}
Also see [Security Best Practices](/additional-reading/security/best-practice#protecting-your-app-metadata-records).
{% endhint %}

Passcode Lock allows for a 4 digit PIN on launch and when exporting app metadata records.

<div align="left"><figure><img src="/files/FqJtBmfNfzlWazZQh26c" alt="" width="375"><figcaption></figcaption></figure></div>

## Passcode on launch

To enable a 4 digit PIN on launch of Appfigurate, tap the switch on. The 4 digit PIN overlay screen will be displayed:

<div align="left"><figure><img src="/files/7xX1eDD7qQGZwcGQtdCU" alt="" width="375"><figcaption></figcaption></figure></div>

Enter a 4 digit PIN. You'll be prompted to confirm the 4 digit PIN a second time. Once matching, the PIN will be set.&#x20;

Anytime you now launch Appfigurate or attempt to enter the Passcode Lock screen you will be prompted for the 4 digit PIN.

{% hint style="danger" %}
**Warning:** If you enter your 4 digit PIN incorrectly 5 times, Appfigurate will delete the local database and reset back to factory defaults, and the [Select app](/appfigurate-user-guide/select-app) screen displayed. If you don't have a backup of your app metadata records they will be lost.
{% endhint %}

{% hint style="info" %}
**Note:** the 4 digit PIN overlay screen will not appear on top of system provided screens such as the iOS Document Picker. If you are using one of these screens and then leave the app and come back, the 4 digit PIN overlay screen will **not** appear.
{% endhint %}

## Biometrics on launch

If you have enabled 4 digit PIN on launch, an additional row *may* appear that allows Biometrics on launch to be enabled:

* **iOS:** TouchID or FaceID must be enrolled.

## Passcode to export and backup

You can set a 4 digit PIN when attempting to export or backup app metadata records.


# Restore

(not available in real device cloud testing services)

The Restore screen allows you to import one or more app metadata records that were previously exported or backed up. You will need to know the passphrase that was used to export or back up the app metadata record(s) in order to decrypt and import them.

The Restore screen has identical functionality to the [Import app](/appfigurate-user-guide/import-app) screen.


# Backup

(not available in real device cloud testing services)

You can export/backup all **exportable** app metadata records so you can either share them to another Appfigurate instance, or for safe keeping.

<div align="left"><figure><img src="/files/3ghIqr8EilVAB0DGSAvN" alt="" width="375"><figcaption></figcaption></figure></div>

Enter a passphrase used to encrypt the app metadata records. The passphrase must be 8 to 64 characters in length. Tap the `Backup` button to continue.

## Output method types

The available output methods differ depending on your device type:

<div align="left"><figure><img src="/files/G0qGo4trvO4rLWvDJx5t" alt="" width="375"><figcaption></figcaption></figure></div>

### Console

You can export app metadata record(s) to the console. In macOS open `Console.app`. Select the iOS Simulator device in the left hand pane. Type `process:appfigurate` in the search box in the top right. Tap `Start` to start streaming events. Due to the length restrictions per log, you'll need to select all the `Appfigurate x of y:` records and paste them into a text editor.

![](/files/FjDo43VIVl0VNk7Xx6ly)

### macOS file system

On a iOS Simulator, you can select a directory from the host macOS file system to store the resulting .`apc` file containing the encrypted app metadata record(s).

<div align="left"><figure><img src="/files/fS2QThNUSL2Os7R43L1T" alt="" width="375"><figcaption></figcaption></figure></div>

### Clipboard

You can export app metadata record(s) to the iOS clipboard. On an iOS Simulator the clipboard is shared with the macOS host.

### Mail

On a device with an email account, you can email the resulting `.apc` file containing the encrypted app metadata record(s) to a recipient.

{% hint style="warning" %}
**Tip:** Don't send the passphrase in the same email as the `.apc` file.
{% endhint %}

### Message

On a device with a text messaging app, you can send the resulting `.apc` file containing the encrypted app metadata record(s) to a phone number.

### iOS Document Picker

On an iOS Simulator or physical device you can use system document picker to select a directory in the device's local file system or iCloud Drive to store the resulting `.apc` file containing the encrypted app metadata record(s).

<div align="left"><figure><img src="/files/2eBt7qhUSgwg9kCI2Dmk" alt="" width="375"><figcaption></figcaption></figure></div>

### QR code

The QR code option is available when [exporting](/appfigurate-user-guide/edit-app#export-share-app) a single app metadata record. For best results, place the device on a flat stable surface, ensuring no bright overhead lights or reflections are visible on the screen. Another device with a camera can Import using QR code function.

<div align="left"><figure><img src="/files/bqRCbzWupWFAeJ4eBpiI" alt="" width="375"><figcaption></figcaption></figure></div>

### iOS Share Sheet

On an iOS Simulator or physical device you can use the iOS Share Sheet to share or save the resulting `.apc` file containing the encrypted app metadata record(s).

<div align="left"><figure><img src="/files/FAmQP9XCIudK5XcDLYac" alt="" width="375"><figcaption></figcaption></figure></div>


# Delete all apps and Settings

(not available in real device cloud testing services)

To delete the local database and reset back to factory defaults, tap `Delete all apps and settings`. You'll be prompted to confirm you want to perform this action. If you don't have a backup of your app metadata records they will be lost. The [Select app](/appfigurate-user-guide/select-app) screen will automatically be shown after performing the function.


# Analytics

We use 3rd party analytics software 'Google Analytics' when you use the app, which is covered under our [privacy policy](/legal/privacy-policy). A setting is available for you to disable the use of analytics from the device's home screen before the app is run for the first time. Please consider leaving analytics enabled so we can identify the most used features.

## iOS

Settings are available under Home ‣ Settings ‣ Appfigurate.

![](/files/sc9eDGgtvjBdHbd9MSwc)


# Edit app

(editing and export functions not available in real device cloud testing services)

The Edit app screen allows the developer of the app to perform some administration and integration functions on the app metadata record.

<div align="left"><figure><img src="/files/kCY3e1NHxFcEjI8NDTQq" alt="" width="375"><figcaption></figcaption></figure></div>

## Header

The header displays the app's icon, title and the hash of the public key.&#x20;

{% hint style="info" %}
The hash of the public key is also output using one of the `Output implementation` rows. For instance:

```swift
override func publicKey() -> String {
  // 41 36 87 71 0D 05
  return "-----BEGIN PUBLIC KEY-----\n" +
  ...
```

{% endhint %}

If your app metadata record is not exportable (the exportable switch is off), then the header will display the number of days remaining before the app metadata record expires:

![](/files/jIF6YLD3nJpLG24C2NZg)

Expired app metadata records cannot be used to configure apps.

## Exportable

The exportable switch when on (the default value after creation) indicates that this app metadata record can be exported/backed up. When the exportable switch is off the app metadata record cannot be exported/backed up. You can only change the exportable switch from on to off. A confirmation dialog will be displayed to ensure you want to perform this operation.

{% hint style="danger" %}
**Caution:** Ensure you have a backup of the app metadata record before you switch exportable to off. Once exportable is switched off it cannot be switched back on.

Ensure you read the [best practice](/additional-reading/security/best-practice) guide for app metadata records.
{% endhint %}

## URL Scheme

If required, the URL Scheme that is used to launch your app in order to retrieve, apply or reset configuration can be modified. The application type (iOS, watchOS) and the URL Scheme must be unique in the local database, otherwise an error dialog is displayed.

## Select app

Tap the Select app row to configure the app. If the app is installed on the device, then the app will be run and made visible, configuration read, then swap back to Appfigurate. You may be prompted with a dialog in the app to 'Open Appfigurate'. Click `Yes`. (This dialog will be displayed only once).

If the app is not installed on the device, then a dialog will be displayed with text similar to "The app *Example* is not installed on this *iPhone Simulator*".

If the configuration was read successfully, the [Configure app](/appfigurate-user-guide/configure-app) screen is displayed.

## Delete app

Tapping the Delete button will first confirm that you want to delete the app, then delete the app's metadata record from the local database.&#x20;

{% hint style="danger" %}
**Caution:** If you haven't got a backup of the app's metadata record, then once it's deleted the app metadata record (specifically the private key) is gone forever.
{% endhint %}

## Export/Share app

You can export/share any **exportable** app metadata records so you can either share them to another Appfigurate instance, or for safe keeping.

<div align="left"><figure><img src="/files/2287CSxno8Ck46R8zpuL" alt="" width="375"><figcaption></figcaption></figure></div>

Enter a passphrase used to encrypt the app metadata record. The passphrase must be 8 to 64 characters in length.&#x20;

The Exportable switch is off by default, meaning that the app metadata record when imported into another device will not be able to be exported/backed up from that device.

If the Custom list items options switch is on, then any custom list items the user has entered in the app will be persisted in the app metadata record, allowing them to be imported into another device.

Tap the `Next` button to continue.

The Export screen has identical functionality to the [Backup](/appfigurate-user-guide/settings/backup) screen.

## Duplicate app

If you already have an existing iOS metadata record, and you want your new watchOS to have the same public/private keypair, then use the Duplicate app function instead.&#x20;

<div align="left"><figure><img src="/files/RqGBKiY0BnHLxYMawSyL" alt="" width="375"><figcaption></figcaption></figure></div>

## Library integration

The library integration section outputs sample app integration snippets including:

* [Swift](/getting-started/ios-native-app-integration#swift) implementation, UIAppDelegate, UIWindowSceneDelegate and Info.plist
* [Objective-C](/getting-started/ios-native-app-integration#objective-c) headers, implementation, UIAppDelegate, UIWindowSceneDelegate and Info.plist
* [Flutter](/getting-started/flutter-integration) implementation


# Configure app

The Configure app screen displays the current configuration received from your app.

<div align="left"><figure><img src="/files/Puzf9PFBSaXaEAdDzlqn" alt="" width="375"><figcaption></figcaption></figure></div>

## Header

The app's embedded library version number is displayed. If the library version is less than the Appfigurate version, a warning icon is displayed. If the library version is equal to the Appfigurate version, then a green tick is displayed.

If the app being configured is a debug build (the `APLConfiguration` subclass method [`allowInvalidSignatures`](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)allowInvalidSignatures) returned true), then the text `Debug build - App allows invalid signature` is displayed with a warning icon.&#x20;

`Signature is valid` is displayed with a green tick if the correct private key was used to sign the configuration payload.

## Configurable properties

The configurable properties in the app are displayed on screen and are modifiable:

* [Boolean properties](/configuration-subclasses/supported-property-types/boolean) are represented by a [switch](/configuration-subclasses/supported-property-types/boolean#switch-ui).
* [Integer properties](/configuration-subclasses/supported-property-types/integer) are represented by a [slider](/configuration-subclasses/supported-property-types/integer#slider-ui), [text field](/configuration-subclasses/supported-property-types/integer#editable-ui), a [list](/configuration-subclasses/supported-property-types/integer#list-ui) or an [editable list](/configuration-subclasses/supported-property-types/integer#editable-list-ui) with custom items.
* [Float ](/configuration-subclasses/supported-property-types/float)and [Double properties](/configuration-subclasses/supported-property-types/double) are represented by a [slider](/configuration-subclasses/supported-property-types/double#slider-ui), [text field](/configuration-subclasses/supported-property-types/double#editable-ui), a [list](/configuration-subclasses/supported-property-types/double#list-ui) or an [editable list](/configuration-subclasses/supported-property-types/double#editable-list-ui) with custom items.
* [String properties](/configuration-subclasses/supported-property-types/plain-string) are represented by a [text field](/configuration-subclasses/supported-property-types/plain-string#editable-ui), a [list](/configuration-subclasses/supported-property-types/plain-string#list-ui) or an [editable list](/configuration-subclasses/supported-property-types/plain-string#editable-list-ui) with custom items.
* [Encrypted string properties](/configuration-subclasses/supported-property-types/encrypted-string) are represented by an [editable list](/configuration-subclasses/supported-property-types/encrypted-string#editable-list-ui) with custom items.

### Property options

If the app is compiled as a release build, any [encrypted string](/configuration-subclasses/supported-property-types/encrypted-string) properties are displayed with an encrypted <img src="/files/aJwyY8YZKXm1G4P4SgZH" alt="" data-size="line"> tag.

Any properties that require the app to be restarted after being changed are displayed with a restart  <img src="/files/Ftza5ZydsIwnt8HUdhTD" alt="" data-size="line"> tag.

## List

Tapping on a configurable property that is a list, or an editable list with custom items shows the following screen:

<div align="left"><figure><img src="/files/IIBUY0J73HbMp6ZzYkOO" alt="" width="375"><figcaption></figcaption></figure></div>

The currently selected list item is displayed with a tick ✓. To select a new item, tap any item in the list; the list screen will be dismissed automatically. Long press an item to copy the value onto the iOS or Android clipboard.

## Editable list

If the list is editable, then the `Add custom item` button is displayed. Tapping the button will display the following screen:

<div align="left"><figure><img src="/files/DCbt1kzTbskCNsQkSi6S" alt="" width="375"><figcaption></figcaption></figure></div>

{% hint style="info" %}
If there is text in the iOS clipboard, then the clipboard contents are automatically pasted into the value field. The first numeric range is highlighted automatically.
{% endhint %}

Enter a name and value. The value must conform to the property's minimum and maximum values (if applicable) and regular expression (if applicable). If the value does not conform, the field will be displayed bold red, and the `Add` button will be disabled.

On the list screen, custom items are displayed below predefined items hardcoded in your app:

<div align="left"><figure><img src="/files/0Y2tDCObmIzuIXHFagg4" alt="" width="375"><figcaption></figcaption></figure></div>

## Applying configuration

When you are ready to apply the selected configuration to your app, tap the `Apply ˅` toolbar button. Your app will be launched, and it's configuration updated.

### Apply options

Long press the `Apply ˅` toolbar button or drag from the right edge of the screen towards the left to open the Apply menu.

<div align="left"><figure><img src="/files/O9ShtTzWDWExr2gAbV4c" alt="" width="375"><figcaption></figcaption></figure></div>

The `Apply to this device` menu item performs identically to tapping the `Apply ˅` toolbar button.

The `Apply invalid to this device` menu item, generates a random private key, which is then used to sign the configuration payload. Your app will then be launched. If your app's `APLConfiguration`  subclass [`allowInvalidSignatures`](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)allowInvalidSignatures) method returns true, then the app's configuration will be updated. If the method returns false, then the configuration will not be updated. This can be useful in testing your apps release build to ensure it only accepts valid signatures.

The `Reset this device` menu item resets your app's configuration back to factory defaults (the values specified in your `APLConfiguration` subclass [`reset`](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)reset) method).


# Permissions

The iOS app uses the following permissions. Denying permissions may result in the app becoming non functional.

#### Network

Standard iOS permissions granted to Appfigurate at install time. Appfigurate uses the network in the following scenarios:

* For Google Analytics (optional - see [privacy policy](/legal/privacy-policy)).
* When you import or export app metadata depending on the [method chosen](/appfigurate-user-guide/import-app#android-file-picker).

#### Camera

Appfigurate requires this permission when you use the [QR code](/appfigurate-user-guide/import-app#qr-code) app metadata import functionality.

#### FaceID

Appfigurate requires this permission to provide the [biometrics on launch](/appfigurate-user-guide/settings/passcode-lock#biometrics-on-launch) security feature.


# Introduction

[AppfigurateSE macOS](#macos-installation) app, providing:

* One-click installation to [iOS Simulator](/appfigurate-se-user-guide/setup-ios-simulator-app)
* One-click installation to [real device cloud testing services](/appfigurate-se-user-guide/real-device-cloud-testing-services)
* [Manual encryption](/appfigurate-se-user-guide/manual-encryption) of strings
* [Xcode source editor extension](/appfigurate-se-user-guide/xcode-source-editor-extension) for automated string encryption

## macOS Installation

AppfigurateSE is [available](/getting-started/getting-started-and-downloads#appfiguratese-macos-and-windows-app) in an arm64 binary for Apple Silicon based Mac computers.

Double click the `AppfigurateSE-[version]-arm64.dmg` to open the disk image. You must accept the [license agreement](/legal/license-agreement) to continue:

<figure><img src="/files/DXNcQ6LQjbRfJ7KzrZNf" alt=""><figcaption><p>macOS AppfigurateSE license agreement</p></figcaption></figure>

&#x20;Drag the AppfigurateSE app icon into the Applications folder:

<figure><img src="/files/v38AdEb51FMxRAc9PL18" alt=""><figcaption><p>macOS AppfigurateSE install</p></figcaption></figure>

## Running

Run the application by double clicking the AppfigurateSE app in the Applications folder:

<figure><img src="/files/a86ZojlBHws2xyvCJiX9" alt=""><figcaption><p>macOS AppfigurateSE app</p></figcaption></figure>


# Manual encryption

{% hint style="info" %}
It is highly recommended to use the [Xcode source editor extension](/appfigurate-se-user-guide/xcode-source-editor-extension) to automate the encryption of strings.
{% endhint %}

If you have [encrypted string](/configuration-subclasses/supported-property-types/encrypted-string) properties in your `APLConfiguration` subclass, then choose the most appropriate guide(s) to follow:

* iOS app with or without a watchOS companion app. Follow the [`ENCRYPTED_STRING` macro/function](/appfigurate-se-user-guide/manual-encryption/encrypted_string-macro-function) guide.
* Mobile Flutter app. The flutter `APLNativeConfiguration` class defers to the underlying platform `APLConfiguration` subclass to read encrypted string properties. Choose the most appropriate guide(s) above to follow.
* Mobile React Native app. The [Appfigurate.nativeValue](/api/react-native-api#function-nativevalue-propertyname) method defers to the underlying platform `APLConfiguration` subclass to read encrypted string properties. Choose the most appropriate guide(s) above to follow.

<figure><img src="/files/WqzEaMZt3FbUz5t7Ggbl" alt=""><figcaption></figcaption></figure>


# ENCRYPTED\_STRING macro/function

Use the `ENCRYPTED_STRING` macro/function for each list item in the [`ENCRYPTED_STRING_PROPERTY_LIST_EDIT`](/configuration-subclasses/supported-property-types/encrypted-string#nsstring-editable-list-implementation) (Objective-C) macro or [`@EncryptedStringPropertyListEdit`](/configuration-subclasses/supported-property-types/encrypted-string#string-editable-list-implementation) (Swift) property wrapper.

### Step 1. Copy the public key

<img src="/files/ZEXn7ysNc9EGtlRVyNvo" alt="" data-size="line"> You can copy the multi line string including the `return` keyword and line breaks as follows (do not include any comments or `#if` macros):

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

<pre class="language-swift"><code class="lang-swift">return "-----BEGIN PUBLIC KEY-----\n" +
    "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4TZnKfGeXttN7Rr3eiAZ\n" +
    "PMEPsZvbo7lgIpMh6OjgBsoqkJJP0yXXLtpqsBCv8vm7RYqCn5+yfkiCQiXvkJBz\n" +
<strong>    "FSKmLF9EPR9l1H+32Id82dDuseD70D66puPUHjciEgmU18DpW2NVvTAykMwTEsiR\n" +
</strong>    "0h/ExBEhUe75qtwlVno8cMFbEfVtiGbKECvWIr122ED71T0Jt2Bcxqx1a7c1hPIV\n" +
    "RwLxIfWfE0+2rB9nJVPBgsTVPywibDvjio82FousyMDmvkAbMq5iyuyvJ0+5bATz\n" +
    "o12GEt5lSiQlCMzfmkWYBROMDCh27qGFVVo1XAUCVsMfsW9n4iQcoLAdUp/LI3B3\n" +
    "ywIDAQAB\n" +
    "-----END PUBLIC KEY-----\n"
</code></pre>

{% endtab %}

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

```objectivec
return @"-----BEGIN PUBLIC KEY-----\n" \
    "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4TZnKfGeXttN7Rr3eiAZ\n" \
    "PMEPsZvbo7lgIpMh6OjgBsoqkJJP0yXXLtpqsBCv8vm7RYqCn5+yfkiCQiXvkJBz\n" \
    "FSKmLF9EPR9l1H+32Id82dDuseD70D66puPUHjciEgmU18DpW2NVvTAykMwTEsiR\n" \
    "0h/ExBEhUe75qtwlVno8cMFbEfVtiGbKECvWIr122ED71T0Jt2Bcxqx1a7c1hPIV\n" \
    "RwLxIfWfE0+2rB9nJVPBgsTVPywibDvjio82FousyMDmvkAbMq5iyuyvJ0+5bATz\n" \
    "o12GEt5lSiQlCMzfmkWYBROMDCh27qGFVVo1XAUCVsMfsW9n4iQcoLAdUp/LI3B3\n" \
    "ywIDAQAB\n" \
    "-----END PUBLIC KEY-----\n";
```

{% endtab %}
{% endtabs %}

### Step 2. Paste into the `Public Key` text field

<img src="/files/9YNE8wB3VmskNCO36LR5" alt="" data-size="line"> The public key will be reformatted automatically.

### Step 3. Copy the plaintext value

<img src="/files/d3Gs5W7oNDWnKkAGctHQ" alt="" data-size="line"> Copy the plaintext value from the first parameter of the `ENCRYPTED_STRING` macro/function

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

```swift
@EncryptedStringPropertyListEdit(
    regex: #"https://[\w\.-]\.appfigurate.io/.*"#, 
    description: "server url", 
    encrypted: ENCRYPTED(), 
    restart: false, 
    values: ["Dev":ENCRYPTED_STRING("https://dev.appfigurate.io/list", // param 1 - plaintext
        "")])                                                          // param 2 - ciphertext
var url: String 
```

{% endtab %}

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

```objectivec
ENCRYPTED_STRING_PROPERTY_LIST_EDIT(
    url, 
    @"https://[\\w\\.-]+\\.appfigurate.io/.*", 
    @"server url", 
    @{@"Dev":ENCRYPTED_STRING(@"https://dev.appfigurate.io/list", // param 1 - plaintext
      @""                                                         // param 2 - ciphertext
    )}); 
```

{% endtab %}
{% endtabs %}

Copy just the plaintext value without surrounding quotes as follows:

```
https://dev.appfigurate.io/list
```

### Step 4. Paste into the `Plaintext` text field

<img src="/files/xjBYUe42UAcYmR3yJkMs" alt="" data-size="line"> The ciphertext will be computed automatically. e.g.:

```
Bj+pIgNMhv+IRdmUJw6nzhKZmD14uAwe2jz50gYHSvkbgzUkmmpm+fjFsPrprljaPhu3w7xr1KuMkcbjdDpbFBoxSsOcZ3WuxjA5mHv1h/inZpi2/cAH/UjhshPO2VQ8FkWg9shduqCEDbjMuO8ammVA5x+zg7oVTPJXbJUH1P3I49f3Sc/Lg2LpxyEXIDFtgpYq5FX1qNa3mu842OMSkJ6NVEI+CDi4hRNz7P+ie2d8Dqlm+w0cvoV91Dc94bUveQsqiAcshTkqmlxxI5uZc+oKR8pQK+RX0pEZz+U2XR4herhDV3Qd/J8YAhL4qkIpxDMaYmiW7EK0RVszlwSDf/t+jPZE0cYVq3jF/ZYHh1S2k3mFRRnyO/gYgf5+SPB6AQ==
```

{% hint style="success" %}
**Note:** due to the nature of RSA public key encryption, the ciphertext randomly changes for every modification you make.
{% endhint %}

### Step 5. Copy the ciphertext value

<img src="/files/Lu0VZDKFQV1SwNHnMnfE" alt="" data-size="line"> Copy the ciphertext value.

### Step 6. Paste into your subclass

<img src="/files/3YGB65BaQuf5X1AzG1Fv" alt="" data-size="line"> Paste the ciphertext into the second parameter of the `ENCRYPTED_STRING` macro/function

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

```swift
@EncryptedStringPropertyListEdit(
    regex: #"https://[\w\.-]\.appfigurate.io/.*"#, 
    description: "server url", 
    encrypted: ENCRYPTED(), 
    restart: false, 
    values: ["Dev":ENCRYPTED_STRING("https://dev.appfigurate.io/list", // param 1 - plaintext
        "Bj+pIgNMhv+IRdmUJw6nzhKZmD14uAwe2jz50gYHSvkbgzUkmmpm+fjFsPrprljaPhu3w7xr1KuMkcbjdDpbFBoxSsOcZ3WuxjA5mHv1h/inZpi2/cAH/UjhshPO2VQ8FkWg9shduqCEDbjMuO8ammVA5x+zg7oVTPJXbJUH1P3I49f3Sc/Lg2LpxyEXIDFtgpYq5FX1qNa3mu842OMSkJ6NVEI+CDi4hRNz7P+ie2d8Dqlm+w0cvoV91Dc94bUveQsqiAcshTkqmlxxI5uZc+oKR8pQK+RX0pEZz+U2XR4herhDV3Qd/J8YAhL4qkIpxDMaYmiW7EK0RVszlwSDf/t+jPZE0cYVq3jF/ZYHh1S2k3mFRRnyO/gYgf5+SPB6AQ==")]) // param 2 - ciphertext
var url: String
```

{% endtab %}

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

```objectivec
ENCRYPTED_STRING_PROPERTY_LIST_EDIT(
    url, 
    @"https://[\\w\\.-]+\\.appfigurate.io/.*", 
    @"server url", 
    @{@"Dev":ENCRYPTED_STRING(@"https://dev.appfigurate.io/list", // param 1 - plaintext
      @"Bj+pIgNMhv+IRdmUJw6nzhKZmD14uAwe2jz50gYHSvkbgzUkmmpm+fjFsPrprljaPhu3w7xr1KuMkcbjdDpbFBoxSsOcZ3WuxjA5mHv1h/inZpi2/cAH/UjhshPO2VQ8FkWg9shduqCEDbjMuO8ammVA5x+zg7oVTPJXbJUH1P3I49f3Sc/Lg2LpxyEXIDFtgpYq5FX1qNa3mu842OMSkJ6NVEI+CDi4hRNz7P+ie2d8Dqlm+w0cvoV91Dc94bUveQsqiAcshTkqmlxxI5uZc+oKR8pQK+RX0pEZz+U2XR4herhDV3Qd/J8YAhL4qkIpxDMaYmiW7EK0RVszlwSDf/t+jPZE0cYVq3jF/ZYHh1S2k3mFRRnyO/gYgf5+SPB6AQ==" // param 2 - ciphertext
    )});
```

{% endtab %}
{% endtabs %}


# Setup iOS Simulator app

## Requirements

In order to install Appfigurate Simulator app into an iOS Simulator, AppfigurateSE runs the Xcode `simctl` tool in:

```bash
/Library/Developer/PrivateFrameworks/CoreSimulator.framework/Versions/A/Resources/bin/simctl
```

## Install app

To install the Appfigurate Simulator app into an iOS Simulator, tap the `iOS Simulator` tab. If no iOS Simulators are detected then the following message is shown:

<figure><img src="/files/QGAeiHAiIIUNomLHfnAR" alt=""><figcaption></figcaption></figure>

Launch your chosen iOS Simulator using Xcode.

Once the iOS Simulator has launched, AppfigurateSE will display the following screen:

<figure><img src="/files/jYhJ35zBOPqHlqno5VBe" alt=""><figcaption></figcaption></figure>

Tap the `Install Appfigurate Simulator` button to install into your chosen iOS Simulator.

## Upgrade app

Any previous version of Appfigurate Simulator can be upgraded by tapping the `Upgrade Appfigurate Simulator` button.

## Uninstall app

In the iOS Simulator, from the home screen, long press the Appfigurate Simulator app icon, then tap `Remove App`.


# Xcode source editor extension

Follow the instructions on the `Xcode extension` tab to enable the source editor extension inside Xcode.

<figure><img src="/files/oUwDzrjnp9qMIk1VPhgh" alt=""><figcaption></figcaption></figure>

The source editor extension supports `APLConfiguration` subclasses written in both Objective-C and Swift.

* In Xcode, open your `APLConfiguration` subclass .m or .swift source file.
* Tap Editor ‣ Appfigurate SE ‣ Encrypt strings.
* All instances of `ENCRYPTED_STRING` will be updated automatically with the generated ciphertext.

If the source editor extension could not parse your .m or .swift source file, an error message will be displayed at the top of the Xcode source editor window. Follow these [Troubleshooting](/appfigurate-se-user-guide/xcode-source-editor-extension/troubleshooting) tips.


# Troubleshooting

The following error messages can be output from the Xcode source editor extension:

| Error                                                                   | Fix                                                                                                                                                                                                                                                                 |
| ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| File type must be either `Objective-C Source` or `Swift Source`         | The Xcode source editor extension supports Objective-C .m and Swift .swift source files. Ensure the `Type` of the source file in Xcode's `Identity and Type` inspector is either `Default - Objective-C Source` or `Default - Swift Source`.                        |
| An internal error occurred                                              | Most likely the result of the regular expression parsing routines failing. Please file an [Issue](https://github.com/electricbolt/appfiguratesdk/issues).                                                                                                           |
| `#elif TARGET_OS_WATCH` missing in publicKey method                     | You have `#if TARGET_OS_IOS` in your Objective-C source file, but the corresponding `#elif TARGET_OS_WATCH` was not found.                                                                                                                                          |
| `#elseif os(watchOS)` missing in publicKey method                       | You have `#if os(iOS)` in your Swift source file, but the corresponding `#elseif os(watchOS)` was not found.                                                                                                                                                        |
| `#endif` missing in publicKey method                                    | You have `#if TARGET_OS_IOS` and `#elif TARGET_OS_WATCH` in your Objective-C source file, but the corresponding `#endif` was not found OR you have `#if os(iOS)` and `#elseif os(watchOS)` in your Swift source file, but the corresponding `#endif` was not found. |
| No `ENCRYPTED_STRING` or `ENCRYPTED_STRING_IOS_WATCHOS` macros found    | The source file does not have any `ENCRYPTED_STRING` or `ENCRYPTED_STRING_IOS_WATCHOS` macros. Ensure you have at least 1 macro specified.                                                                                                                          |
| Both `ENCRYPTED_STRING` and `ENCRYPTED_STRING_IOS_WATCHOS` macros found | The source file has both types of `ENCRYPTED_STRING` or `ENCRYPTED_STRING_IOS_WATCHOS` macros. You may specify `ENCRYPTED_STRING` or `ENCRYPTED_STRING_IOS_WATCHOS` in your source file but not both.                                                               |
| No return statement found                                               | When parsing the `publicKey` method, for either the iOS (or watchOS) public key, the `return` statement was not found.                                                                                                                                              |
| Could not parse public key, MIIB prefix not found                       | Public key strings always start with the prefix `MIIB`. Ensure your public key string is output from the Appfigurate (Simulator or App Store) app unchanged.                                                                                                        |
| Could not parse public key, base64 decoding failed                      | The public key that was parsed could not be decoded. Ensure your public key string is output from the Appfigurate (Simulator or App Store) app unchanged.                                                                                                           |
| publicKey method not found                                              | The `- (NSString*) publicKey` method was not found. Ensure the signature of the `publicKey` method is correctly specified.                                                                                                                                          |
| publicKey method incomplete                                             | The `publicKey` method doesn't appear to be complete. Ensure you've added no additional logic or multi line comments to the method from what is output from the Appfigurate (Simulator or App Store) app.                                                           |
| publicKey method missing }                                              | Could not find the closing brace `}` of the `publicKey` method. Ensure you've added no additional logic or multi line comments to the method from what is output from the Appfigurate (Simulator or App Store) app.                                                 |

<br>


# Real device cloud testing services

{% hint style="success" %}
In order to use Appfigurate in a real device cloud testing service (as per the Appfigurate [license](/legal/license-agreement) agreement), you should purchase at least one license of Appfigurate app from the Apple App Store.
{% endhint %}

AppfigurateSE supports the following real device cloud testing services:

* [BrowserStack](/appfigurate-se-user-guide/real-device-cloud-testing-services/browserstack)


# BrowserStack

AppfigurateSE can pre-package the iOS physical device builds with your local database of app metadata records and then upload the resulting binary to BrowserStack. You can then use Appfigurate to configure your own app in BrowserStack App Live.

## Corporate proxy

If you are behind a corporate proxy, then tap the Proxy icon in the toolbar:

<figure><img src="/files/CiF8T58Uj36nHFzKayys" alt="" width="563"><figcaption><p>AppfiguateSE Proxy icon in the toolbar</p></figcaption></figure>

The proxy settings page will be displayed:

<figure><img src="/files/EBow4BSricZ8oyQdu78k" alt=""><figcaption><p>AppfigurateSE proxy settings page</p></figcaption></figure>

AppfigurateSE currently supports HTTP proxies without authentication. The proxy host and proxy port will be saved into user defaults.

## Sign in to BrowserStack

Your BrowserStack Username and Access Key can be found in BrowserStack under Account & Profile ‣ Details ‣ Authentication & Security.

<figure><img src="/files/4UOzvVG31haECcZpYva0" alt=""><figcaption><p>AppfigurateSE BrowserStack sign in page</p></figcaption></figure>

Tick on `Save credentials` to save your Username and Access Key into the Keychain. Your credentials will be automatically populated the next time you use AppfigurateSE.

If sign in is successful, the following screen is displayed. The left hand pane shows any previously uploaded Appfigurate iOS. The right hand pane displays the local database of app metadata records.&#x20;

If your local database is empty the local database of app metadata records is shared by Appfigurate Simulator app for all iOS Simulators.

<figure><img src="/files/GNCkK5VUKEwxU6Mgpfo6" alt=""><figcaption><p>AppfigurateSE upload to BrowserStack page</p></figcaption></figure>

## Uploading an Appfiguate app

Tap the `Upload Appfigurate iOS` button to pre-package the iOS physical device build with the local database's app metadata records. It can take several minutes to generate the app and upload it to BrowserStack (depending on the speed of your internet connection) .

{% hint style="info" %}
All app metadata records in the local database will be available in BrowserStack. Please read [Security Best practice](/additional-reading/security/best-practice#real-device-cloud-testing-services).
{% endhint %}

## Deleting an Appfigurate app

Swiping from right to left over any previously uploaded Appfigurate app will display a delete icon. Tapping the delete icon will delete the iOS Appfigurate pre-packaged app from BrowserStack.

<div align="center"><figure><img src="/files/KlQppEr0Dju60SvptYBF" alt="" width="320"><figcaption></figcaption></figure></div>

You can also delete a previously uploaded app in BrowserStack.

## BrowserStack uploaded apps

Uploaded pre-packaged Appfigurate apps appear in the Uploaded apps dialog in BrowserStack:

<figure><img src="/files/BbToxB93ge9JMikeTii6" alt="" width="307"><figcaption></figcaption></figure>

## Installing Appfigurate when testing your own app

When testing your own app in BrowserStack on a real device, to install Appfigurate into the same device, tap the Install New App link in the left hand menu:&#x20;

<figure><img src="/files/wDAMbMLqJtkVbdV8v5IX" alt="" width="151"><figcaption></figcaption></figure>

## Pre-packaged Appfigurate app restrictions

When the Appfigurate iOS physical device builds detect they have been pre-packaged with app metadata records and running in a real device cloud testing service, then only the [Select app](/appfigurate-user-guide/select-app), [Edit app](/appfigurate-user-guide/edit-app) and [Configure app](/appfigurate-user-guide/configure-app) screens are available. All other screens and features are automatically disabled including [Add app](/appfigurate-user-guide/add-app), [Import app](/appfigurate-user-guide/import-app), [Install example apps](/appfigurate-user-guide/install-example-apps) and  [Settings](/appfigurate-user-guide/settings).


# Archived products

{% hint style="danger" %}
This page lists products that are no longer supported. Not all products are available to be downloaded (e.g. Android app for physical devices).
{% endhint %}

#### AppfigurateSE macOS app for Intel <a href="#appfiguratese-macos-app" id="appfiguratese-macos-app"></a>

AppfigurateSE macOS app for Intel allows for easy one-click installation of the bundled iOS Simulator app, bundled Android Emulator app, iOS and Android pre-packaged apps for real device cloud testing services, manual encryption of strings.

<a href="https://github.com/electricbolt/appfiguratesdk/releases/download/4.0.2/AppfigurateSE-4.0.0-x86_64.dmg" class="button primary" data-icon="cloud-arrow-down">Mac with Intel chip - 4.0.0</a>

#### AppfigurateSE Windows app <a href="#appfiguratese-macos-app" id="appfiguratese-macos-app"></a>

AppfigurateSE windows app allows for easy one-click installation of the bundled Android Emulator app, iOS and Android pre-packaged apps for real device cloud testing services, manual encryption of strings.

<a href="https://github.com/electricbolt/appfiguratesdk/releases/download/4.0.2/AppfigurateSE-4.0.0.35.exe" class="button primary" data-icon="cloud-arrow-down">Windows with Intel chip - 4.0.0.35</a>


# License Agreement

*Last updated 12 April 2026*

## **1. Preamble**

1.1 The Appfigurate SDK (“SDK”) comprising of: (a) XCFramework, header files and examples for iOS and watchOS; and (b) Plugin and examples for Flutter; and (c) AppfigurateSE macOS app; and (d) Appfigurate Simulator app; and (e) Appfigurate pre-packaged app for iOS cloud testing services; and (f) Documentation; are licensed to you subject to the terms of this License Agreement. This License Agreement forms a legally binding contract between you and Electric Bolt in relation to your use of the SDK.

1.2 "Electric Bolt" means Electric Bolt Limited, a limited liability company registered in New Zealand.

## **2. Accepting this License Agreement**

2.1 In order to use the SDK, you must first agree to this License Agreement. You may not use the SDK if you do not accept this License Agreement.

2.2 You can accept this License Agreement by including the XCFramework or Flutter plugin into your own apps, or by using the AppfigurateSE macOS app, Appfigurate Simulator app, Appfigurate pre-packaged app for iOS cloud testing services. You agree that use of the SDK constitutes acceptance of the Licensing Agreement from that point onwards.

2.3 If you are agreeing to be bound by this License Agreement on behalf of your employer or other entity, you represent and warrant that you have full legal authority to bind your employer or such entity to this License Agreement. If you do not have the requisite authority, you may not accept the Licensing Agreement or use the SDK on behalf of your employer or other entity.

## **3. SDK License from Electric Bolt**

3.1 Subject to the terms of this License Agreement, Electric Bolt grants you a limited, worldwide, royalty-free, non-sublicenseable, non-assignable and non-exclusive license to use the SDK to include the XCFramework or Flutter plugin into your own apps.

3.2 You agree that the form and nature of the SDK that Electric Bolt provides may change without prior notice to you and that future versions of the SDK may be incompatible with apps developed on previous versions of the SDK. You agree that Electric Bolt may stop (permanently or temporarily) providing the SDK (or any features within the SDK) to you at Electric Bolt’s sole discretion, without prior notice.

3.3 You may not claim that the SDK, in whole or in part, is yours, and you may not use the name the Electric Bolt or any of their respective logos or trademarks in any manner without first obtaining the prior written permission from an authorized representative of the Electric Bolt.

3.4 Nothing in this License Agreement gives you a right to use any of Electric Bolt’s logos, trademarks, trade names, domain names, or other distinctive brand features.

3.5 You agree that you will not obscure, remove, or alter any proprietary rights notices (including copyright and trademark notices) that may be affixed to or contained within the SDK.

## **4. Usage of the SDK**

4.1 Electric Bolt agrees that it obtains no right, title or interest from you under this License Agreement in or to any apps that you develop using the SDK, including any intellectual property rights that subsist in those apps.

4.2 You agree to use the SDK and write apps only for purposes that are permitted by (a) this License Agreement and (b) any applicable law, regulation or generally accepted practices or guidelines in relevant jurisdictions.

4.3 You agree that you will only run the Appfigurate Simulator app in an iOS Simulator on an Apple Mac computer.

4.5 You agree that (a) you will only run the Appfigurate pre-packaged iOS app in a supported cloud testing service; and (b) the Appfigurate pre-packaged iOS app is installed into the cloud testing service using the AppfigurateSE macOS app and (c) you have purchased at least one license of Appfigurate app from the Apple App Store.

4.6 You agree that for apps distributed through the (Apple) App Store, you will not include any hidden features that could be enabled by your usage of the SDK without fully disclosing them to App Review.

4.7 You agree you will not reverse engineer, decompile, disassemble or otherwise attempt to derive the source code, techniques, processes, algorithms, know-how or other information from the binary code portions of the SDK (collectively, "Reverse Engineering") or permit or induce the foregoing.

4.8 You agree that you are solely responsible for (and that Electric Bolt has no responsibility to you or to any third party for) any breach of your obligations under this License Agreement, any applicable third party contract or Terms of Service, or any applicable law or regulation, and for the consequences (including any loss or damage which Electric Bolt or any third party may suffer) of any such breach.

## **5. Terminating this License Agreement**

5.1 This License Agreement will continue to apply until terminated by either you or Electric Bolt as set out below.

5.2 If you want to terminate this License Agreement, you may do so by ceasing your use of the SDK.

5.3 Electric Bolt may at any time, terminate this License Agreement with you if: (a) Electric Bolt is required to do so by law; or (b) you have breached any provision of this License Agreement; or (c) Electric Bolt decides to no longer provide the SDK or portions of the SDK, where in Electric Bolt’s sole discretion, it is no longer commercially viable to do so.

5.4 When this License Agreement comes to an end, all of the legal rights, obligations and liabilities that you and Electric Bolt have benefited from, been subject to (or which have accrued over time whilst this License Agreement has been in force) or which are expressed to continue indefinitely, shall be unaffected by this cessation, and the provisions of paragraph 10.6 shall continue to apply to such rights, obligations and liabilities indefinitely.

## **6. DISCLAIMER OF WARRANTIES**

6.1 YOU EXPRESSLY UNDERSTAND AND AGREE THAT YOUR USE OF THE SDK IS AT YOUR SOLE RISK AND THAT THE SDK IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND FROM ELECTRIC BOLT.

6.2 YOUR USE OF THE SDK AND ANY MATERIAL DOWNLOADED OR OTHERWISE OBTAINED THROUGH THE USE OF THE SDK IS AT YOUR OWN DISCRETION AND RISK AND YOU ARE SOLELY RESPONSIBLE FOR ANY DAMAGE TO YOUR COMPUTER SYSTEM OR OTHER DEVICE OR LOSS OF DATA THAT RESULTS FROM SUCH USE.

6.3 ELECTRIC BOLT FURTHER EXPRESSLY DISCLAIMS ALL WARRANTIES AND CONDITIONS OF ANY KIND, WHETHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.

## **7. LIMITATION OF LIABILITY**

7.1 YOU EXPRESSLY UNDERSTAND AND AGREE THAT ELECTRIC BOLT, ITS SUBSIDIARIES AND AFFILIATES, AND ITS LICENSORS SHALL NOT BE LIABLE TO YOU UNDER ANY THEORY OF LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL CONSEQUENTIAL OR EXEMPLARY DAMAGES THAT MAY BE INCURRED BY YOU, INCLUDING ANY LOSS OF DATA, WHETHER OR NOT ELECTRIC BOLT OR ITS REPRESENTATIVES HAVE BEEN ADVISED OF OR SHOULD HAVE BEEN AWARE OF THE POSSIBILITY OF ANY SUCH LOSSES ARISING.

## **8. Indemnification**

8.1 To the maximum extent permitted by law, you agree to defend, indemnify and hold harmless Electric Bolt, its affiliates and their respective directors, officers, employees and agents from and against any and all claims, actions, suits or proceedings, as well as any and all losses, liabilities, damages, costs and expenses (including reasonable attorneys fees) arising out of or accruing from (a) any non-compliance by you with this License Agreement, (b) any apps you develop using the SDK that infringes any copyright, trademark, trade secret, trade dress, patent or other intellectual property right of any person or defames any person or violates their rights of publicity or privacy, and (c) your use of the SDK.

## **9. Changes to the License Agreement**

9.1 Electric Bolt may make changes to the License Agreement as it distributes new versions of the SDK. When these changes are made, Electric Bolt will make a new version of the License Agreement available on the website where the SDK is made available.

## **10. General Legal Terms**

10.1 This License Agreement constitutes the whole legal agreement between you and Electric Bolt and governs your use of the SDK, and completely replaces any prior agreements between you and Electric Bolt in relation to the SDK.

10.2 If any court of law, having the jurisdiction to decide on this matter, rules that any provision of this License Agreement is invalid, then that provision will be removed from this License Agreement without affecting the rest of this License Agreement. The remaining provisions of this License Agreement will continue to be valid and enforceable.

10.3 You agree that if Electric Bolt does not exercise or enforce any legal right or remedy which is contained in this License Agreement (or which Electric Bolt has the benefit of under any applicable law), this will not be taken to be a formal waiver of Electric Bolt’s rights and that those rights or remedies will still be available to Electric Bolt.

10.4 The rights granted in this License Agreement may not be assigned or transferred by either you or Electric Bolt without the prior written approval of the other party. Neither you nor Electric Bolt shall be permitted to delegate their responsibilities or obligations under this License Agreement without the prior written approval of the other party.

10.5 EXPORT RESTRICTIONS. THE SDK IS SUBJECT TO U.S. EXPORT LAWS AND REGULATIONS. YOU MUST COMPLY WITH ALL DOMESTIC AND INTERNATIONAL EXPORT LAWS AND REGULATIONS THAT APPLY TO THE SDK. THESE LAWS INCLUDE RESTRICTIONS ON DESTINATIONS, END USERS AND END USE.

10.6 This License Agreement, and your relationship with Electric Bolt under this License Agreement, shall be governed by the laws of the New Zealand without regard to its conflict of laws provisions. You and Electric Bolt agree to submit to the exclusive jurisdiction of the courts located within New Zealand to resolve any legal matter arising from this License Agreement. Notwithstanding this, you agree that Electric Bolt shall still be allowed to apply for injunctive remedies (or an equivalent type of urgent legal relief) in any jurisdiction.

10.7 The Terms of Service constitute the entire agreement between Electric Bolt and you with respect to the subject matter hereof.


# Privacy Policy

*Last updated 3 March 2023*

Electric Bolt is a New Zealand registered, limited liability company and is bound by the Privacy Act of 1993 (the 'Act'). Electric Bolt is deemed an agency under the Act. We understand the need to safeguard your personal information and respect your rights under the Act.

From time to time when you visit electricbolt.co.nz and any sub domains (the 'Site'), or use Appfigurate™ (the 'App'), we collect, retain and use information about you that we consider will help us to operate our business and provide you with relevant services and information.

## **Who are 'We'?**

In this Privacy Policy, all references to 'we', 'our', and 'us' refer to Electric Bolt.

## **Information Collected**

We use 3rd party analytics software 'Google Analytics' when you visit the Site or use our App. The types of information collected are:

* The type of device or browser you used.
* The date and time of your visit.
* The address of any web site that referred you to the Site.
* Your device's IP address.
* Any URL of the pages on the Site you browsed to.
* Any URL schemes entered into the App.
* Any screens used in the App.

## **How We Use Your Information**

We will not sell, give away or publish your information. We only use the information that you have provided for statistical analysis.

## **Cookies**

'Cookies' are pieces of user-specific information generated by a web server and stored in the user's computer, ready for future access. A cookie does not provide us with personally identifiable information. Cookies are collected on this Site by our third-party analytics provider 'Google Analytics'.

## **Modification**

We may add to, remove or change the provisions of this Privacy Policy from time to time. All additions and changes will be notified to you by being posted on this page. It is your responsibility to refer to these additions and changes.


# Release History

## Current release

<table><thead><tr><th width="484">Product</th><th>Version</th></tr></thead><tbody><tr><td><a href="/pages/bYFnFQ0B46Ab1Vc5Rshu#appfiguratese-macos-and-windows-app">AppfigurateSE macOS app</a></td><td>5.0.0</td></tr><tr><td>    <a href="/pages/bYFnFQ0B46Ab1Vc5Rshu#appfiguratese-macos-and-windows-app">Appfigurate app for iOS Simulators</a></td><td>5.0.0</td></tr><tr><td><a href="/pages/bYFnFQ0B46Ab1Vc5Rshu#appfigurate-app-for-physical-ios-devices">Appfigurate app for physical iOS devices</a></td><td>5.0.0</td></tr><tr><td><a href="/pages/15YH8YZPl3k8bnWiWxdw#integration-methods">Appfigurate library for iOS and watchOS</a></td><td>5.0.0</td></tr><tr><td>    <a href="/pages/nNylef8LvpuLiLEpe0ml#swift-example">Swift example</a></td><td>5.0.0</td></tr><tr><td><a href="/pages/0TgR46sQ5nNofXkpAkuh#modify-pubspec.yaml">Appfigurate library for Flutter</a></td><td>5.0.0</td></tr></tbody></table>

## Release history

## 5.0.0 - 27 July 2026

{% hint style="danger" %}
Appfigurate app for physical **Android** devices, Appfigurate app for **Android** Emulator, **Android** pre-packaged apps for real device cloud testing services and the AppfigurateSE **Windows** app **have been withdrawn from sale and are no longer available**.
{% endhint %}

{% hint style="success" %}
Appfigurate app for physical **iOS** devices, Appfigurate app for **iOS** Simulator, **iOS** pre-packaged apps for real device cloud testing services and the AppfigurateSE **macOS** app continue to be fully supported, developed and maintained.
{% endhint %}

* **App:** Bug fixes.
* **App:** Appfigurate Simulator now only contains arm64 architecture. The x86\_64 architecture is no longer supported.
* **AppfigurateSE:** Now only contains arm64 architecture for Apple Silicon based Mac computers. The x86\_64 binary for Intel based Mac computers is no longer supported.
* **AppfigurateSE:** Initial compatibility with macOS 27 Beta and Xcode 27 Beta.
* **AppfigurateSE:** Bug fixes - displaying Running Simulators no longer hangs if a large number of Simulators are installed.
* **SDK:** SwiftUI enhancements to Appfigurate Library for iOS and watchOS:
  * New `.configurationView` modifier for displaying a overridden configuration label (previously you would have used a UIKit `APLConfigurationLabel`).&#x20;
  * New `APLConfigurationUpdatedNotificationName` for use in an `.onReceive` modifier (instead of/in addition to using `APLAddConfigurationUpdatedListener` and `APLAddConfigurationUpdatedBlock` methods). e.g.

```
.onReceive(NotificationCenter.default.publisher(for: APLConfigurationUpdatedNotificationName)) { _ in
  ...
}
```

* **SDK:** Appfigurate Library has been internally rewritten in Swift. This *may* require you to add an initialiser to your existing Swift `APLConfiguration` subclass. Usage of Appfigurate Library from Objective-C continues to be fully supported.
* **SDK:** SwiftExample updated to use SwiftUI.
* **SDK:** ObjCExample is deprecated and will no longer be updated (Usage of Appfigurate Library from Objective-C continues to be fully supported).
* **SDK:** watchOS minimum target raised to 8.0.
* **SDK:** Updated documentation on integrating with connected scenes.

#### Known issues

* **App:** Appfigurate Simulator on an iPad running in Windowed mode has the toolbar `< Back` button placed underneath the iPad window controls. *Workaround:* Run in full screen mode.
* **SDK:** SwiftExample compiling under Xcode 27 Beta requires you to increase the watchOS minimum target to 9.0 (as Xcode 27 no longer supports targeting watchOS 8.0).

#### Requirements

* **Xcode:** 26.4+, **Minimum deployment target:** iOS 15.0, watchOS 8.0
* **Flutter:** 3.41.4+ (Dart 3.11.1+)

## 4.0.0 - 8 Sep 2025

* **App:** Locally override [third party remote provider](/getting-started/third-party-remote-configuration-providers) configuration/feature toggles/flags. Tested and documented providers include [Firebase Remote Config](/getting-started/third-party-remote-configuration-providers/firebase-remote-config) and [Launch Darkly](/getting-started/third-party-remote-configuration-providers/launch-darkly).
* **App:** The [Configure app](/appfigurate-user-guide/configure-app) screen now displays an additional header label if the app has remote properties. The label is either "App has received remote config" or "App is using default remote config". If the label says "App is using default remote config" and you are expecting non default values to be displayed, re-run your app and give it some time to allow the third party remote configuration provider to update itself. Then try again.
* **App:** Change multiple list property values at the same time by [selecting an environment](/configuration-subclasses/environment-tags). e.g. Dev, Test, PVT.
* **App:** Properties in the [Configure app](/appfigurate-user-guide/configure-app) screen are now sorted by alphabetical order.
* **App:** Filter by value name on property list screen.
* **App:** You can no longer [Add a new watchOS app](https://docs.electricbolt.co.nz/appfigurate-user-guide/add-app). Instead [Duplicate](https://docs.electricbolt.co.nz/appfigurate-user-guide/edit-app#duplicate-app) the companion iOS app to create a new watchOS app using the same public/private keypair.
* **SDK:** Appfigurate Library no longer supports having an iOS and watchOS app using different public/private key pairs. Instead the watchOS app must be changed to use the iOS companion app's public/private key pair. The `ENCRYPTED_STRING_IOS_WATCHOS` function/macro has been removed. See the [upgrade guide](/getting-started/upgrade-guide/v3.2.1-to-v4.0.0) for migration guidance.
* **SDK:** Added [`@RemoteBoolProperty`](/configuration-subclasses/supported-property-types#remote-properties), [`@RemoteIntPropertyEdit`](/configuration-subclasses/supported-property-types#remote-properties), [`@RemoteDoublePropertyEdit`](/configuration-subclasses/supported-property-types#remote-properties) and [`@RemoteStringPropertyEdit`](/configuration-subclasses/supported-property-types#remote-properties) annotations/property wrappers to support third party remote provider configuration/feature toggles/flags.
* **SDK:** [Swift example](/getting-started/examples#swift-example), [Objective-C example](/getting-started/examples#objective-c-example), [Kotlin example](/getting-started/examples#kotlin-example) and [Java example](/getting-started/examples#java-example) repositories updated to demonstrate environment tags and third party remote configuration provider integration (Firebase Remote Config).
* **App:** Appfigurate Emulator for Android now requires a device with provisioned expanded storage (which is the default behaviour when configuring Android Virtual Devices). The database of app metadata records is migrated from internal storage to external storage on first use.
* **AppfigurateSE:** The Appfigurate Emulator for Android, Sync database feature no longer requires adb daemon root access. This allows for easier installation and testing of Google Play Services system images.

#### Requirements

* **Xcode:** 16.4+, **Minimum deployment target:** iOS 15.0, watchOS 5.0
* **Android Studio:** Narwhal 2025.1.1+,  **IntelliJ:** 2025.1+, **Minimum API level:** Android 26 (Marshmallow 8.0)&#x20;
* **Flutter:** 3.32.5+ (Dart 3.8.1+)

## 3.2.2 - 3 July 2025

* **SDK:** [Appfigurate Flutter Plugin](https://docs.electricbolt.co.nz/legal/pages/0TgR46sQ5nNofXkpAkuh#modify-pubspec.yaml) now supports Swift Package Manager (as well as the existing Cocoapods integration). Please read <https://docs.flutter.dev/packages-and-plugins/swift-package-manager/for-app-developers> to enable.
* **SDK:** [Android Flutter example](/getting-started/examples#flutter) modernised to use a Kotlin Gradle build script file.

#### Requirements

* **Xcode:** 16.4+, **Minimum deployment target:** iOS 13.0, watchOS 5.0
* **Android Studio:** Narwhal 2025.1.1+,  **IntelliJ:** 2025.1+, **Minimum API level:** Android 26 (Marshmallow 8.0)&#x20;
* **Flutter:** 3.32.5+ (Dart 3.8.1+)

## 3.2.1 - 29 November 2024

* **SDK:** iOS AppfigurateLibrary.xcframework now includes enhanced automation testing functionality:
  * [Apply and read configuration](/automation-testing/ios-native-app-automation-testing#applying-configuration-at-runtime-to-the-app-under-test) from the app under test, at runtime.
  * [Invoke functionality](/automation-testing/ios-native-app-automation-testing#instrument-the-app-under-test) in the app under test, at runtime.
* **SDK:** Appfigurate [Flutter plugin](https://docs.electricbolt.co.nz/legal/pages/0TgR46sQ5nNofXkpAkuh#modify-pubspec.yaml) for Mobile Flutter apps updated to include latest iOS AppfigurateLibrary.xcframework.
* **SDK:** [Objective-C example](/getting-started/examples#objective-c-example) and [Swift example](/getting-started/examples#swift-example) updated to demonstrate enhanced automation testing functionality.
* **SDK:** [Java example](/getting-started/examples#java-example) and [Kotlin example](/getting-started/examples#kotlin-example) updated to Android Studio Ladybird 2024.2.1 Patch 2.

## 3.2.0 - 24 July 2024

* **App:** Improved performance on the configuration screen, including refreshed design.
* **App:** Filter by property name on configuration screen.
* **App:** Access help from most screens in the app.
* **App:** General bug fixes.
* **AppfigurateSE:** Support for pre-packaging your local database of app metadata records with the iOS or Android physical device builds, and uploading it to supported [real device cloud testing services](/appfigurate-se-user-guide/real-device-cloud-testing-services) (currently BrowserStack App Live).
* **AppfigurateSE:** [Windows 10 compatible version of AppfigurateSE](/appfigurate-se-user-guide/introduction#windows-installation) is now available, which has all of the functionality of the macOS version with the exception of Apple specific features [Setup iOS Simulator app](/appfigurate-se-user-guide/setup-ios-simulator-app) and [Xcode Source Editor Extension](/appfigurate-se-user-guide/xcode-source-editor-extension).

## 3.1.4 - 9 July 2024

* **SDK:** iOS and watchOS AppfigurateLibrary.xcframework resigned (Electric Bolt Limited, KLCLPVKM8C). No other changes applied.

## 3.1.3 - 25 February 2024

* **SDK:** iOS and watchOS AppfigurateLibrary.xcframework is now signed (Electric Bolt Limited, KLCLPVKM8C) and includes an embedded [PrivacyInfo.xcprivacy](/additional-reading/security/privacyinfo.xcprivacy) file.
* **SDK:** The [Swift Example](/getting-started/examples#swift-example) and [Objective-C Example](/getting-started/examples#objective-c-example) apps now use Swift Package Manager to automatically use the latest version of AppfigurateLibrary.xcframework.
* **SDK:** Appfigurate [Flutter plugin](https://docs.electricbolt.co.nz/legal/pages/0TgR46sQ5nNofXkpAkuh#modify-pubspec.yaml) for Mobile Flutter apps updated to include latest iOS AppfigurateLibrary.xcframework.

#### Requirements

* **Xcode:** 15.2+, **Minimum deployment target:** iOS 13.0, watchOS 5.0
* **Android Studio:** Electric Eel 2022.1.1+,  **IntelliJ:** 2022.3+, **Minimum API level:** Android 26 (Marshmallow 8.0)&#x20;
* **Flutter:** 3.10.0+ (Dart 3.0.0+)

## 3.1.2 - 31 August 2023

* **App:** Bug fixes - Property with string text fields increased from 16 to 250 characters.
* **App:** Bug fixes - Catch exception thrown from decryption of incorrect ENCRYPTED\_STRING value.
* **App:** Bug fixes - Display actual decryption error on Apply App screen.
* **App:** iOS bug fixes - Crash after sending email.

#### Requirements

* **Xcode:** 14.3+, **Minimum deployment target:** iOS 13.0, watchOS 5.0
* **Android Studio:** Electric Eel 2022.1.1+,  **IntelliJ:** 2022.3+, **Minimum API level:** Android 26 (Marshmallow 8.0)&#x20;
* **Flutter:** 3.10.0+ (Dart 3.0.0+)

## **3.1.1 - 2 June 2023**

* **App:** iOS bug fixes - Applied app configuration was not being kept for 7 days.
* **App:** Android bug fixes - Reliability improvements for launching Android apps from Appfigurate.

#### Requirements

* **Xcode:** 14.2+, **Minimum deployment target:** iOS 13.0, watchOS 5.0
* **Android Studio:** Electric Eel 2022.1.1+,  **IntelliJ:** 2022.3+, **Minimum API level:** Android 26 (Marshmallow 8.0)&#x20;
* **Flutter:** 3.10.0+ (Dart 3.0.0+)

## **3.1.0 - 27 May 2023**

* **App:** Bug fixes.
* **SDK:** Support for automation testing Android apps using Espresso.
* **SDK:** Improved algorithm for launching Android apps from Appfigurate (by task id).
* **SDK:** Appfigurate Emulator for Android when running on Apple Silicon based Mac computers now only contains arm64 architecture slice for a smaller download size.
* **SDK:** Appfigurate Emulator for Android when running on Intel based Mac computers now contains additional x86\_64 architecture slice (along with existing arm64 and armv7) for better compatibility with later Android versions.
* **AppfigurateSE:** Bug fixes.

#### Requirements

* **Xcode:** 14.2+, **Minimum deployment target:** iOS 13.0, watchOS 5.0
* **Android Studio:** Electric Eel 2022.1.1+,  **IntelliJ:** 2022.3+, **Minimum API level:** Android 26 (Marshmallow 8.0)&#x20;
* **Flutter:** 3.10.0+ (Dart 3.0.0+)

## **3.0.0 - 3 March 2023**

#### **Release notes**

* **App:** Now available for Android Emulator and physical devices.
* **App:** Upgrading to Appfigurate 3.0.0 from a previous version converts the database to a new format, therefore downgrading to an earlier version is not possible.
* **App:** Appfigurate can no longer be used to apply configuration remotely.
* **App:** Multipeer import/export is no longer available. Use the [iOS Share Sheet](/appfigurate-user-guide/settings/backup#ios-share-sheet) to Airdrop `.apc` files to another device instead.
* **SDK:** The `APLConfiguration customIdentifier` method is no longer supported and you should remove any subclass implementations.
* **SDK:** New [Java](/getting-started/examples#java-example), [Kotlin](/getting-started/examples#kotlin-example) and [Flutter](/getting-started/examples#mobile-flutter) examples.
* **SDK:** New Appfigurate Library for Android, distributed in an AAR format supporting both Java and Kotlin languages.
* **SDK:** New Appfigurate [Flutter plugin](https://docs.electricbolt.co.nz/legal/pages/0TgR46sQ5nNofXkpAkuh#modify-pubspec.yaml) for Mobile Flutter apps (iOS and Android).
* **SDK:** AppfigurateLibrary.xcframework for watchOS now contains arm64 architecture alongside the existing arm64\_32 and armv7k for physical devices.
* **AppfigurateSE:** [Appfigurate Simulator for iOS](/appfigurate-se-user-guide/setup-ios-simulator-app) and Appfigurate Emulator for Android are now included in the AppfigurateSE macOS app, and have easy 1 click installation. You no longer have to build the Appfigurate Simulator app in Xcode to deploy.
* **AppfigurateSE:** Is now a [direct download](/getting-started/getting-started-and-downloads#appfiguratese-macos-app) instead of through the Mac App Store, and is using the hardened runtime and is notarised by Apple. The app is available in either a x86\_64 binary for Intel based Mac computers or an arm64 binary for Apple Silicon based Mac computers. You must download the correct binary that matches your computers architecture.

#### Known issues

* If the app icon in a watchOS app is compiled into an Assets.car file, it won't be made available to Appfigurate and the default placeholder icon will be used instead.

#### Requirements

* **Xcode:** 14.0+, **Minimum deployment target:** iOS 13.0, watchOS 5.0
* **Android Studio:** Electric Eel 2022.1.1+,  **IntelliJ:** 2022.3+, **Minimum API level:** Android 26 (Marshmallow 8.0)&#x20;
* **Flutter:** 3.3.10+ (Dart 2.18.6+)

## **2.2.1 - 30 March 2022**

#### **Release notes**

* **SDK:** Bug fix for iOS Extensions and watchOS Intent Extensions to read the current APLConfiguration from shared keychain.

#### Requirements

* **Xcode:** 13.2+, **Minimum deployment target:** iOS 10.0, watchOS 3.0

## **2.2.0 - 19 October 2021 (Updated)**

#### **Release notes**

* **Xcode:** Tested for compatibility with Xcode 13

#### Requirements

* **Xcode:** 12.4+, **Minimum deployment target:** iOS 10.0, watchOS 3.0

## **2.2.0 - 17 March 2021**

#### **Release notes**

* **SDK:** Removal of `APLConfigurationClass` from `Info.plist` for increased reliability and faster startup time of your app. You must add an implementation of the `APLConfigurationClass` function into your app as per the prototype in `Appfigurate.h`, otherwise a linker error will be issued. The suggested location is to place this function at the end of your `APLConfiguration` subclass file.
* **App:** Output Objective-C implementation now includes APLConfigurationClass() function (≡ ‣ \[App name] ‣ OBJ-C LIBRARY INTEGRATION section ‣ Output implementation).
* **App:** Output Swift implementation now includes APLConfigurationClass() function (≡ ‣ \[App name] ‣ SWIFT LIBRARY INTEGRATION section ‣ Output implementation).
* **App:** Output Info.plist snippet no longer includes APLConfigurationClass declaration (≡ ‣ \[App name] ‣ OBJ-C/SWIFT LIBRARY INTEGRATION section ‣ Output Info.plist snippet).

#### Requirements

* **Xcode:** 12.4+, **Minimum deployment target:** iOS 10.0, watchOS 3.0

## **2.1.1 - 14 March 2021**

#### **Release notes**

* **App:** Due to incomplete emulation of iOS in macOS Big Sur on Apple Silicon, Appfigurate can not be downloaded from the App Store and run as an iOS App on macOS app. Appfigurate App Store continues to function on a real iPhone, iPod or iPad device.
* **SDK:** Due to incomplete emulation of iOS in macOS Big Sur on Apple Silicon, Appfigurate Library will ignore any attempted configuration changes when *your* app is run as an iOS app on macOS. Appfigurate Library continues to function on a real iPhone, iPod, iPad or Simulator device.
* **SDK:** For `SwiftExample/Common/ExampleConfiguration.swift` removed use of `#if DEBUG` active compliation condition and replaced with `ENCRYPTED()` function. Online documentation also updated to remove Step 2: DEBUG active compilation condition.
* **SDK:** Moved SwiftExample and ObjCExample into their own repositories.

#### Requirements

* **Xcode:** 12.4+, **Minimum deployment target:** iOS 10.0, watchOS 3.0

## **2.1.0 - 27 September 2020**

#### **Release notes**

* **App:** Full Apple Silicon support - Appfigurate Simulator now contains arm64 architecture alongside existing i386 and x86\_64 architectures.
* **App:** Fix broken multipeer import/export on OS 14.

#### Requirements

* **Xcode:** 12.0+, **Minimum deployment target:** iOS 10.0, watchOS 3.0

## **2.0.2 - 17 September 2020**

#### **Release notes**

* **App:** Changes required for compatibility with iOS 14
* **SDK:** Adds support for Swift Package Manager. Existing integration technologies (Manual, Carthage, Cocoapods) continue to be available.
* **SDK:** Appfigurate Library security enhancements - digital signature verification of applied configuration via secure enclave on eligible devices.
* **SDK:** Partial Apple Silicon support - Appfigurate Library contains arm64 architecture for iOS and watchOS Simulators. A future release will support running Appfigurate Simulator on Apple Silicon Macs.

#### Requirements

* **Xcode:** 12.0+, **Minimum deployment target:** iOS 9.0, watchOS 3.0

## **2.0.1 - 13 July 2020**

#### **Release notes**

* **AppfigurateSE:** Bug fix.

#### Requirements

* **Xcode:** 11.5+, **Minimum deployment target:** iOS 9.0, watchOS 3.0

## **2.0.0 - 21 June 2020**

#### **Release notes**

* **SDK:** Full support of Swift 5.2+ using property wrappers.
* **SDK:** Appfigurate Library requires Swift to be linked to your target.
* **SDK:** Appfigurate Library is now distributed as a single static XCFramework rather than separate `libAppfigurateLibrary.a` (iOS) and `libAppfigurateLibraryWatch.a` (watchOS) static libraries.
* **SDK:** Example apps are now available in both Objective-C and Swift, and have been updated to support `UIWindowScene` (multiple windows) on iPadOS 13+.
* **SDK:** Your APLConfiguration subclass must now be specified in the `Info.plist` item `APLConfigurationClass`. Swift `APLConfiguration` subclasses may use unicode characters.
* **SDK:** Appfigurate Library no longer supports swizzling `UIApplicationDelegate` or `WKExtensionDelegate`. Instead you must programmatically call `APLApplicationDidFinishLaunchingWithOptions`, `APLApplicationDidFinishLaunching` and `APLApplicationOpenURL` at the appropriate times.
* **SDK:** `APLConfiguration` subclass properties and executable actions can now be marked as requiring an app restart when they change.
* **SDK:** Appfigurate Library now works correctly inside applications that use `UIWindowScene`.
* **SDK:** Appfigurate in conjunction with Appfigurate Library can automatically restart your app when restartable properties have changed.
* **App:** Restart required properties and executable actions are now indicated in the UI with a green refresh icon (↩).
* **App:** Install example app metadata for easier testing of included SDK example apps (≡ ‣ Install example apps).
* **App:** Output `UIWindowSceneDelegate` snippets for easier integration. (≡ ‣ *App* ‣ Output UIWindowSceneDelegate snippet).
* **AppfigurateSE:** Companion AppfigurateSE macOS app updated to automatically generate encrypted string list items for both Objective-C and Swift `APLConfiguration` subclasses.

#### Requirements

* **Xcode:** 11.5+, **Minimum deployment target:** iOS 9.0, watchOS 3.0

## **1.4.0 - 21 December 2019**

#### Release notes

* **App:** Support for iOS 13 Dark Mode.
* **SDK:** Added ability to execute custom actions declared in an `APLConfiguration` subclass. Check API documentation for ACTION\_METHOD macro.

#### Requirements

* **Xcode:** 10.1+, **Minimum deployment target:** iOS 8.0, watchOS 3.0

## **1.3.1 - 6 October 2019**

#### **Release notes**

* **App:** Minor fixes to UI for iOS 13 compatibility.

#### Requirements

* **Xcode:** 10.1+, **Minimum deployment target:** iOS 8.0, watchOS 3.0

## **1.3.0 - 27 August 2019**

#### Release notes

* **App:** Share app metadata quickly using Multipeer connectivity. (≡ ‣ Import apps ‣ Multipeer).
* **App:** The shared Simulator database stored in `~/Library/Application Support/Appfigurate` is upgraded to 1.3.0 and is incompatible with <= 1.2.1. Please ensure you upgrade Appfigurate to 1.3.0 in all Simulators you use.
* **App:** Output Swift implementation (≡ ‣ *\[App name]* ‣ Output Swift implementation).
* **App:** Non-exportable app metadata will now expire after 180 days from installation.
* **App:** Removed ability to perform remote apply of configuration using non-exportable app metadata.
* **App:** Conversion of exportable app metadata to non-exportable app metadata. Use with caution as this action cannot be undone. (≡ ‣ *\[App name]* ‣ Exportable switch).
* **App:** Custom list item screen improvements.
* **App:** Refreshed look and feel.
* **AppfigurateSE:** StringEncryption macOS app removed and replaced with AppfigurateSE app, available on macOS App Store. Includes source editor extension for Xcode to automatically generate encrypted string list items (currently Objective-C only).
* **SDK:** Reformatted API documentation available.
* **SDK:** Library now throws an NSException (only when `allowInvalidSignatures` return `YES`, e.g. Debug builds) in the following scenarios: Invalid APLKeychainAccessibility value, Invalid configuration after reset method, Invalid list values found not conforming to regex.

#### **Requirements**

* **Xcode:** 10.1+, **Minimum deployment target:** iOS 8.0, watchOS 3.0

## **1.2.1 - 22 May 2019**

#### **Release notes**

* **SDK only:** Added `APLSaveConfiguration()` and `APLRestoreConfiguration()` to save and restore keychain.

#### Requirements

* **Xcode:** 10.1+, **Minimum deployment target:** iOS 8.0, watchOS 3.0

## **1.2.0 - 28 Mar 2019**

#### **Release notes**

* **App:** Minor bug fixes.
* **App:** Share app metadata quickly using QR codes. Scanning requires a device running iOS 11.0+ (≡ ‣ Import apps ‣ QR code).
* **SDK:** Support for encrypted string list items using the `ENCRYPTED_STRING_PROPERTY_LIST_EDIT`, `ENCRYPTED_STRING` and `ENCRYPTED_STRING_IOS_WATCHOS` macros.
* **AppfigurateSE:** Use the new StringEncryption macOS app to generate the encrypted string list item values.

#### Requirements

* **Xcode:** 9.0+, **Minimum deployment target:** iOS 8.0, watchOS 3.0

## **1.1.3 - 10 Feb 2019**

#### **Release notes**

* **App:** Minor bug fixes.
* **SDK:** Added `APLAddConfigurationUpdatedBlock()` and `APLRemoveConfigurationUpdatedBlock()` functions to complement the existing delegate based callbacks.
* **SDK:** `APLConfiguration` subclass is now instantiated earlier, allowing it to be accessed from within `+load` methods and `__attribute__((constructor))` functions.

#### Requirements

* **Xcode:** 9.0+, **Minimum deployment target:** iOS 8.0, watchOS 3.0

## **1.1.2 - 17 Jan 2019**

#### **Release notes**

* **App:** Automatic notification of new releases.
* **SDK:** Bug fixes.

#### Requirements

* **Xcode:** 9.0+, **Minimum deployment target:** iOS 8.0, watchOS 3.0

## **1.1.1 - 16 Dec 2018**

#### **Release notes**

* **SDK:** Support for arm64\_32 architecture (Apple Watch series 4) in Appfigurate Library.
* **SDK:** Fix missing Watch Extension icon.

#### Requirements

* **Xcode:** 9.0+, **Minimum deployment target:** iOS 8.0, watchOS 3.0

## **1.1.0 - 12 Sep 2018**

#### **Release notes**

* **SDK:** Support for watchOS app intent extensions (e.g. SiriKit)

**Requirements**

* **Xcode:** 9.0+, **Minimum deployment target:** iOS 8.0, watchOS 3.0

## **1.0.1 - 9 Feb 2018**

#### **Release notes**

* **App:** Device builds have optional PIN/TouchID/FaceID authentication.

#### Requirements

* **Xcode:** 8.0, **Minimum deployment target:** iOS 8.0, watchOS 3.0

## **1.0.0 - 17 Jan 2018**

#### **Release notes**

* Initial public release.

#### Requirements

* **Xcode:** 8.0+, **Minimum deployment target:** iOS 8.0, watchOS 3.0


# Third party notices

Appfigurate, AppfigurateSE and libraries may contain the following 3rd party software:

{% file src="/files/UxiSJWuD5MVe1cxUToqu" %}

iPhone, iPad, Xcode and watchOS are trademarks of Apple Inc.&#x20;

iOS is a trademark or registered trademark of Cisco in the U.S. and other countries and is used under license by Apple.&#x20;

Android and Firebase are trademarks of Google LLC.&#x20;

Launch Darkly is a trademark of Catamorphic Co.&#x20;

Electric Bolt is not endorsed by or affiliated with Apple, Google, BrowserStack, or Catamorphic in any way.


