> For the complete documentation index, see [llms.txt](https://docs.infocus.company/ios-sdk/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.infocus.company/ios-sdk/premium-ios-sdk.md).

# Premium iOS SDK

* Version: **2.0.0**
* Minimum supported iOS version: **15.8.3** (this is what we test)
* Minimum compiling iOS version: **14** (this is where it compiles, but it does not actually work, and we block it by a warning message on the SDK initial screen)
* UI framework: SwiftUI
* Appearance: Light/Dark
* Supported orientations: Portrait
* SDK Size: up to 2 MB
* Supported languages: English/Russian/Kazakh

## iOS Compatibility

<table><thead><tr><th width="152.59375"></th><th></th></tr></thead><tbody><tr><td><strong>iOS Version</strong></td><td><strong>Expected behavior</strong></td></tr><tr><td>&#x3C; 14.0</td><td>App cannot be installed</td></tr><tr><td>14.0 – 15.8.2</td><td>Installs, displays upgrade notice</td></tr><tr><td>15.8.3+</td><td>Full functionality</td></tr></tbody></table>

## How to setup the SDK

To get the dependency, you first need to configure access to our repository.

1. Install AWS CLI using this [guide](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-version.html).
2. Configure AWS CLI using the credentials provided by us. To do this, run the following command in your terminal.

```shell
> aws configure

# After executing the command, you will be prompted to enter the following information
AWS Access Key ID [None]: <YOUR_ACCESS_KEY>
AWS Secret Access Key [None]: <YOUR_SECRET_KEY>
Default region name [None]: eu-central-1
Default output format [None]: json
```

3. Initialise your app

```shell
swift package init --type executable
```

3. Configure your swift client using this AWS CLI CodeArtifact command

```sh
aws codeartifact login --tool swift --repository premium-sdk --domain infocus --domain-owner 346627266603 --region eu-central-1
```

4. Modify the Package.swift file within your application project directory to adjust the package dependencies utilized by your project. If the Package.swift file does not have a dependencies section, include one. Within the targets section, add the targets requiring the dependency. It should look similar to code snippet below:

```swift
import PackageDescription

let package = Package(
    name: "premium-sdk-test",
    dependencies: [
        .package(id: "ios-premium.premium-ios-sdk", from: "2.0.0")
    ],
    targets: [
        // Targets are the basic building blocks of a package, defining a module or a test suite.
        // Targets can depend on other targets in this package and products from dependencies.
        .executableTarget(
            name: "premium-sdk-test",
            dependencies: ["premium_sdk"]
        ),
    ]
)
```

Use the **resolve** command to download the package dependencies from CodeArtifact.

```sh
swift package resolve
```

Alternatively, you can add packages by navigating to App > package dependencies on Xcode. “Click” on the + icon, Enter **ios.premium-ios-sdk**. Once the package is found, choose the package and “Click” on **Add Package**. After a second or two, the package name appears on the list. On the top right side, you can verify the source repository (next to the **Registry** label).

5. Import the framework

```swift
import premium_sdk
```

Add strings to Info.plist to avoid crashes&#x20;

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

6. There are 2 convenience initialisers for SDK: by client token and by customer ID (like shown in code sample below). \
   \
   To init the SDK by client token you will need to set a client token and in which way the token was retrieved (by list of phone hashes or by customer id). **To obtain this token, your backend must have the appropriate API.** You need to implement the logic for retrieving this token from your backend and passing it to the SDK. Init ConciergeSDK with your client token

```swift
let sdk = PremiumSDK(
    clientToken: "<<YOUR CLIENT TOKEN>>"
)
```

7. \[Optionally] Set language:

```swift
sdk.setLanguage(.en)
```

8. \[Optionally] Set colors:

```swift
sdk.setColors(
    SharedColors(
        mainColor: Color(hex: "#C28BFC"),
        mainColorTinted: Color(hex: "#E3DEFC"),
        defaultShadowColor: Color.dynamicColor(
            light: Color(red: 0.525, green: 0.442, blue: 0.613, opacity: 0.13),
            dark: Color(red: 0.035, green: 0.031, blue: 0.157, opacity: 0.5)
        ),
        mainBlack: Color.dynamicColor(
            light: Color(hex: "#090828"),
            dark: Color(hex: "#E3DEFC")
        ),
        mainGray: Color.gray,
        mainWhite: Color(hex: "#FFFFFF"),
        background: ColorSettings(
            withSolidColor: Color.dynamicColor(
                light: Color(hex: "#F6F6FA"),
                dark: Color(hex: "#090828")
            )
        ),
        primaryButtonBackground: ColorSettings(withSolidColor: Color(hex: "#C28BFC")),
        primaryButtonBorderColor: Color(hex: "#C28BFC"),
        primaryButtonShadowColor: Color.dynamicColor(
            light: Color(red: 0.525, green: 0.442, blue: 0.613, opacity: 0.13),
            dark: Color(red: 0.035, green: 0.031, blue: 0.157, opacity: 0.5)
        ),
        textFieldBackgroundColor: Color(hex: "#FFFFFF"),
        textFieldTextColor: Color(hex: "#090828"),
        textInputBorderColor: Color(hex: "#090828").opacity(0.12),
        textInputUnderlineColor: Color(hex: "#090828"),
        hintBackgroundColor: Color(hex: "#090828").opacity(0.07),
        hintTextColor: Color(hex: "#090828").opacity(0.5),
        radioButtonDefaultStateColor: Color(hex: "#090828").opacity(0.35),
        radioButtonSelectedStateColor: Color(hex: "#C28BFC"),
        spinnerColor: Color(hex: "#C28BFC"),
        successIconTintColor: Color(hex: "#C28BFC"),
        accordionBorderColor: Color(hex: "#090828").opacity(0.12),
        intermediateBackgroundColor: Color.dynamicColor(
            light: Color(hex: "#FFFFFF"),
            dark: Color(hex: "#1E1240")
        ),
        errorColor: Color(hex: "#D92D20"),
        hyperlinkColor: Color.blue,
        iconTint: Color(hex: "#C28BFC"),
        benefitCardOverlayTextColor: Color.white,
        benefitCardOverlayGradientStartColor: Color.black.opacity(0.35),
        benefitCardOverlayGradientEndColor: Color.black.opacity(0.15),
        errorPopupIconBackgroundColor: Color(hex: "#D92D20").opacity(0.08),
        errorPopupIconBorderColor: Color(hex: "#D92D20").opacity(0.2),
        errorPopupIconForegroundColor: Color.white,
        progressBarFilledColor: Color(hex: "#C28BFC"),
        progressBarTrackStripeColor: Color(.systemGray4).opacity(0.6),
        progressBarTrackBackgroundColor: Color(.systemGray5).opacity(0.5)
    )
)
```

There is also an option to use pre-define color themes to avoid setting the long list of colors. A the moment SDK supports 2 color schemes: **light** and **dark.** If you want to resent previous theme then you need to specify **custom** theme

```swift
sdk.setColorTheme(.light)
```

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

9. \[Optionally] Set images\
   Now there is an ability to set different sets of images for different sub-systems of the SDK. At the moment of writing this documentation there are 2 main subsystems: Concierge and Shared (which are used in both Concierge and Benefits parts of SDK).\
   So in order to specify images you need to invoke code similar to what is shown below:

```swift
sdk.setSharedImages(
    SharedImages(
        visaLogo: Image(systemName: "creditcard.fill"),
        documentUploadIcon: Image(systemName: "paperclip"),
        documentUploadRemoveIcon: Image(systemName: "trash")
    )
)

sdk.setConciergeImages(
    ConciergeImages(
        conciergeAvatarImage: Image(systemName: "person.crop.circle.fill"),
        infoLogoImage: Image(systemName: "info.circle.fill"),
        tasksState1: Image(systemName: "1.circle.fill"),
        tasksState2: Image(systemName: "2.circle.fill"),
        tasksState3: Image(systemName: "3.circle.fill")
    )
)
```

10. \[Optionally] Set fonts:\
    This customisation is common for Concierge and Benefits sub-systems and will be applied in both of them

```swift
sdk.setFonts(
    SharedFonts(customFontName: "Rubik-Medium")
)
```

11. \[Optionally] Set benefits list view screen banner:\
    SDK allows to show additional element on benefits list view which displays HTML content. It is configured as JSON which looks like shown below

```swift
let bannerJSON = """
{
  "additionalInfo": {
    "title": "Banner Title"
  },
  "info": "{\\"title\\":\\"Some info here\\"}",
  "text": "<!DOCTYPE html><html><head><meta name='viewport' content='width=device-width, initial-scale=1.0'></head><body><h3>Card benefits</h3><p>Your custom HTML banner content.</p></body></html>"
}
"""

sdk.setBenefitsUISettings(
    BenefitsUISettings(
        benefitsShowsCheckSpending: true,
        benefitsListBannerJSON: bannerJSON
    )
)
```

11. \[Optionally] Set custom texts if needed:\
    This customisation is only for Concierge sub-system

```swift
sdk.setConciergeTexts( 
    ConciergeTexts(
        chatInfoAdditionalText: "For questions about Premium cards and transactions, please contact the XXXX toll-free line."
    )
)
```

12. \[Optionally] Set other UI settings if needed:\
    The idea is the same as with setting custom images: there are 3 subsystems (Concierge, Benefits and Shared which is used in both Concierge and Benefits part of SDK)

<pre class="language-swift"><code class="lang-swift">sdk.setSharedUISettings(
    SharedUISettings(
        primaryButtonCornerRadius: 12,
        primaryButtonBorderType: .solid,
        primaryButtonBorderWidth: 1,
        primaryButtonShadowEnabled: true,
        primaryButtonShadowRadius: 10,
        dateTimeInputMode: .textInput,
        textInputBorderType: .solid,
        textInputBorderWidth: 1,
        showTextInputUnderline: true,
        textInputUnderlineWidth: 1,
        accordionBorderType: .solid,
        accordionBorderWidth: 1,
        documentUploadStyle: .compact
    )
)

sdk.setBenefitsUISettings(
    BenefitsUISettings(
        benefitsShowsCheckSpending: true
    )
)

sdk.setConciergeUISettings(
<strong>    ConciergeUISettings(
</strong>        chatStyle: .messenger,
        chatFilePickerEnabled: true,
        shouldShowVisaLogoInInfo: true
    )

)
</code></pre>

13. Set delegate and implement delegate methods&#x20;

<pre class="language-swift"><code class="lang-swift"><strong>sdk.setDelegate(self)
</strong></code></pre>

14. Call start() function

```swift
sdk.start()
```

15. Instantiate SwiftUI view:\
    Depending on which sub-system of SDK you want to use (Concierge/Benefits) you should invoke corresponding function

```swift
sdk.createBenefitsView() // or 'sdk.createConciergeView()'
```

* The code snippet above opens benefits list view which allows to navigate among benefits. If you are not interested in this step and want to navigate directly to benefit internal you may pass benefit ID as shown in code snippet below.

```swift
sdk.createBenefitsView(
    withSelectedBenefitId: "<SOME_ID_HERE>"
)
```

16. \[Optionally] Clear user-specific data (for example during logout). This method clears keychain, files cache and user settings

```swift
sdk.flush()
```

Due to the tab bar and navigation bars, It should be presented as a full screen cover or as separate window\
\
Full example:

```swift
import SwiftUI
import premium_sdk

struct InputView: View {
    var body: some View {
        NavigationView {
            VStack(spacing: 24) {
                Spacer()

                Text("Authentication")
                    .font(.title2)
                    .fontWeight(.semibold)

                TextField("Access token", text: $viewModel.authToken)
                    .textFieldStyle(RoundedBorderTextFieldStyle())
                    .autocapitalization(.none)
                    .disableAutocorrection(true)

                Toggle("Auth by customer ID", isOn: $viewModel.isAuthByCustomerId)

                if case .authFailed(let message) = viewModel.state {
                    Text(message)
                        .foregroundColor(.red)
                        .padding(.horizontal)
                }

                if viewModel.state == .authenticating {
                    ProgressView("Authenticating...")
                }

                HStack(spacing: 16) {
                    Button("Benefits") {
                        viewModel.handleBenefitsButtonPressed()
                    }

                    Button("Concierge") {
                        viewModel.handleConciergeButtonPressed()
                    }
                }
                .buttonStyle(.bordered)

                NavigationLink(
                    tag: .benefits,
                    selection: $targetRoute,
                    destination: {
                        if let sdk = viewModel.sdk {
                            AnyView(
                                sdk.createBenefitsView(
                                    withSelectedBenefitId: nil,
                                    withConcierge: true,
                                    usingSwiftUINavigation: true
                                )
                            )
                        } else {
                            AnyView(EmptyView())
                        }
                    },
                    label: { EmptyView() }
                )

                NavigationLink(
                    tag: .concierge,
                    selection: $targetRoute,
                    destination: {
                        if let sdk = viewModel.sdk {
                            AnyView(
                                sdk.createConciergeView(
                                    usingSwiftUINavigation: true
                                )
                            )
                        } else {
                            AnyView(EmptyView())
                        }
                    },
                    label: { EmptyView() }
                )

                Spacer()
            }
            .padding()
        }
        .onAppear {
            viewModel.onAuthenticated = { target in
                switch target {
                case .benefits:
                    targetRoute = .benefits
                case .concierge:
                    targetRoute = .concierge
                case .none:
                    break
                }
            }
        }
    }

    @StateObject private var viewModel = InputViewModel()
    @State private var targetRoute: Route?

    private enum Route: Hashable {
        case benefits
        case concierge
    }
}

@MainActor
final class InputViewModel: ObservableObject {
    enum State: Equatable {
        case idle
        case authenticating
        case authFailed(String)
    }

    enum TargetScreen {
        case none
        case concierge
        case benefits
    }

    @Published var state: State = .idle
    @Published var authToken: String = ""
    @Published var isAuthByCustomerId: Bool = false

    private(set) var sdk: PremiumSDK?
    private(set) var targetScreen: TargetScreen = .none

    var onAuthenticated: ((TargetScreen) -> Void)?

    func handleBenefitsButtonPressed() {
        targetScreen = .benefits
        authenticate()
    }

    func handleConciergeButtonPressed() {
        targetScreen = .concierge
        authenticate()
    }

    private func authenticate() {
        guard !authToken.isEmpty else {
            state = .authFailed("Auth info is not provided")
            return
        }

        state = .authenticating

        sdk = isAuthByCustomerId
            ? PremiumSDK(withCustomerIdAccessToken: authToken)
            : PremiumSDK(clientToken: authToken)

        configureSDK()

        sdk?.setDelegate(self)
        sdk?.start()
    }

    private func configureSDK() {
        sdk?.setLanguage(.en)

        // Use either a predefined theme...
        sdk?.setColorTheme(.light)

        // ...or full manual color customization.
        // sdk?.setColors(SharedColors(...))

        sdk?.setFonts(
            SharedFonts(
                customFontName: nil
            )
        )

        sdk?.setSharedImages(
            SharedImages(
                visaLogo: Image(systemName: "creditcard.fill"),
                documentUploadIcon: Image(systemName: "paperclip"),
                documentUploadRemoveIcon: Image(systemName: "trash")
            )
        )

        sdk?.setConciergeImages(
            ConciergeImages(
                conciergeAvatarImage: Image(systemName: "person.crop.circle.fill"),
                infoLogoImage: Image(systemName: "info.circle.fill"),
                tasksState1: Image(systemName: "1.circle.fill"),
                tasksState2: Image(systemName: "2.circle.fill"),
                tasksState3: Image(systemName: "3.circle.fill")
            )
        )

        sdk?.setSharedUISettings(
            SharedUISettings(
                primaryButtonCornerRadius: 12,
                primaryButtonBorderType: .solid,
                primaryButtonBorderWidth: 1,
                primaryButtonShadowEnabled: true,
                primaryButtonShadowRadius: 10,
                dateTimeInputMode: .textInput,
                textInputBorderType: .solid,
                textInputBorderWidth: 1,
                showTextInputUnderline: true,
                textInputUnderlineWidth: 1,
                accordionBorderType: .solid,
                accordionBorderWidth: 1,
                documentUploadStyle: .compact
            )
        )

        sdk?.setBenefitsUISettings(
            BenefitsUISettings(
                benefitsShowsCheckSpending: true
            )
        )

        sdk?.setConciergeUISettings(
            ConciergeUISettings(
                chatStyle: .messenger,
                chatFilePickerEnabled: true,
                shouldShowVisaLogoInInfo: true
            )
        )

        sdk?.setConciergeTexts(
            ConciergeTexts(
                chatInfoAdditionalText: "For questions about Premium cards and transactions, please contact support."
            )
        )
    }
}

extension InputViewModel: PremiumSDK.Delegate {
    func conciergeSDKDidStart(isSubscribed: Bool) {
        state = .idle
        onAuthenticated?(targetScreen)
    }

    func conciergeSDKDidSubscribe() {
        state = .idle
        onAuthenticated?(targetScreen)
    }

    func conciergeSDKDidStartWithError(_ error: Error) {
        state = .authFailed(error.localizedDescription)
    }

    func conciergeSDKCantAuthorize() {
        state = .authFailed("Failed to authorize")
    }
}
```
