Appfigurate™️
HomeDocumentation
  • Introducing Appfigurate™️ 3
  • Getting Started
    • Getting Started
    • Examples
    • Upgrade guide
      • v1.4.0 to v2.2.0
      • v2.1.1 to v2.2.0
      • v2.2.1 to v3.0.0
      • v3.2.1 to v4.0.0
    • iOS native app integration
      • iOS app extension integration
    • watchOS app integration
      • watchOS app extension integration
    • Android native app integration
    • Mobile Flutter integration
      • Flutter iOS
      • Flutter Android
    • React Native integration
      • iOS native module integration
      • Android native module integration
      • JavaScript integration
    • Third party remote configuration providers
      • Firebase Remote Config
      • Launch Darkly
      • Other third party remote configuration providers
  • Configuration subclasses
    • Supported property types
      • Boolean
      • Integer
      • Float
      • Double
      • Plain String
      • Encrypted String
    • Custom executable actions
    • Slider icon types
  • Additional reading
    • Info.plist options
    • AndroidManifest.xml options
    • Displaying overridden configuration
    • Security
      • Best practice
      • Encryption
      • Export compliance
      • App Store compliance
      • PrivacyInfo.xcprivacy
      • Rotating your private key
  • Automation testing
    • iOS native app automation testing
    • Android native automation testing
  • API
    • iOS and watchOS API
    • Android API
    • Mobile Flutter API
    • React Native API
  • Appfigurate User Guide
    • Introduction
    • Main menu
    • Select app
    • Add app
    • Import app
    • Install example apps
    • Settings
      • Passcode Lock
      • Restore
      • Backup
      • Delete all apps and Settings
      • Analytics
    • Edit app
    • Configure app
    • Permissions
  • Appfigurate SE user guide
    • Introduction
    • Manual encryption
      • ENCRYPTED_STRING macro/function
      • ENCRYPTED_STRING_IOS_WATCHOS macro/function
    • Setup iOS Simulator app
    • Setup Android Emulator app
    • Xcode source editor extension
      • Troubleshooting
    • Real device cloud testing services
      • BrowserStack
  • LEGAL
    • License Agreement
    • Privacy Policy
    • Release History
    • Third party notices
Powered by GitBook
On this page
  • Local properties
  • Editable List UI
  • Encryption best practice
  • iOS and watchOS
  • Android
  • Mobile Flutter
  • React Native
  1. Configuration subclasses
  2. Supported property types

Encrypted String

PreviousPlain StringNextCustom executable actions

Last updated 4 months ago

The default value of an encrypted String is nil. You must change the default value of the property by assigning a new plain text value in an overridden method.

The default value of a NSString is nil. You must change the default value of the property by assigning a new plain text value in an overridden method.

NSString properties should be declared in your subclass header file as follows:

@import AppfigurateLibrary;

@interface Configuration : APLConfiguration

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

The default value of an encrypted String is null. You must change the default value of the property by assigning a new plaintext value in an overridden method.

The default value of an encrypted String is null. You must change the default value of the property by assigning a new plaintext value in an overridden method.

The flutter class defers to the underlying platform (iOS) or (Android) subclass to read property values.

The React Native module AppfigurateModule class defers to the underlying platform (iOS) or (Android) subclass to read property values.

This page describes encrypted strings. Appfigurate also supports .

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.

Swift @EncryptedStringPropertyListEdit prototype

@EncryptedStringPropertyListEdit(regex, description, encrypted, restart, values)
var propertyName: 

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

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

Swift @EncryptedStringPropertyListEdit example

    @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: 
    
    override func reset() {
        url = "https://m.appfigurate.io/list"
    }
    ...

ENCRYPTED_STRING function

Swift ENCRYPTED_STRING prototype

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.

Swift ENCRYPTED_STRING example

    @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_STRING_IOS_WATCHOS function

Note: Using different public keys and the ENCRYPTED_STRING_IOS_WATCHOS function for watchOS and it's companion iOS app is deprecated will be removed in the upcoming Appfigurate 4 release. Migrate to shared public keys and the ENCRYPTED_STRING function instead.

