iOS SDK 集成指南
概述
Width Identity SDK 为 iOS
应用提供即插即用的身份验证流程。支持 KYC 和 KYB 工作流。你
import WidthIdentity 并调用一个入口;SDK 即呈现完整的采集 /
活体 / 审核界面,并返回单一结果。公共 API 使用 Swift
原生类型(带关联值的枚举、结构体、闭包),从 Swift 中使用。
系统要求
| 要求 | 最低版本 |
|---|---|
| iOS | 16.0+(部署目标) |
| Xcode | 16+ |
| 依赖管理器 | Swift Package Manager 或 CocoaPods |
安装
方式 A:Swift Package Manager(推荐)
WidthIdentity-iOS 是私有仓库 —
在你的 GitHub 账号拿到访问权之前,Xcode 和 git
都只会提示仓库不存在,所以先确认访问权已开通,再执行下面的步骤。
在 Xcode 中:File > Add Package Dependencies…,输入:
https://github.com/width-com/WidthIdentity-iOS.git
或在 Package.swift 中:
dependencies: [
.package(url: "https://github.com/width-com/WidthIdentity-iOS.git", from: "1.0.11"),
],
targets: [
.target(name: "MyApp", dependencies: [
.product(name: "WidthIdentity", package: "WidthIdentity-iOS"),
]),
]方式 B:CocoaPods
WidthIdentity 发布在 Width 的私有 Specs 仓库,因此需在
Podfile 中声明该源:
该 Specs 仓库同样是私有的 — 只有你的 GitHub
账号拿到访问权之后,pod install
才解析得到;没有访问权时,这个源看起来就是「仓库不存在」。
source 'https://github.com/width-com/Specs.git' source 'https://cdn.cocoapods.org/' # default trunk, for any public pods target 'YourApp' do pod 'WidthIdentity', '~> 1.0.11' end
然后执行 pod install。
Info.plist 权限
SDK token 不再存放于 Info.plist。它在运行时通过
CredentialsConfig.sdkToken
传入(见快速开始)。相机、麦克风、语音识别三项使用描述都必须声明;照片库那一项可选:
<!-- Required: document capture and liveness -->
<key>NSCameraUsageDescription</key>
<string>Used to capture identity documents and complete the liveness check.</string>
<!-- Required: document verification records background audio (anti-fraud) -->
<key>NSMicrophoneUsageDescription</key>
<string>Used to record background audio during document verification to help prevent fraud.</string>
<!-- Optional: only if you let users pick a document image from the photo
library. The SDK uses the system PhotosPicker, which does not strictly
require this key. -->
<key>NSPhotoLibraryUsageDescription</key>
<string>Used to select identity document images.</string>
<!-- Required by App Store ingestion, never requested at runtime (see below) -->
<key>NSSpeechRecognitionUsageDescription</key>
<string>This app does not request speech recognition.</string>
NSCameraUsageDescription 与
NSMicrophoneUsageDescription
两项都是必填:证件扫描环节会录制背景音频用于反欺诈,缺了麦克风这个
key,应用会在运行时直接崩溃。SDK
没有任何与环境相关的 Info.plist key。
NSSpeechRecognitionUsageDescription
必填的原因不一样。SDK 运行时从不申请语音识别,但
AwsLiveness.xcframework 静态吸收了 Amplify 的 Predictions
插件,该插件引用了 SFSpeechRecognizer。缺这个 key,App
Store 会在入库(ingestion,上传包进入审核前的自动校验)阶段直接退回(错误码
ITMS-90683)—
而且是在整个二进制包传完之后才报,与这个接口有没有被调用无关。这个 key
的文案直说「本应用不使用语音识别」即可:权限永不申请,用户也就永远看不到这句话。
打包构成
SDK 一共包含八个
xcframework:WidthIdentity、SecCamera、onnxruntime、AwsLiveness、Veriff、DocumentReader、DocumentReaderCore、RegulaCommon,全部自动嵌入。其中
AwsLiveness 是自包含的,你不需要自己引入任何 Amplify 或
AWS 依赖。
快速开始
import WidthIdentity 提供两种等价的入口风格 —
按喜好选择即可。两者驱动完全相同的验证界面。
直接输入(UIKit)
import UIKit
import WidthIdentity
final class ViewController: UIViewController {
func startVerification() throws {
let input = try VerificationInput(
credentials: CredentialsConfig(
domain: "YOUR_DOMAIN",
sdkToken: "YOUR_SDK_TOKEN"
),
customer: CustomerConfig(
// Both customerId and email are required and must be non-empty.
customerId: "user-123",
email: "user@example.com"
)
)
WIDTHVerification.present(from: self, input: input) { result in
switch result {
case .completed(let id, let decision): // .approved / .rejected / .review
print("done:", id, decision)
case .waiting(let id): print("waiting:", id)
case .dismissed(let id): print("dismissed:", id ?? "(no execution)")
case .cancelled(let id): print("cancelled:", id)
case .failed(let code, let message, _): print("failed:", code, message)
}
}
}
}直接输入(SwiftUI)
import SwiftUI
import WidthIdentity
struct ContentView: View {
@State private var presenting = false
@State private var customerId = ""
@State private var email = ""
var body: some View {
Button("Verify") { presenting = true }
.widthVerification(isPresented: $presenting, input: {
// Lazy closure — built only when the cover is about to present.
try VerificationInput(
credentials: CredentialsConfig(
domain: "YOUR_DOMAIN", sdkToken: "YOUR_SDK_TOKEN"
),
customer: CustomerConfig(customerId: customerId, email: email)
)
}) { result in
// If the input closure throws, you'll receive .failed(code: "W-INIT-410", ...) here.
}
}
}Handler 风格入口(备选)
WIDTHMobileSDK 是另一种入口风格,以对象方式使用 —
先构造对象,设置属性与各个 handler 闭包,再调用
present。
import WidthIdentity
let sdk = WIDTHMobileSDK(accessToken: "YOUR_SDK_TOKEN") // RiskSec sdkToken
sdk.credentials = .init(domain: "YOUR_DOMAIN") // appId/apiKey decoded from accessToken
sdk.applicant = .init(customerId: "user-123", email: "user@example.com")
sdk.dayNightMode = .night // optional; omit to follow the system
sdk.language = .zh // optional; .en (default) / .zh
sdk.verificationHandler { isApproved in
print(isApproved ? "approved" : "finally rejected")
}
sdk.onStatusDidChange { sdk, _ in
print("status:", sdk.status, "exec:", sdk.executionId ?? "-")
if let fail = sdk.failReason { print("failed:", fail.code, fail.message) }
}
sdk.onEvent { _, event in /* INFO/WARN/ERROR lifecycle log */ } // optional
sdk.present(from: self)SwiftUI:
Button("Verify") { presenting = true }
.widthMobileSDK(isPresented: $presenting) {
let sdk = WIDTHMobileSDK(accessToken: "YOUR_SDK_TOKEN")
sdk.credentials = .init(domain: "YOUR_DOMAIN") // appId/apiKey decoded from accessToken
sdk.applicant = .init(customerId: customerId, email: email)
sdk.verificationHandler { isApproved in /* ... */ }
return sdk
}accessToken 里装的是
sdkToken;其余凭据在客户端通过
sdk.credentials / sdk.applicant
设置。凭据或申请人信息缺失这类配置错误,会表现为
sdk.status == .failed,并在
sdk.failReason 中给出原因。
事件日志(可选)
直接输入入口接受一个 SdkEventLogger,可将 SDK
生命周期事件(INFO / WARN /
ERROR)接入你自己的管道(Crashlytics、DataDog 等);回调触发前已脱敏
PII(个人身份信息)。Handler 风格入口通过
sdk.onEvent 暴露相同的事件流。
final class MyLogger: SdkEventLogger {
func onEvent(level: SdkLogLevel, tag: String, message: String, error: Error?) {
// forward to your logging backend
}
}
let input = try VerificationInput(credentials: ..., customer: ..., eventLogger: MyLogger())配置参考
输入结构与 Android
相同:VerificationInput、CredentialsConfig(domain
与 sdkToken)以及
CustomerConfig(customerId +
email,两者都必填)。字段详情参见
Android 配置参考。在 iOS 上,VerificationInput
的初始化器是 throws 的,并额外接受一个可选的
eventLogger。
iOS 特定的入口与主题覆盖:
| 符号 | 用途 |
|---|---|
WIDTHVerification.present(from:input:completion:)
|
UIKit 原生入口 |
View.widthVerification(isPresented:input:onResult:)
|
SwiftUI 原生入口 |
WIDTHMobileSDK(accessToken:) +
credentials/applicant
+
present(from:)
|
UIKit Handler 风格入口 |
View.widthMobileSDK(isPresented:makeSDK:) |
SwiftUI Handler 风格入口 |
ThemeConfig / ThemeMode |
可选主题覆盖 |
UILanguage |
可选的界面语言(.en 为默认值 /
.zh)
|
结果处理
直接输入的 VerificationResult
五种 Swift 枚举变体(与 Android 的五种变体对应):
| 变体 | 何时 |
|---|---|
.completed(executionId, .approved) |
服务端通过 |
.completed(executionId, .rejected) |
服务端拒绝 |
.completed(executionId, .review) |
提交在带外进行人工审核 |
.waiting(executionId) |
等待服务端通知最终决定 |
.dismissed(executionId?) |
用户退出但打算稍后返回(可恢复);若工作流尚未开始,executionId
为 nil
|
.cancelled(executionId) |
用户主动放弃;执行已销毁,不可恢复 |
.failed(code, message, executionId?) |
内部错误 |
.dismissed 表示用户暂停、可恢复;.cancelled
表示用户放弃、服务端执行已销毁(不可恢复)。若失败发生在执行创建之前,executionId
可能为 nil;如果存在,请在工单中附上该 ID
以加快排查。
Handler 风格入口
verificationHandler { (isApproved: Bool) }
在最终通过/拒绝决定时触发。sdk.status(WIDTHStatus):.approved、.finallyRejected、.pending(人工审核)、.incomplete(中途退出
/
等待,可恢复)、.cancelled(已销毁)、.failed。当为
.failed
时,sdk.failReason(WIDTHException)为
.initialization、.network、.api、.permission、.workflow、.unknown
之一 — 每个都暴露 code 与 message。
主题定制
验证界面默认跟随系统外观。用 dayNightMode
强制指定模式;省略则跟随系统。
let sdk = WIDTHMobileSDK(accessToken: "YOUR_SDK_TOKEN") sdk.dayNightMode = .night // optional; omit to follow the system
恢复与生命周期
与 Android 行为一致。在 iOS 上,活跃工作流期间会禁用 sheet 的下滑关闭手势,以防止意外丢失进度。
| 用户选择 | 行为 | 结果 | 可恢复? |
|---|---|---|---|
| 暂停并退出(保留进度) | 服务端执行被保留 | .dismissed |
是 |
| 结束验证(放弃) | 服务端取消,执行已销毁 | .cancelled |
否 |
恢复验证时,使用相同的 customerId 再次启动验证界面,SDK
会自动检测进行中的工作流。
权限
| 权限 | Info.plist 键 | 必填 | 用途 |
|---|---|---|---|
| 相机 | NSCameraUsageDescription |
是 | 证件采集、活体检测 |
| 麦克风 | NSMicrophoneUsageDescription |
是 | 证件验证流程录制背景音频作为反欺诈措施 |
| 照片库 | NSPhotoLibraryUsageDescription |
否 | 仅当允许用户从照片库选取证件图片时(系统 PhotosPicker 并不严格要求该 key) |
| 语音识别 | NSSpeechRecognitionUsageDescription |
是 | 由 App Store 入库校验检查。运行时永不申请该权限,用户也就永远看不到这句文案 |
NSMicrophoneUsageDescription
会在启动时导致硬崩溃。NSSpeechRecognitionUsageDescription
同样必填,但它卡的是 App Store 入库校验、不是运行时(见「安装」一节)。没有任何
SDK 环境相关的 Info.plist key(无位置 key、无 token key)。网络:通过
URLSession 使用 HTTPS,无需配置
NSAppTransportSecurity 例外。
App Thinning:XCFramework 支持 App Thinning。不嵌入
Bitcode(已在 Xcode 14 中弃用)。如遇 release 构建问题,请检查应用
target 中
Strip Swift Symbols
(STRIP_SWIFT_SYMBOLS) 是否设置为默认值 (YES)。
故障排查
| 问题 | 解决方案 |
|---|---|
| No such module 'WidthIdentity' |
SPM:File > Packages > Resolve Package
Versions。确认 WidthIdentity 库已添加到
Target > General > Frameworks, Libraries, and Embedded
Content
中。CocoaPods:声明私有源
source 'https://github.com/width-com/Specs.git',执行
pod install,并打开 .xcworkspace(而非
.xcodeproj)。
|
启动时返回 .failed(如
W-INIT-410)
|
VerificationInput 初始化器在 domain /
sdkToken 为空,或 customerId /
email
为空时抛错;输入闭包抛出的错误会表现为
.failed。请确认这四项都非空。
|
| 摄像头无法使用 |
确保 Info.plist 中存在
NSCameraUsageDescription
且描述不为空。若用户此前已拒绝权限,iOS 不会重新弹窗 — SDK
会检测到此状态并显示引导界面,指引用户前往设置 > 你的应用 > 相机开启权限。
|
| 主题颜色未生效 | 检查十六进制值、令牌名称(区分大小写)及正确的主题字典 |
| SwiftUI sheet 未弹出 |
将 .widthVerification /
.widthMobileSDK
附加到视图层级中的视图上,而非条件性添加
|
| UIKit:"not in window hierarchy" |
在 viewDidAppear 之后从视图控制器发起呈现
|
| 活体检测页面语言不对 |
AWS 活体检测页面的文案跟随设备语言,不跟随 SDK 的
language 设置。流程的其余部分仍按
sdk.language 显示。
|
from: 值,或 Podfile 里的版本),再重新解析依赖即可。
集成说明
请按上文获取软件包访问权限并完成安装。SDK 构建版本及所用凭证须与目标 WIDTH 环境保持一致。