Accessing Sensibill API
Info
Currently this feature is only available for iOS. Direct native interfaces to the Sensibill APIs are not available on Android.
SDK provides the interfaces that allow a conveinent access to Sensibill API.
Overview
The main interface through which the integrator will interact with the Sensibill API Client is based on SensibillAPIProvider protocol. The protocol defines configuration and authorization for the API client, and provides the access to all Sensibill API endpoints.
An instance of the SensibillAPIProvider
can be obtained in one of the following two methods:
- Using
SensibillSDK.shared.apiClient
property (recommended method)
After the SDK is started viaSensibillSDK.start
(see: Launching the SDK ), the Sensibill API client available viaSensibillSDK.shared.apiClient
property is automatically initialized with the configuration and credentials used for SDK start. - Standalone invocation using
SensibillAPIClient.get
If the additional customization of the configuration is required, the Sensibill API client can also be started independently, without starting the SDK. In this case the integrator needs to provide a configuration, and set the user access token delegate (to access the user-specific information). See Standalone Invocation usingSensibillAPIClient.get
section below for more details.
The SensibillAPIProvider
allows to access the various categories of endpoints. Their naming matches the categories described in Sensibill API
docs.
Currently the following categories are supported:
- The Home endpoints
- The Authentication endpoints
- The Documents endpoints
- The Exports endpoints
- The Folders endpoints
- The Lookups endpoints
- The Users endpoints
Additional categories will be added in the future versions.
Each category contains one or more functions, that return a SensibillAPIClient.Task
, which can then be executed to send the request to the server. The task may require input models, and it has an associated output model. All input and output models are contained in SensibillAPIModel
namespace.
Upon completion the task will return the SensibillAPIClient.Task
itself with the result
property of type Result<Model, Error>
, which will contain the reuslt of the execution.
In general invocation of a Sensibill API Client endpoint involves the following steps:
- Obtain an instance of the
SensibillAPIProvider
guard let apiClient = SensibillSDK.shared.apiClient else {
return
}
- Access a category for which you’d like to send a request
let documents = apiClient.documents // returns DocumentsEndpointsProvider
- Create a task for a particular endpoint
do {
let task = try documents.documents() // returns SensibillAPIClient.Task<SensibillAPIModel.GetDocumentListResponseDto>
} catch {
// Error creating a task
}
- Execute the task and decide how to handle a response
task.execute { taskWithResult in
switch taskWithResult.result {
case .success(let model):
// ...
case .failure(let error):
// ...
case .none:
// task completed, but no result was set
}
}
The above code can also be converted to a shorter version:
try documents.documents().execute { taskWithResult in
// ...
}
Details
Authorization
Almost all Sensibill API endpoints require authorization:
- Depending on operation, endpoints in Authorization category may require client key and secret, refresh token, or JWT.
- Most of the endpoints in the Users category require an authorization with the client token.
For these endpoints the Sensibill API Client allows to provide the authorization directly with the request. This allows the integrator control the context and the flow of requests related to authentication, and retrieving client-specific information.
The majority of the endpoints, however, access user-specific information, and require an authorization with the user access token, which they obtain via the UserAccessTokenDelegate
protocol.
For Sensibill API Client available via the SensibillSDK.shared.apiClient
property, the UserAccessTokenDelegate
is automatically assigned to a correct user identity upon the SensibillSDK.start
completion. The UserAccessTokenDelegate
is unassigned with SDK is stopped.
For standalone invocation of the Sensibill API client, the caller must provide the UserAccessTokenDelegate
and manage its lifecycle. See Standalone Invocation using SensibillAPIClient.get
section below for more details.
- All the endpoints that require user access token will try to obtain the current token from the assigned
UserAccessTokenDelegate
just before the request is started. - If the token is unavailable, or if a request fails with the HTTP status code 401, depending on
TaskOptions
(see below), the request may fail, or may call theUserAccessTokenDelegate.updateToken
.
Retrying a Failed Task
In general, the SensibillAPIClient.Task.execute
can be called again to retry a failed task, if desired. Before execution, the task will create a new dataTask
in the underlying URLSession
, and will obtain the latest user access token.
Additionally, an integrator can configure a task to retry once automatically in the following cases:
- If the task could not obtain a current user access token
- If the task execution failed with the status code 401 (authorization error)
This option is turned off by default.
To enable the automatic retry:
- Obtain an instance of the task before execution
- Modify the task options to enable a retry
- Execute the task. It will now automatically retry if needed.
let task = try apiClient.documents.documents()
task.options.shouldRefreshTokenAndRetryOnStatusCode401 = true
task.execute { ... }
Additional Information
Standalone Invocation using SensibillAPIClient.get
Info
Information in this section is not applicable when using Sensibill API Client via the SensibillSDK.shared.apiClient
property.
Initialization
On standalone initialization, the SensibillAPIProvider
requires:
- an instance of SensibillAPIClient.Configuration , which defines various options for the Sensibill API client
- an instance of UserAccessTokenDelegate to be set for the provider instance after initialization to access all user-specific information.
- Implement the
UserAccessTokenDelegate
- Create an instance of
SensibillAPIClient.Configuration
- Get an instance of
SensibillAPIProvider
usingSensibillAPIClient.get
function with the configuration you created in step 2. - Create an instance of
UserAccessTokenDelegate
you implemented in step 1, and set it forSensibillAPIProvider
you created in step 3.
Note: an instance of SensibillAPIProvider
created using the above steps, must be retained by an integrator. Additionally, the SensibillAPIProvider
retains only the weak
instance of UserAccessTokenDelegate
, hence its lifecycle should be managed by a calling app.
// Step 1
class MyDelegate: UserAccessTokenDelegate {
func currentAccessToken(for cacheIdentifier: String) -> String? {
// ...
}
func updateToken(for cacheIdentifier: String, completion: @escaping (Error?) -> Void) {
// ...
}
}
// Step 2
let environmentUrl = "..." // Sensibill API Environment URL
let certificatePinningOn = true // or false
let configuration = SensibillAPIClient.Configuration(
baseUrl: environmentUrl,
certificatePinningEnabled: certificatePinningOn
)
// Step 3
let client = SensibillAPIClient.get(configuration: configuration)
// Step 4
let delegate = MyDelegate()
let cacheIdentifier = "..." // Identify the current user
client.setUserAccessTokenDelegate(delegate, withCacheIdentifier: cacheIdentifier)
Handling User Token
When Sensibill API Client is used via the SensibillSDK.shared.apiClient
property, the handling of the authorization for all user-related requests is done automatically in accordance with SDK state.
When Standalone client is instantiated, the handling of the Authorization must be handled by the caller with the help of UserAccessTokenDelegate
.
- To allow the access to user information, call
SensibillAPIProvider.setUserAccessTokenDelegate
. The client will ask the assigned delegate for user token for all requests that require a user token. - You can clear the user context any time using the
SensibillAPIProvider.resetUserAccessTokenDelegate
function. WhenUserAccessTokenDelegate
is not set, any reuqests that require user token will fail.
In order to ensure that the token for a correct user is served, provide the cacheIdentifier
to the client on SensibillAPIProvider.setUserAccessTokenDelegate
, and compare to a supplied cacheIdentifier in your implementation of UserAccessTokenDelegate
:
// UserAccessTokenDelegate implementation
class MyDelegate: UserAccessTokenDelegate {
let cacheIdentifier: String
init(cacheIdentifier: String) {
self.cacheIdentifier = cacheIdentifier
}
func currentAccessToken(for cacheIdentifier: String) -> String? {
guard cacheIdentifier == self.cacheIdentifier else { return nil }
// return current token
}
func updateToken(for cacheIdentifier: String, completion: @escaping (Error?) -> Void) {
guard cacheIdentifier == self.cacheIdentifier else {
completion(/* Error indicating an incorrect user context */)
return
}
// otherwise update the token
}
}