Module HAAPI iOS UIKit Documentation

An iOS XCFramework with classes and layout definitions that are used to perform an Haapi Flow authorization with Curity Identity Server Hypermedia Authentication API (HAAPI) from iOS devices.

The framework allows a client to implement an authorization flow via HAAPI by using a set of prebuilt UI screens that represent the different responses/steps that may be involved in the API-driven flow.

Usage examples

UIKit application

Start by creating the configurations for the client access to the server and user flow (HaapiUIKitConfiguration), and create an HaapiUIKitApplication in your AppDelegate.

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
    var haapiUIKitConfiguration = HaapiUIKitConfigurationBuilder(clientId: Constants.clientId,
                                                                 baseUrl: Constants.baseURL,
                                                                 tokenEndpointUrl: Constants.tokenEndpointURL,
                                                                 authorizationEndpointUrl: Constants.authorizationEndpointURL,
                                                                 appRedirect: Constants.appRedirectURIString)
        .build()

    var haapiUIKitApplication: HaapiUIKitApplication!

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        haapiUIKitApplication = HaapiUIKitApplicationBuilder(haapiUIKitConfiguration: haapiUIKitConfiguration).build()
        return true
    }
  // ...
}

Start the Haapi flow in your main UIViewController that is implementing HaapiFlowResult to receive a result at the end of the Haapi flow or an error during the flow.

extension UIApplication {
    var haapiUIKitApplication: HaapiUIKitApplication {
        get {
            guard let appDelegate = delegate as? AppDelegate else { fatalError("...") }
            return appDelegate.haapiUIKitApplication
        }
    }
}
// ... Presented UIViewController
do {
  try HaapiFlow.start(from: self, // self is an UIViewController that is presented.
                haapiUIKitApplication: UIApplication.shared.haapiUIKitApplication,
                haapiDeepLinkManageable: HaapiDeepLinkManager.shared)
} catch {
  // handle error if the HaapiFlow does not start.
}
// ...

SwiftUI application

Start by creating the configurations for the client access to the server and user flow (HaapiUIKitConfiguration) in an AppDelegate.

class AppDelegate: NSObject, UIApplicationDelegate {
    var haapiUIKitConfiguration = HaapiUIKitConfigurationBuilder(clientId: Constants.clientId,
                                                                 baseUrl: Constants.baseURL,
                                                                 tokenEndpointUrl: Constants.tokenEndpointURL,
                                                                 authorizationEndpointUrl: Constants.authorizationEndpointURL,
                                                                 appRedirect: Constants.appRedirectURIString)
        .build()
  
    var haapiUIKitApplication: HaapiUIKitApplication!

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        haapiUIKitApplication = HaapiUIKitApplicationBuilder(haapiUIKitConfiguration: haapiUIKitConfiguration).build()
        return true
    }
}

Hook the AppDelegate with the struct App.

@main
struct SwiftUIApp: App {
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate

    var body: some Scene {
        WindowGroup {
            ContentView(haapiApplication: appDelegate.haapiUIKitApplication)
        }
    }
  // ...
}

Implement HaapiFlowResult in the View that can start the Haapi flow and start the Haapi flow. HaapiFlowResult provides a set of methods to receive a result at the end of the Haapi flow or an error during the flow

struct ContentView: View, HaapiFlowResult {

    let haapiApplication: HaapiUIKitApplication
    @State private var showingHaapiVC = false

    var body: some View {
        VStack {
            Button {
                showingHaapiVC = true
            } label: {
                Text("Start Haapi flow")
            }

        }
        .sheet(isPresented: $showingHaapiVC) {
            HaapiFlow.start(self,
                            haapiUIKitApplication: haapiApplication,
                            haapiDeepLinkManageable: HaapiDeepLinkManager.shared)
        }
        // ...
    }

    func didReceiveOAuthTokenModel(_ oAuthTokenModel: IdsvrHaapiUIKit.OAuthTokenModel) { /* ... */ }

    func didReceiveError(_ error: Error) { /* ... */ }
}

