Overview
For iOS and MacOS, Keychain Core is provided as an Objective-C framework. In order to use it in a Swift project:
#import <Keychain-ObjectiveC/Gateway.h> #import <Keychain-ObjectiveC/Monitor.h> #import <Keychain-ObjectiveC/Asset.h> #import <Keychain-ObjectiveC/Persona.h> #import <Keychain-ObjectiveC/PersonaStatus.h> #import <Keychain-ObjectiveC/Contact.h> #import <Keychain-ObjectiveC/Facade.h> #import <Keychain-ObjectiveC/Uri.h> #import <Keychain-ObjectiveC/Certificate.h> #import <Keychain-ObjectiveC/LedgerResult.h> #import <Keychain-ObjectiveC/LedgerTransaction.h> #import <Keychain-ObjectiveC/SecurityLevel.h> #import <Keychain-ObjectiveC/Verification.h> #import <Keychain-ObjectiveC/Settings.h> #import <Keychain-ObjectiveC/WrapperErrorCodes.h> |
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 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
.