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.