HaapiUIKitConfiguration options

As demonstrated in [Usage examples](#Usage examples), HaapiUIKitConfiguration is being used to build an HaapiUIKitApplication via HaapiUIKitApplicationBuilder. When using HaapiUIKitConfiguration, it is possible to configure a few options:

autoPollingDuration

During the Haapi Flow, when a polling process is invoked then an automatic polling waits a few seconds between triggering polling requests to the server. The default value is 3 seconds. It is possible to configure a number of seconds for the automatic polling. When the value is greater than 0 then the Haapi Flow will automatically handle the polling process, else it will disable automatic polling behavior.

shouldAutoHandleFlowErrorFeedback

It is possible that during the Haapi Flow, the server returns an unexpected problem or an internal exception occurs on the framework which prevents the flow from proceeding and completing. In this case, as a good practice, the user is notified when something bad happened, thus preventing them from completing an action.

When set to true, an AlertDialog is presented and gives the user some feedback about the error which user interaction causes the flow to finish and to return the error information to HaapiFlowResult. When set to false, the flow is directly interrupted and returns the error information to HaapiFlowResult which delegates the user feedback responsibility to the application developer.

presentationMode

This configuration informs HaapiFlow.start() how HaapiFlowViewController is presented. The presentation determines the type of animation and transition when presenting HaapiFlowViewController to the client's application. The default value is set to PresentationMode.modal. PresentationMode.stack is another option.

With Presentation.modal, HaapiFlowViewController is presented as a modal" navigation, presenting the flow view controller entering from the bottom to the top and closing with an animation from top to bottom.

With Presentation.stack, HaapiFlowViewController is presented as a stack navigation, presenting the flow view controller entering from right to left and closing with an animation from left to right.

authenticationSelectionPresentation

This configuration informs how HaapiFlowViewController presents the AuthenticatorSelectorStep. The presentation determines how the elements of an AuthenticatorSelectorStep are presented. The default value is set to AuthenticatorSelectionPresentation.list. AuthenticatorSelectionPresentation.tabs is another option.

With AuthenticatorSelectionPresentation.list, authenticators are rendered as a scrollable list.

With AuthenticatorSelectionPresentation.tabs, authenticators are rendered with a TabbedPager. A Tab element displays the authenticators from left to right and a "Pager" displays the selected authenticator's content.

shouldConfirmFlowInterruption

When pressing the back button or the close button during the Haapi Flow, it is possible to display an alert that informs the user that the flow is going to be interrupted. The default value is true. When the value is set to false then no alert is displayed when pressing the back button or the close button.

useDefaultExternalBrowser

When the app needs to trigger some kind of external interaction action, it does so by delegating the action URL to be handled by the OS, either by using an ASWebAuthenticationSession which embeds the browser in the app, or by using the default external browser. The default value for this configuration is false which instructs the use of ASWebAuthenticationSession.

HaapiFlowViewModel

The HaapiFlowViewModel is the main driver for the execution and stepping through the authentication flow and it is used internally by the framework to mediate the interaction with the Curity Identity Server. However it is also possible to use it as a standalone functional component allowing a client developer to build a completely custom UI and use the viewmodel as the internal driver to their authentication flow.

To do so, there's a public construtor available and then it is possible to subscribe to state changes on the @Published state variables.

var haapiUIKitConfiguration = HaapiUIKitConfigurationBuilder(...).build()
...
var haapiUIKitApplication = HaapiUIKitApplicationBuilder(haapiUIKitConfiguration: haapiUIKitConfiguration).build()

...

let haapiFlowViewModel = HaapiFlowViewModel(haapiUIKitApplication: haapiUIKitApplication)

haapiFlowViewModel.$isLoading
  .receive(on: DispatchQueue.main)
  .sink(receiveValue: { isLoading in
      // handle isLoading
  })
  .store(in: &cancellables)

  haapiFlowViewModel.$uiModel
  .receive(on: DispatchQueue.main)
  .sink(receiveValue: { result in
      // handle uiModel change
  })
  .store(in: &cancellables)

  haapiFlowViewModel.$error
  .receive(on: DispatchQueue.main)
  .sink(receiveValue: { error in
      // handle error
  })
  .store(in: &cancellables)

  haapiFlowViewModel.$oAuthModel
  .receive(on: DispatchQueue.main)
  .sink(receiveValue: { oAuthModel in
      // handle OAuthModel
  }
  .store(in: &cancellables)

Starting the Haapi Authentication flow

The framework provides some convenience helper methods to setup and start the authentication flow inside the application. To do so, an HaapiFlow API is provided and the user is able to easily trigger the presentation of the flow by using it like below.

HaapiFlow.start(from: parentVC,
                haapiUIKitApplication: haapiApplication,
                haapiDeepLinkManageable: HaapiDeepLinkManager.shared)

When the configuration for the presentation mode is set to STACK, using the convenience flow helpers from SwiftUI code OR when running the app in iOS version below 16.0, a back navigation button is placed with a label that can be customized by providing a translation for the key hui_back and the tint for the button text and image can be customized by providing the theme's HaapiFlowViewControler style color configuration with the key closeButtonTintColorName.

Alternatively, if more control over the presentation and lifecycle management is necessary, when using from UIKit code an instance of HaapiFlowViewController can be manually created. If using SwiftUI, then a HaapiFlowViewControllerRepresentable is available or the developer can write their own custom implementation of UIViewControllerRepresentable wrapping the HaapiFlowViewController.

⚠️ Important:
Always make sure to thoroughly test the app's authentication flow navigation.

On iOS below 16.0, running from SwiftUI to present the flow in a NavigationView, using a NavigationLink and set it's destination to call HaapiFlow.start() may cause unexpected behavior due to the destination View being immediately initialized when the link is rendered and it's state internally kept until used when navigating. To avoid this behaviour it is possible and recomended to use the modifier flag isActive and bind it to a state variable that will trigger the init only when set to true, like the example below by clicking a button that triggers the NavigationLink. Also, make sure to set navigationViewStyle to .stack to avoid unexpected behaviour.

NavigationView {
    NavigationLink(destination: HaapiFlow.start(self,
                                                haapiUIKitApplication: haapiApplication,
                                                haapiDeepLinkManageable: HaapiDeepLinkManager.shared),
                    isActive: $isShowingStackVC) { EmptyView() }
    Button("Start Haapi Stack") {
        self.isShowingStackVC = true
    }
}.navigationViewStyle(.stack)

Starting from iOS 16.0, it is recomended to replace NavigationView with NavigationStack due to API deprecation. The example below demonstrates a fleshed out version of the changes. More information on NavigationView/NavigationStack migration here.

@State private var path: [NavigationPath] = []

NavigationStack {
    NavigationLink("Start Haapi Stack", value: NavigationPath.haapiVC)
        .navigationDestination(for: NavigationPath.self) { navigation in
            switch navigation {
            case .haapiVC:
                HaapiFlow.start(self,
                                haapiUIKitApplication: haapiApplication,
                                haapiDeepLinkManageable: HaapiDeepLinkManager.shared)
            }
        }
}

Theming

When using IdsvrHaapiUIKit in the application, UI components including ViewControllers are themed using colors, images, font, and UI configurations specified in a plist file. Everything that is displayed when performing an Haapi flow can be themed to some extent.

Each UI component uses its own style definition. A style definition contains keys and values. The latest are String, Number, Boolean, or Dictionary. When trying to change any aspects of the UI component as demonstrated below, it is important to fit the specification. Otherwise, the application may crash and if it is the case, please check the logs in the console.

How to override colors that are used in IdsvrHaapiUIKit?

In the application Asset catalog, define an existing key and provide a new color. Here is the list of colors that are being used.

For example, to change hui_background, declare the same key in the application Asset catalog and define the colors. When an UIComponent in IdsvrHaapiUIKit uses hui_background, it uses the ones declared in the application Asset catalog instead of the framework.

It is also possible to assign for the UIComponent in IdsvrHaapiUIKit, via a custom theme, to use a color that is not in the list. See below how to assign a custom theme.

How to override images that are used in IdsvrHaapiUIKit?

In the application Asset catalog, define an existing key and provide a new image. Here is the list of images that are being used.

For example, to change hui_ic_authenticator_user, declare the same key in the application Asset catalog and provide the image asset. When an UIComponent in IdsvrHaapiUIKit uses hui_ic_authenticator_user, it uses the ones declared in the application Asset catalog instead of the framework.

How to change the font in IdsvrHaapiUIKit?

IdsvrHaapiUIKit uses by default Roboto font.

If the current font does not meet your needs, it is possible to change it. There are 2 ways: existing font from the iOS system or custom font.

Using an existing font from the iOS system

In the iOS system, here is the list of supported fonts.

For example, to change the font to Baskerville, create a plist file in the application. In the plist file, add a new key of String named fontName.

<?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>fontName</key>
	<string>Baskerville</string>
</dict>
</plist>

When using HaapiUIKitApplicationBuilder, the plist file name needs to be provided. For example, if the plist file is named: "MyCustom.plist". As explained above in the AppDelegate when using HaapiUIKitApplicationBuilder, a new method needs to be invoked.

HaapiUIKitApplicationBuilder(haapiUIKitConfiguration: haapiUIKitConfiguration)
.setThemingPlistFileName("MyCustom") // It referes to MyCustom.plist. 
.build()

Using a custom font

To use a custom font, here are the requirements:

  1. Add the files related to the custom font to the application workspace. The files need to be a ttf file extension and target the application workspace.
  2. Create a plist file in the application. In the plist file, add a new key of String named fontName. The value is the font family name using title case representation. For example, if the files were added and have the prefix, IBMPlexMono, then the font family name title case representation is IBM Plex Mono.
<?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>fontName</key>
	<string>IBM Plex Mono</string>
</dict>
</plist>

When using HaapiUIKitApplicationBuilder, the plist file name needs to be provided. For example, if the plist file is named: "MyCustom.plist". As explained above in the AppDelegate when using HaapiUIKitApplicationBuilder, a new method needs to be invoked.

HaapiUIKitApplicationBuilder(haapiUIKitConfiguration: haapiUIKitConfiguration)
.setThemingPlistFileName("MyCustom") // It referes to MyCustom.plist. 
.build()

When starting the application with these changes, the logs should indicate if the custom font was added with success.

2023-08-10 21:32:18.512355+0200 IdsvrHaapiUIKitApp[36182:2904595] [HAAPI_UI_THEMING] [INFO] ThemeRegistry.291 : There are initially 84 registered fonts.
2023-08-10 21:32:18.528310+0200 IdsvrHaapiUIKitApp[36182:2904595] [HAAPI_UI_THEMING] [INFO] ThemeRegistry.305 : Now there are 86 registered fonts.
2023-08-10 21:32:18.529603+0200 IdsvrHaapiUIKitApp[36182:2904595] [HAAPI_UI_THEMING] [INFO] ThemeRegistry.308 : These fonts were added: ["Roboto", "IBM Plex Mono"])

As shown above, the custom font "IBM Plex Mono" was added. (Roboto is the default font that is used in the framework.)

ℹ️ If the fontName was configured with IBMPlexMono instead of IBM Plex Mono. A warning will be displayed when the application starts as shown below. ⚠️ If the name is configured incorrectly, then the application crashes.

2023-08-10 21:50:51.418855+0200 IdsvrHaapiUIKitApp[37474:2932255] [HAAPI_UI_THEMING] [WARNING] ThemeRegistry.316 : The configured fontName is `IBMPlexMono`. Isn't it `IBM Plex Mono`?

⚠️ If the fonts were already registered in the application's project by declaring them in the application's info plist file as the example below.

<dict>
	<key>UIAppFonts</key>
	<array>
        <string>IBMPlexMono-Bold.ttf</string>
        <string>IBMPlexMono-BoldItalic.ttf</string>
        <string>IBMPlexMono-ExtraLight.ttf</string>
        <string>IBMPlexMono-ExtraLightItalic.ttf</string>
        <string>IBMPlexMono-Italic.ttf</string>
        <string>IBMPlexMono-Light.ttf</string>
        <string>IBMPlexMono-LightItalic.ttf</string>
        <string>IBMPlexMono-Medium.ttf</string>
        <string>IBMPlexMono-MediumItalic.ttf</string>
        <string>IBMPlexMono-Regular.ttf</string>
        <string>IBMPlexMono-SemiBold.ttf</string>
        <string>IBMPlexMono-SemiBoldItalic.ttf</string>
        <string>IBMPlexMono-Text.ttf</string>
        <string>IBMPlexMono-TextItalic.ttf</string>
        <string>IBMPlexMono-Thin.ttf</string>
        <string>IBMPlexMono-ThinItalic.ttf</string>
	</array>
</dict>
</plist>

The success log is different because ThemeRegistry does not need to register the custom font.

2023-08-10 21:56:09.982900+0200 IdsvrHaapiUIKitApp[37856:2940070] [HAAPI_UI_THEMING] [INFO] ThemeRegistry.291 : There are initially 86 registered fonts.
2023-08-10 21:56:09.995493+0200 IdsvrHaapiUIKitApp[37856:2940070] [HAAPI_UI_THEMING] [INFO] ThemeRegistry.305 : Now there are 87 registered fonts.
2023-08-10 21:56:09.996721+0200 IdsvrHaapiUIKitApp[37856:2940070] [HAAPI_UI_THEMING] [INFO] ThemeRegistry.308 : These fonts were added: ["Roboto"])
2023-08-10 21:56:09.998978+0200 IdsvrHaapiUIKitApp[37856:2940070] [HAAPI_UI_THEMING] [INFO] ThemeRegistry.94 : ThemeRegistry is setup.