Swift ENCRYPTED_STRING_IOS_WATCHOS prototype

func ENCRYPTED_STRING_IOS_WATCHOS(_ plaintext: String, _ ciphertextIOS: String, _ ciphertextWatchOS: String) -> String

For an iOS and watchOS app where the APLConfiguration subclass has two public keys, use the ENCRYPTED_STRING_IOS_WATCHOS function for each list item in the @EncryptedStringPropertyListEdit.

Swift ENCRYPTED_STRING_IOS_WATCHOS example

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

ENCRYPTED function

Swift ENCRYPTED prototype

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.

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

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:

Swift isEncrypted example

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

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

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

Objective-C ENCRYPTED_STRING_PROPERTY_LIST_EDIT prototype

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

Objective-C ENCRYPTED_STRING_PROPERTY_LIST_EDIT example

@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

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.

Objective-C ENCRYPTED_STRING example

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==")});

ENCRYPTED_STRING_IOS_WATCHOS macro

Note: Using different public keys and the ENCRYPTED_STRING_IOS_WATCHOS macro for watchOS and it's companion iOS app is deprecated will be removed in the upcoming Appfigurate 4 release. Migrate to shared public keys and the ENCRYPTED_STRING macro instead.

Objective-C ENCRYPTED_STRING_IOS_WATCHOS prototype

ENCRYPTED_STRING_IOS_WATCHOS(plaintext, ciphertextIOS, ciphertextWatchOS)

For an iOS and watchOS app where the APLConfiguration subclass has two public keys, use the ENCRYPTED_STRING_IOS_WATCHOS macro for each list item in the ENCRYPTED_STRING_PROPERTY_LIST_EDIT macro.

Objective-C ENCRYPTED_STRING_IOS_WATCHOS example

