# iOS native app integration

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

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

## Prerequisites

You must have the following:

* Xcode 16.4+
* 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.md) for easy 1 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.md) 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.md).


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.electricbolt.co.nz/getting-started/ios-native-app-integration.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