How to identify which themable components are displayed?

The HaapiLogger requires to be configured:

HaapiLogger.followUpTags = [UIKitFollowUpTag.theming, UIKitFollowUpTag.component, UIKitFollowUpTag.layout]
HaapiLogger.isDebugEnabled = true

When running the flow, check in the console for the follow up tag: "HAAPI_UI_LAYOUT". Here is an example:

2023-08-08 23:43:10.820923+0200 IdsvrHaapiUIKitApp[24669:526101] [HAAPI_UI_LAYOUT] [DEBUG] HaapiFlowViewController.246 : Presenting <IdsvrHaapiUIKit.HaapiFlowViewController: 0x7f8230023200> can be themed and uses these themable elements: ["NotificationBannerView", "LoadingIndicatorView", "LoadingIndicatorView.ButtonPrimary", "LoadingIndicatorView.ButtonSecondary", "LoadingIndicatorView.ButtonText", "LoadingIndicatorView.ButtonLinkView", "StackView", "StackView.Interactions", "StackView.Links", "StackView.Polling"] - it is not an exhaustive list.

How to change the UI configurations?

Every UI component that is used in IdsvrHaapiUIKit can be themed. The theming is relying on a set of configuration that reference the color, image or primitive values such as String, Number, Boolean, etc. The set of configuration can be overridden by providing a plist file that overrides any specific keys with the corresponding values.