ENCRYPTED_STRING_PROPERTY_LIST_EDIT(url, @"https://[\\w\\.-]+\\.appfigurate.io/.*", @"url", NO, @{
    @"Dev": ENCRYPTED_STRING_IOS_WATCHOS(@"https://dev.appfigurate.io/list",@"H7o9Lgqd4RgMJ...rtJQGh8DiiSAQ==",@""JaMa92a122zZs...Mnz23KamnZ0a="), 
    @"Test": ENCRYPTED_STRING_IOS_WATCHOS(@"https://test.appfigurate.io/list",@"PL+UtWB9WHuO7...aX5BdNqEcbmQE=",@"Na1MSadBCaDD...KKANZias199Km=="), 
    @"Prod": ENCRYPTED_STRING_IOS_WATCHOS(@"https://m.appfigurate.io/list",@"IxrJFFUarMg6p...E7OWYOC2uJ1AQ==",@"aZSDIMSaasiaM...8SKS1MMahfpIa=")});

Kotlin @EncryptedStringPropertyListEdit prototype

@EncryptedStringPropertyListEdit(description, regularExpression, encrypted, restart)

The encrypted parameter of EncryptedStringPropertyListEdit must be the result of calling BuildConfig.ENCRYPTED. e.g.

     ... description = "url", encrypted = BuildConfig.ENCRYPTED, restart = ...

You must implement a method with the same name as the property, but ending with Values. e.g. if your property name is url, your method must be named urlValues. The method must return a Map<String, String> of the possible property values.

Kotlin Values method prototype

var propertyName: 

companion object {
    var _propertyNameValues: MutableMap<String, String> = HashMap()

    init {
        _propertyNameValues["key1"] = if (BuildConfig.ENCRYPTED) "cipher1" else "plain1"
    }
}

fun propertyNameValues(): Map<String, String> {
    return _propertyNameValues;
}

Kotlin example

import nz.co.electricbolt.appfiguratelibrary.Configuration
import nz.co.electricbolt.appfiguratelibrary.annotations.EncryptedStringPropertyListEdit

class AppConfiguration : Configuration() {

    @EncryptedStringPropertyListEdit(description = "url", regularExpression = "https://[\w\.-]+\.appfigurate.io/.*", encrypted = BuildConfig.ENCRYPTED, restart = false)
    var url: 
    
    companion object {
        var _urlValues: MutableMap<String, String> _urlValues = HashMap()  

        init {
            _urlValues["Dev"] = if (BuildConfig.ENCRYPTED) "H7o9Lgqd4RgMJ...rtJQGh8DiiSAQ==" else "https://dev.appfigurate.io/list"
            _urlValues["Test"] = if (BuildConfig.ENCRYPTED) "PL+UtWB9WHuO7...aX5BdNqEcbmQE=" else "https://test.appfigurate.io/list"
            _urlValues["Prod"] = if (BuildConfig.ENCRYPTED) "IxrJFFUarMg6p...E7OWYOC2uJ1AQ==" else "https://m.appfigurate.io/list"
        }
    }

    fun urlValues(): Map<String, String> {
        return _urlValues
    }
    
    override fun reset() {
        super.reset()
        url = "https://m.appfigurate.io/list"
    } 
...

BuildConfig.ENCRYPTED build constant

In your application's build.gradle:

  • Add the following into the android‣buildTypes‣debug section:

    buildConfigField("boolean", "ENCRYPTED", "false")    
  • Add the following into the android‣buildTypes‣release section:

    buildConfigField("boolean", "ENCRYPTED", "true")    
  • Ensure ProGuard runs on a Release build variant so that BuildConfig.ENCRYPTED == false dead code is stripped from the resulting APK or AAB (this will ensure the plaintext values are removed).

build.gradle example

apply plugin: 'com.android.application'

android {
    compileSdkVersion 32
    defaultConfig {
        ...
    }
    buildTypes {
        debug {
            debuggable true
            minifyEnabled false
            zipAlignEnabled false
            buildConfigField("boolean", "ENCRYPTED", "false")
        }
        release {
            minifyEnabled true
            shrinkResources true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.debug
            buildConfigField("boolean", "ENCRYPTED", "true")
        }
    }
}
...

Once you've modified build.gradle and synced the Gradle project, ensure the generated BuildConfig.java contains the following for a Debug build variant:

public static final boolean ENCRYPTED = false;

and the following for a Release build variant:

public static final boolean ENCRYPTED = true;

Java @EncryptedStringPropertyListEdit prototype

@EncryptedStringPropertyListEdit(description, regularExpression, encrypted, restart)

The encrypted parameter of EncryptedStringPropertyListEdit must be the result of calling BuildConfig.ENCRYPTED. e.g.

     ... description = "url", encrypted = BuildConfig.ENCRYPTED, restart = ...

You must implement a method with the same name as the property, but ending with Values. e.g. if your property name is url, your method must be named urlValues. The method must return a Map<String, String> of the possible property values.

Java Values method prototype

String propertyName;

static Map<String, String> _propertyNameValues = new HashMap<>();

static {
    _propertyNameValues.put("key1", BuildConfig.ENCRYPTED ? "cipher1" : "plain1");
}

public Map<String, String> propertyNameValues() {
    return _propertyNameValues;
}

Java example

import nz.co.electricbolt.appfiguratelibrary.Configuration;
import nz.co.electricbolt.appfiguratelibrary.annotations.EncryptedStringPropertyListEdit;

public class AppConfiguration extends Configuration {

    @EncryptedStringPropertyListEdit(description: "url", regularExpression = "https://[\w\.-]+\.appfigurate.io/.*", encrypted = BuildConfig.ENCRYPTED, restart = false)
    String url;
    
    static Map<String, String> _urlValues = new HashMap<>();

    static {
        _urlValues("Dev", BuildConfig.ENCRYPTED ? "H7o9Lgqd4RgMJ...rtJQGh8DiiSAQ==" : "https://dev.appfigurate.io/list");
        _urlValues("Test", BuildConfig.ENCRYPTED ? "PL+UtWB9WHuO7...aX5BdNqEcbmQE=" : "https://test.appfigurate.io/list");
        _urlValues("Prod", BuildConfig.ENCRYPTED ? "IxrJFFUarMg6p...E7OWYOC2uJ1AQ==" : "https://m.appfigurate.io/list");
    }

    public Map<String, String> urlValues() {
        return _urlValues;
    } 
    
    @Override
    public void reset() {
        super.reset();
        this.url = "https://m.appfigurate.io/list";
    }
...

BuildConfig.ENCRYPTED build constant

In your application's build.gradle:

  • Add the following into the android‣buildTypes‣debug section:

    buildConfigField("boolean", "ENCRYPTED", "false")    
  • Add the following into the android‣buildTypes‣release section:

    buildConfigField("boolean", "ENCRYPTED", "true")    
  • Ensure ProGuard runs on a Release build variant so that BuildConfig.ENCRYPTED == false dead code is stripped from the resulting APK or AAB (this will ensure the plaintext values are removed).

build.gradle example

apply plugin: 'com.android.application'

android {
    compileSdkVersion 32
    defaultConfig {
        ...
    }
    buildTypes {
        debug {
            debuggable true
            minifyEnabled false
            zipAlignEnabled false
            buildConfigField("boolean", "ENCRYPTED", "false")
        }
        release {
            minifyEnabled true
            shrinkResources true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.debug
            buildConfigField("boolean", "ENCRYPTED", "true")
        }
    }
}
...

Once you've modified build.gradle and synced the Gradle project, ensure the generated BuildConfig.java contains the following for a Debug build variant:

public static final boolean ENCRYPTED = false;

and the following for a Release build variant:

public static final boolean ENCRYPTED = true;

Dart prototype

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

Dart example

import 'package:appfigurateflutter/appfigurateflutter.dart';

public class Configuration extends APLNativeConfiguration {

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

JavaScript example

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

const {Appfigurate} = NativeModules;

...

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

Encryption best practice

iOS and watchOS

Using ENCRYPTED_STRING and ENCRYPTED_STRING_IOS_WATCHOS 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

> 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 and ENCRYPTED_STRING_IOS_WATCHOS to encrypt sensitive information such as server urls (e.g. internal test environments), rather than expose them as plain text.

Android

Using a combination of BuildConfig.ENCRYPTED and ProGuard 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 plaintext is not included in the RELEASE build variant APK or AAB using the macOS strings tool as follows:

Terminal

> cd AppfigurateExample/build/outputs/apk/release
> unzip AppfigurateExample-release.apk
> d2j-dex2jar -f classes.dex
> unzip classes-dex2jar.jar
> cd nz/co/electricbolt/appfigurateexample
> strings - ExampleConfiguration.class | grep "https://dev.appfigurate.io/list" // no results
> strings - ExampleConfiguration.class | grep "jm9SM4MEYa4FR...glwMGnpRI4JAQ==" // prints jm9SM4MEYa4FR...glwMGnpRI4JAQ==

The dex2jar tool can be installed using brew.

Mobile Flutter

React Native

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 or .

The first parameter of the ENCRYPTED_STRING function is the plaintext, and will be used by DEBUG builds. The second and third parameters of the ENCRYPTED_STRING function are the ciphertext for iOS and watchOS apps, and will be used by RELEASE builds. To generate the ciphertext, use the or .

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 or .

The first parameter of the ENCRYPTED_STRING macro is the plain text, and will be used by DEBUG builds. The second and third parameters of the ENCRYPTED_STRING macro are the ciphertext for iOS and watchOS apps, and will be used by RELEASE builds. To generate the ciphertext, use the or .

To generate the ciphertext, use the .

To generate the ciphertext, use the .

Ensure you have read the encryption best practices for , and sections.

Ensure you have read the encryption best practices for , and sections.

source editor extension for Xcode
AppfigurateSE app
source editor extension for Xcode
AppfigurateSE app
source editor extension for Xcode
AppfigurateSE app
source editor extension for Xcode
AppfigurateSE app
AppfigurateSE app
AppfigurateSE app
iOS and watchOS
Android
iOS and watchOS
Android
reset
reset
APLConfiguration
reset
reset
APLNativeConfiguration
APLConfiguration
nz.co.electricbolt.appfiguratelibrary.Configuration
APLConfiguration
nz.co.electricbolt.appfiguratelibrary.Configuration
plain textual strings