Skip to main content

Native iOS and Android integration

You can integrate the SDK binaries directly into a Swift or Kotlin application. See the overview for delivery and environments, and the reference for results and errors.

iOS — Swift​

Install the framework​

Add CertificallTrust.xcframework to General > Frameworks, Libraries, and Embedded Content in Xcode, selecting Embed & Sign.

Add usage descriptions to Info.plist:

<key>NSCameraUsageDescription</key>
<string>Required to capture certified photos.</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Required to certify the photo's location.</string>
<key>NSMotionUsageDescription</key>
<string>Required to collect motion during capture.</string>

Keep a CertificallTrustSDK instance in your controller. Request permissions before configuring it:

import CertificallTrust

// Property of your UIViewController:
let sdk = CertificallTrustSDK()

func prepareCapture() {
sdk.requestCameraPermission { [weak self] granted in
guard let self = self, granted else { return }
self.sdk.requestLocationPermission { [weak self] status in
guard let self = self, status == "granted" else { return }
self.sdk.requestMotionPermission()
self.sdk.configure(
apiKey: "<your SDK API key>",
baseUrl: "https://dev.certificall.app/certificall/",
appVersion: "1.0.0"
)
}
}
}

Capture and upload​

Start capture from your visible controller, with no other modal screen already presented:

sdk.takePhoto(
from: self,
reportToken: "JOB-2026-001",
metadata: ["site": "Paris", "zone": "A1"]
) { [weak self] photo, error in
guard let photo = photo else {
print("Capture: \(CertificallSDKError.code(of: error))")
return
}

// Save photo.toJSON() in your storage before a deferred upload.
self?.sdk.sendPhoto(photo) { sent, error in
guard let sent = sent else {
print("Upload: \(CertificallSDKError.code(of: error))")
// Keep the capture for a retry, depending on the error.
return
}
DispatchQueue.main.async {
print("PDF certificate: \(sent["pdfUrl"] ?? "")")
}
}
}

The upload response is a [String: Any] dictionary. The upload callback can run outside the main thread: use DispatchQueue.main to update your interface.

To save a capture and restore it:

let data = try JSONSerialization.data(withJSONObject: photo.toJSON())
// Save `data` in your storage.

let json = try JSONSerialization.jsonObject(with: data) as! [String: Any]
let restored = try CapturedPhoto.fromJSON(json)
sdk.sendPhoto(restored) { sent, error in
// Same upload workflow as for the initial capture.
}

For a typed error, read CertificallSDKError.code(of: error) and the reasons, httpStatus and serverCode properties on CertificallSDKError.

Android — Kotlin​

Install the binary​

Place the delivered certificall-trust-sdk.aar binary in app/libs/. In your application's build.gradle, add the binary and its dependencies: a local AAR does not install them automatically.

dependencies {
implementation(files("libs/certificall-trust-sdk.aar"))
implementation("com.google.android.play:integrity:1.4.0")
implementation("com.google.android.gms:play-services-location:21.0.1")
implementation("androidx.exifinterface:exifinterface:1.3.7")
implementation("androidx.core:core-ktx:1.12.0")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2")
}

Declare permissions in AndroidManifest.xml:

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />

The SDK provides its own camera and file provider declarations. No additional FileProvider declaration is needed.

Configure and receive the camera result​

Keep the SDK instance in your Activity. Request camera and location permissions using Android APIs, then configure the SDK on the main thread once permissions have been granted:

import android.content.Intent
import app.certificall.sdk.CertificallTrustSDK

// Property of your Activity:
private val sdk by lazy { CertificallTrustSDK(applicationContext) }

private fun configureSdk() {
sdk.configure(
apiKey = "<your SDK API key>",
baseUrl = "https://dev.certificall.app/certificall/",
appVersion = "1.0.0"
)
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
sdk.handleActivityResult(this, requestCode, resultCode, data)
}

Forwarding onActivityResult() to the SDK is required to complete the capture call.

Capture and upload​

import app.certificall.sdk.CertificallError

sdk.takePhoto(
this,
"JOB-2026-001",
mapOf("site" to "Paris", "zone" to "A1")
) { photo, error ->
if (photo == null) {
println("Capture: ${CertificallError.codeOf(error)}")
return@takePhoto
}

// Save photo.toJson().toString() in your storage.
sdk.sendPhoto(photo) { sent, sendError ->
if (sent == null) {
println("Upload: ${CertificallError.codeOf(sendError)}")
// Keep the capture for a retry, depending on the error.
return@sendPhoto
}
runOnUiThread {
println("PDF certificate: ${sent.optString("pdfUrl")}")
}
}
}

The response is a JSONObject. The upload callback runs outside the main thread: use runOnUiThread to update your interface.

To restore saved JSON:

import app.certificall.sdk.models.CapturedPhoto
import org.json.JSONObject

val restored = CapturedPhoto.fromJson(JSONObject(serializedPhoto))
sdk.sendPhoto(restored) { sent, error ->
// Same upload workflow as for the initial capture.
}

Read the error code with CertificallError.codeOf(error). Device refusals are DeviceNotTrustedException instances (reasons), and upload errors are SendPhotoException instances (httpStatus, serverCode).

Release monitoring​

Native SDKs start network, position and motion monitoring during configuration. Call sdk.stop() when your workflow no longer needs them. Before a new series of captures, configure the SDK again to restart collection.