⚠️ Providing an incorrect value to a valid key may crash the application. Please check carefully the specification for the corresponding style definition.

Here is the list of style definitions for every UI component.

Here are a few examples:

Changing UICTFontTextStyleBody

UICTFontTextStyleBody is a key that represents the font size. This key is being used by many UI components. In IdsvrHaapiUIKit, the value is 17.

How to change to 15?

Create or use an existing plist file in the application. In the plist file, add a new key of String named fontName.

<?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>UICTFontTextStyleBody</key>
	<number>15</number>
</dict>
</plist>

When using HaapiUIKitApplicationBuilder, the plist file name needs to be provided. For example, if the plist file is named: "MyCustom.plist". As explained above in the AppDelegate when using HaapiUIKitApplicationBuilder, a new method needs to be invoked.

HaapiUIKitApplicationBuilder(haapiUIKitConfiguration: haapiUIKitConfiguration)
.setThemingPlistFileName("MyCustom") // It referes to MyCustom.plist. 
.build()

Now every UI component that uses UICTFontTextStyleBody is affected by this change.

Override TextAppearance.Body

TextAppearance.Body is a key that represents a text appearance that uses UICTFontTextStyleBody and natural text alignment (source). This key is used by some UI component such as ActionableButton.Primary.

How to change the text appearance (italic) for ActionableButton.Primary without affecting the other UI component?

