Overview

For iOS and MacOS, Keychain Core is provided as an Objective-C framework called

Keychain-ObjectiveC.xcframework.

It can be used by developers who are writing code using Objective-C, Objective-C++, or Swift. However, all examples herein provided will be in Swift.

The framework also packages the Keychain Core C++ library (libkeychain.dylib), so there is no need to explicitely add it to your projects.

Getting Started

For any iOS application, Keychain supplies three files that need to be placed in your application resource bundle. The files are, keychain.cfg, keychain.sql, and drop_keychain.sql. You will need to get and pass the paths to these files to the Gateway constructor when initializing the gateway.

As the Gateway class is lightweight in that it does not perform network calls. It only accesses the Keychain database. As such, they are generally safe to call on the UI thread. However, if you call Gateway methods on a background thread, be sure to dispatch the result to the main thread if the UI needs to be updated.

Likewise, the Monitor object performs (semi) blocking, synchronous pub/sub network operations to receive updates from Query servers. Monitor automatically creates a thread for you when onStart() is called.

Both Gateway and Monitor should be created in an instance of a singleton class that is accessible anywhere in your application that needs to use them. For example, if you are using Swift, you can create a service class that is an ObservableObject that contains both Gateway and Monitor instances as in the following example:

class GatewayService {

    var gateway: Gateway?

    private var monitor: MonitorService?

    private init() throws {
        try initializePaths()

        guard let configFile = configPath,
              let dropDbFile = dropDbPath,
              let createDbFile = createDbPath,
              let dbFilePath = dbPath else {
                  throw KeychainError.unexpected
              }

        let databasePath = "\(dbFilePath)/keychain.db"

        gateway = try Gateway(configFile, databasePath, false, dropDbFile, createDbFile)
        monitor = try MonitorService(dbFilePath: databasePath, gateway: gateway, refreshInterval: appConfig.refreshInterval)

        var address = ""
        var mnemonicList = NSMutableArray()
        try gateway?.seed(address, mnemonicList)
    }

    static let instance: GatewayService? = {
        do {
            let instance = try GatewayService()
            // setup code
            return instance
        } catch {
            Logger(label: GatewayService.typeName).error("Error initializing gateway: \(error)")
        }

        return nil
    }()

In this case, MonitorService is another service (created by the developer, not supplied by Keychain) that wraps the Monitor instance.

Because the constructor of the GatewayService class is private only the GatewayService class can instantiate itself. Consumers of GatewayService must call GatewayService.instance in order to get the singleton instance that contains the Gateway and Monitor instances.

In this case GatewayService is an example of an application level service that you would create (calling it whatever you want to). Keychain does not supply such a class. It is just an example of what you may consider doing in your own application.

In the main entry point of your application you can create a view model that instantiates the singleton instance of GatewayService (i.e. transactionViewModel), and is annotated with @StateObject, as follows:

@StateObject var transactionViewModel = TransactionViewModel()

Then add it to your main view using like this:app-name:

.environmentObject(transactionViewModel)

Now every view in your app will be able to access the Gateway and Monitor functions by calling methods on the transactionViewModel (implemented by you), which then call the appropriate methods in the GatewayService (also implemented by you), which then calls the appropriate methods in the Gateway and/or Monitor.