DEA SDK for iOS¶
Detect whether an app session comes from a real person. The SDK observes touch, stylus, pointer, scroll and device-motion input across every window of your iOS app and scores the session on the device.
On this page: Prerequisites · Install · Initialize the SDK · Ask for consent · Use the classification · Verify your setup · Before you release · Next steps
Prerequisites¶
- An
appIdandappKeyfor this app — see Accounts and keys - iOS 13.0 or later deployment target
- Xcode with Swift 5.9 or later
- For Attest with App Attest: iOS 14.0 or later on the device
- For the SwiftUI consent alert: iOS 15.0 or later (UIKit apps can ask from iOS 13)
The SDK builds for iOS devices (arm64) and the iOS Simulator (arm64 and x86_64).
Install¶
Not available yet. DEA does not publish the iOS SDK for release 0.19.0. There is no Swift package URL and no CocoaPods podspec to install it from: the podspec URLs on
sdk.dep-protocol.comanswer404, and the release manifest lists no iOS package.
The SDK is two modules: DeaSDK, the SDK itself, and DeaBase, which DeaSDK depends on at the
exact same version and which defines a few shared types (Consent, SessionInfo, JSONValue,
CaptureEvent, KeyClass). Your app target links DeaSDK, and DeaBase comes with it. Import
DeaBase as well in files that use those types.
Platform setup¶
No setup is needed for capture. The SDK needs no permission prompt and no Info.plist entry:
it reads touches through passive gesture recognizers and motion through Core Motion's device
motion, neither of which asks the user. It does not change your app delegate or swizzle anything.
Attest has additional setup (the App Attest capability).
Privacy manifest¶
Warning: The SDK does not include a privacy manifest (
PrivacyInfo.xcprivacy). Your app's own privacy manifest has to cover what the SDK does.
The SDK calls these APIs, which Apple lists as required-reason APIs:
| API | Apple's category | Why the SDK calls it |
|---|---|---|
UserDefaults |
NSPrivacyAccessedAPICategoryUserDefaults |
Keeps the user's consent answer |
ProcessInfo.systemUptime |
NSPrivacyAccessedAPICategorySystemBootTime |
Timestamps scroll and hover input on the same clock as touches |
stat, lstat |
NSPrivacyAccessedAPICategoryFileTimestamp |
During Attest, checks for files a jailbreak leaves behind |
What the SDK records, stores and sends is described on Privacy and network.
Initialize the SDK¶
Create one DeaSdk for the whole app and call start() as soon as the app launches, so that
capture covers the whole session. Keep the instance somewhere every screen can reach it:
import DeaSDK
enum Dea {
/// The app's one DEA SDK instance. Nil until `start()` has run.
private(set) static var sdk: DeaSdk?
static func start() {
guard sdk == nil else { return }
let dea = DeaSdk(
options: DeaSdkOptions(appId: "YOUR_APP_ID", appKey: "YOUR_APP_KEY")
)
dea.start()
sdk = dea
}
}
appId and appKey are the pair DEA issued for this iOS app (see
Accounts and keys). Reading the classification makes no
request to DEA and does not use appKey; Attest needs it, and without
it answers .unsupported without contacting DEA. See Options.
The examples in this guide use this Dea.sdk holder.
Note: The samples in this guide are written for the Swift 5 language mode. In the Swift 6 language mode, declare the holder's property as
nonisolated(unsafe) private(set) static var sdk: DeaSdk?.
SwiftUI¶
import SwiftUI
@main
struct MyApp: App {
init() {
Dea.start()
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
UIKit¶
import UIKit
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
Dea.start()
return true
}
}
- Call
start()once, on the main thread, at launch. Windows created later — alerts, a second scene on iPad — are picked up when they appear. - Create one instance. There is no shared singleton: each
DeaSdkyou create captures on its own, so keep the one you made. - The initializer never throws. If the scoring engine
cannot load, the SDK still starts,
readyisfalse, andcoreFaultsays why. - Capture is global: every touch in every window, trackpad and mouse scrolling, pointer hover, the motion sensors, and app lifecycle. There is no view to wrap and nothing to register.
- When it is created, the SDK checks DEA's server for a newer scoring model, in the background. Nothing waits for that check. See Options.
- Nothing is recorded until the user consents. See the next step.
Ask for consent¶
By default the SDK records nothing until the user agrees. This step is required: an app that never asks collects no data and never gets a classification.
The SDK ships a ready-made alert. In SwiftUI (iOS 15 or later), attach it to your first screen:
import DeaBase
import DeaSDK
import SwiftUI
struct ContentView: View {
@State private var askingConsent = false
var body: some View {
let home = Text("Home")
.onAppear {
if let dea = Dea.sdk, !dea.consent.isAnswered {
askingConsent = true
}
}
if let dea = Dea.sdk {
home.deaConsent(dea.consent, asking: $askingConsent) { agreed in
print("DEA consent: \(agreed)")
}
} else {
home
}
}
}
In UIKit, ask from your first view controller:
import DeaSDK
import UIKit
class HomeViewController: UIViewController {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
Dea.sdk?.requestConsent(from: self) { agreed in
print("DEA consent: \(agreed)")
}
}
}
The answer is stored on the device. To use your own UI, change the wording, or ask again, see User Consent.
Use the classification¶
Read the classification at the moment you make a decision.
import DeaSDK
func onSignUp(_ form: SignUpForm) {
let result = (try? Dea.sdk?.getClassification())?.result ?? .insufficient
switch result {
case .human:
submitSignUp(form)
case .bot, .undetermined, .insufficient:
showChallenge() // your fallback: a captcha, a delay, a manual review
}
}
Replace SignUpForm, submitSignUp and showChallenge with your own.
getClassification() first waits for input the app has already received to be scored — usually
nothing — then reads the score and classifies it against the loaded model's thresholds. It does not
call the network. .insufficient means the session is not
sufficient yet — it has fewer
actions than the model asks for, 8 with the built-in model (see
Evidence) — and value is nil. .undetermined
means the score falls between the model's two thresholds. Decide what your app does in each case,
rather than treating it as .human or .bot. How the score and the thresholds work is explained in
How scoring works.
Verify your setup¶
The production SDK writes no log. Check it from code instead:
-
Run the app on a device or the simulator, accept the consent alert, and use the app — tap, scroll, swipe — for at least 8 actions, the number the built-in model needs.
-
Print the SDK's state, for example from a debug button:
import DeaSDK
if let dea = Dea.sdk {
print("ready:", dea.ready) // true
print("fault:", dea.coreFault ?? "none") // none
print("consent:", dea.consent.decision) // granted
print("session:", dea.sessionInfo.session) // a UUID
print("sdk:", dea.sessionInfo.sdk.version) // the SDK version
print("model:", dea.modelInfo) // the model scoring now
print("motion Hz:", dea.sessionInfo.device.sensorRate) // 50 on a device
print("processed:", dea.coreCounts.samples) // grows as you touch
print("score:", String(describing: try? dea.getClassification()))
}
processedshould grow while you touch the screen, andgetClassification().resultleaves.insufficientonceactionsreaches 8. Ifreadyisfalseor nothing grows, see Troubleshooting.
sensorRate is 0 on the simulator, which has no motion sensors.
Before you release¶
- [ ]
appKeyis set, if you use Attest — see Options - [ ] The consent alert is shown, or your own consent flow calls
setConsent— see User Consent - [ ] The classification is read at the decision point, with a fallback for
.bot,.undeterminedand.insufficient— see Human Scoring - [ ] Capture is paused on password and payment screens — see Sensitive Screens
- [ ]
reset()is called on logout — see Sessions - [ ] The App Attest capability is added, if you use Attest — see Attest
- [ ]
attest()andrenewAttestation()are never called on the main thread - [ ] Your app's privacy manifest covers the SDK — see Privacy manifest
See also Going to production.
Next steps¶
- User Consent — customize the alert or use your own
- Human Scoring — read the classification at a decision point
- Sensitive Screens — pause capture on password and payment screens
- Sessions — start a new session on logout
- Options — every option the SDK accepts
- Privacy and network — what the SDK records, stores and sends
- Troubleshooting