Create or use an existing plist file in the application. In the plist file, add a dictionary that fits the requirements for a TextAppearance.

<?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>TextAppearance.MyCustomBody</key>
	<dict>
		<key>fontSymbolicTrait</key>
		<string>traitItalic</string>
	</dict>
</dict>
</plist>

In the same plist file, add a dictionary that fits the requirements for ActionableButton.Primary.

<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>TextAppearance.MyCustomBody</key>
	<dict>
		<key>fontSymbolicTrait</key>
		<string>traitItalic</string>
	</dict>
  <key>ActionableButton.Primary</key>
	<dict>
		<key>textAppearance</key>
		<string>TextAppearance.Body</string>
	</dict>
</dict>
</plist>

When using HaapiUIKitApplicationBuilder, the plist file name needs to be provided. For example, if the plist file is named: "MyCustom.plist". As explained above in the AppDelegate when using HaapiUIKitApplicationBuilder, a new method needs to be invoked.

HaapiUIKitApplicationBuilder(haapiUIKitConfiguration: haapiUIKitConfiguration)
.setThemingPlistFileName("MyCustom") // It referes to MyCustom.plist. 
.build()

Now a component that uses ActionableButton.Primary style will only be affected by this change. The change only affects the textAppearance. As highlighted above, there is no need to mention the other keys because the other keys/values are taken from the initial ActionableButton.Primary definition from IdsvrHaapiUIKit.

Define another color in a style definition

textColorName is a key that exists in many style definitions. For example. this key is used in ActionableButton.Primary.

How to define a textColorName for ActionableButton.Primary ?

In the application Asset catalog, define a new set of colors that is named: "pastel_color".

Create or use an existing plist file in the application. In the plist file, add a dictionary that fits the requirements for a TextAppearance.

<?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>ActionableButton.Primary</key>
	<dict>
		<key>textColorName</key>
		<string>pastel_color</string>
	</dict>
</dict>
</plist>

When using HaapiUIKitApplicationBuilder, the plist file name needs to be provided. For example, if the plist file is named: "MyCustom.plist". As explained above in the AppDelegate when using HaapiUIKitApplicationBuilder, a new method needs to be invoked.

HaapiUIKitApplicationBuilder(haapiUIKitConfiguration: haapiUIKitConfiguration)
.setThemingPlistFileName("MyCustom") // It referes to MyCustom.plist. 
.build()

Now a component that uses ActionableButton.Primary style will only be affected by this change. The change only affects the textColorName. As highlighted above, there is no need to mention the other keys because the other keys/values are taken from the initial ActionableButton.Primary definition from IdsvrHaapiUIKit.

Reference documentation

OAuthLifecycle

With the OAuthTokenModel at hand, it is possible to request the server to refresh the currently held token in order to be able to continue accessing the resources for longer without having to go through the Haapi Flow.

To do so, an OAuthLifecycle API is provided and the user is able to easily refresh the token by using it like below.

// if OAuthTokenModel's refreshToken is nil or empty, we cannot execute the refresh token request.
if let refreshToken = (oauthModel as? OAuthTokenModel)?.refreshToken, !refreshToken.isEmpty {
    OAuthLifecycle.refreshToken(refreshToken: refreshToken, 
                                haapiUIKitApplication: haapiUIKitApplication,
                                lifecycleResultListener: self)// self is an instance of OAuthLifecycleResultListener
}

Configuring external apps integration

When integrating with external providers, third party apps required for the authentication flow that are launched from within the application must have their scheme's declared in the app's plist info file. The third party app's scheme list must be declared in the Queried URL Schemes configuration section like below. You can find relevant information here.

<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	...
    <key>LSApplicationQueriesSchemes</key>
    <array>
        <string>bankid</string>
        ...
        <string>other_app_scheme</string>
    </array>
</dict>
</plist>

Autofill for username and password text fields

Username and password autoFill are supported when running the Haapi flow. However, it is possible to improve the user experience by setting up the application’s associated domains as explained here.

HaapiLogger

When using IdsvrHaapiUIKit, it is possible to display the logs in the console as demonstrated below.

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
  // ...
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // ...
      	HaapiLogger.followUpTags = DriverFollowUpTag.allCases + SdkFollowUpTag.allCases + UIKitFollowUpTag.allCases // To filter logs.
        return true
    }
  // ...
}

Display logs by configuring HaapiLogger.followUpTags

// Driver, Sdk, and UIKit logs are displayed in the console
HaapiLogger.followUpTags = DriverFollowUpTag.allCases + SdkFollowUpTag.allCases + UIKitFollowUpTag.allCases
// Only UIKit logs are displayed in the console.
HaapiLogger.followUpTags = UIKitFollowUpTag.allCases

⚠️ If HaapiLogger.followUpTags is not set, then no logs are displayed in the console.

Supported log levels

HaapiLogger only uses the following log levels:

Log level How to enabled Default value
Debug HaapiLogger.isDebugEnabled It is set to true in DEBUG mode. Otherwise, default value is false but can be set by the developer if needed.
Info HaapiLogger.isInfoEnabled It is set to true.
Warning HaapiLogger.isWarningEnabled It is set to true.
Error HaapiLogger.isErrorEnabled It is set to true.

Display masking sensitive data

By default, HaapiLogger.isSensitiveValueMasked is set to true. With this configuration, logs are displayed with masking value such as:

2023-08-08 22:03:13.866367+0200 IdsvrHaapiUIKitApp[19098:404410] [HAAPI_DRIVER_HTTP] [DEBUG] HaapiTokenManager.378 : Requesting challenge from *****/cat

To remove the masking, HaapiLogger.isSensitiveValueMasked has to be set to false. Now with this configuration, logs are displayed without masking value but followed with warnings logs such as:

2023-08-08 22:06:18.982975+0200 IdsvrHaapiUIKitApp[19269:408868] [UNSECURED] ********** SENSITIVE VALUE IS UNMASKED **********
2023-08-08 22:06:18.983071+0200 IdsvrHaapiUIKitApp[19269:408868] [UNSECURED] ********** HaapiLogger.isSensitiveValueMasked must be set to true in `release` mode. **********
2023-08-08 22:06:18.983182+0200 IdsvrHaapiUIKitApp[19269:408868] [HAAPI_DRIVER_HTTP] [DEBUG] HaapiTokenManager.503 : Requesting CAT from https://localhost:8443/dev/oauth/token/cat

❗️Setting this value to false is only recommended when debugging.

Read the logs

All logs are structured like the following:

2023-06-29 16:31:50.845676+0200 IdsvrHaapiUIKitApp[66219:26995872] [HAAPI_UI_THEMING] [DEBUG] UIStylesContainer.34 : Loading styles definitions from IdsvrHaapiUIKit

It is possible to filter the Haapi logs by using the prefix: HAAPI.

To filter Driver logs, use the prefix: HAAPI_DRIVER. Here are the following up tags for the driver:

To filter Sdk logs, use the prefix: HAAPI_SDK. Here are the following up tags for the sdk:

To filter UI logs, use the prefix: HAAPI_UI. Here are the following up tags for the UIKit:

Additional note

If more control over transport/serialization or custom behavior is needed, the HAAPI iOS Driver and HAAPI iOS SDK can be used as they define lower-level building blocks to issue HAAPI requests.

Reference Documentation

Protocols

Structs

Classes

Enums

Extensions

Typealiases