--- title: iOS / macOS SDK --- # iOS / macOS SDK SDK nền tảng Apple cho **iOS 15+** và **macOS 11+ (Big Sur)** qua Mac Catalyst, phân phối dưới dạng `.xcframework` cùng `.bundle` tài nguyên đa ngôn ngữ. Tích hợp được vào dự án SwiftUI, UIKit và AppKit. ## Demo trên GitHub ::: tip 📦 [Captcha-La/iosmacos-demo](https://github.com/Captcha-La/iosmacos-demo) — ví dụ đầy đủ, có thể chạy được, kèm mọi bước tích hợp. ::: ## Cài đặt Thêm CaptchaLa vào `Podfile` của bạn (CocoaPods 1.10+): ```ruby # Podfile platform :ios, '13.0' target 'YourApp' do use_frameworks! pod 'Captchala', '~> 1.0.2' end ``` ```bash pod install ``` Hoặc, nếu bạn thích tích hợp thủ công: Download the latest iOS release from the [CaptchaLa dashboard](https://dash.captcha.la). The archive contains: - `Captchala.xcframework` — the compiled SDK - `Captchala.bundle` — localized resources Drop both next to your `.xcodeproj`. The demo project references them by file name; no manual linking is needed beyond keeping their location stable. Để tích hợp thủ công, tải xcframework trực tiếp: [dash.captcha.la/downloads](https://dash.captcha.la/downloads) ```text YourApp/ ├── YourApp.xcodeproj ├── YourApp/ ├── Captchala.xcframework └── Captchala.bundle ``` Open the project and run on the simulator, a device, or **My Mac (Mac Catalyst)**: ```bash open YourApp.xcodeproj # Cmd-R in Xcode ``` ## Bắt đầu nhanh ```swift import SwiftUI import Captchala final class CaptchaDelegateBridge: NSObject, CaptchalaDelegate { var onSuccess: ((CaptchalaResult) -> Void)? var onFailure: ((CaptchalaError) -> Void)? var onClose: (() -> Void)? func captcha(didSucceedWith result: CaptchalaResult) { onSuccess?(result) } func captcha(didFailWithError error: CaptchalaError) { onFailure?(error) } func captchaDidClose() { onClose?() } } struct LoginView: View { @State private var bridge = CaptchaDelegateBridge() @State private var status = "Tap to verify" var body: some View { Button("Verify with CAPTCHA", action: startVerify) Text(status).font(.caption) } private func startVerify() { bridge.onSuccess = { r in // Send r.passToken to your backend for validation. status = "OK: \(r.passToken)" } bridge.onFailure = { e in status = "ERROR [\(e.code)] \(e.message)" } Task { @MainActor in // 1. Fetch a one-shot server_token from YOUR backend. let token = await fetchServerTokenFromYourBackend() // 2. Build config and present. let config = CaptchalaConfigBuilder() .appKey("YOUR_APP_KEY") .action("login") .lang("en") // en, zh-CN, zh-TW, ja, ko, ms, vi, id .theme("light") // "light" | "dark" .enableVoice(true) .enableOfflineMode(true) .serverToken(token) .onServerTokenExpired { await fetchServerTokenFromYourBackend() } .build() guard let presenter = topViewController() else { return } CaptchalaClient.shared .initialize(config: config) .setDelegate(bridge) .verify(from: presenter) } } } ``` ::: tip Mac Catalyst & native macOS The exact same Swift code runs on iOS, Mac Catalyst, and native macOS. On Catalyst pass any `UIViewController`. On native macOS use `NSViewController` and call `.verify()` without an argument — the SDK presents in its own `NSWindow`. ::: ## API chính | Ký hiệu | Mục đích | | --- | --- | | `CaptchalaClient.shared` | Singleton dùng chung. Mọi điểm vào đều thông qua nó. | | `CaptchalaConfigBuilder()` | Builder dạng fluent cho `appKey`, `action`, `lang`, `theme`, `enableVoice`, `enableOfflineMode`, `serverToken`. | | `initialize(config:)` | Áp dụng cấu hình đã build. Trả về `self` để chain với `setDelegate`. | | `setDelegate(_:)` | Truyền vào `NSObject` tuân thủ `CaptchalaDelegate`. SDK giữ tham chiếu yếu. | | `verify(from: presenter)` | Hiển thị CAPTCHA trên UIViewController được chỉ định (iOS / Catalyst). SDK trình bày dưới dạng modal sheet. | | `CaptchalaResult` | Trả về qua `captcha(didSucceedWith:)`. Trường: `passToken`, `challengeId`, `ttl`, `isOffline`, `isClientOnly`. | | `onServerTokenExpired { … }` | Closure bất đồng bộ, lấy lại `server_token` mới khi token trước hết hạn giữa chừng. | ## Xác thực phía máy chủ Chuyển `result.passToken` (hoặc `result.token`) tới backend của bạn rồi xác thực qua API CaptchaLa. **Tuyệt đối không để lộ `X-App-Secret` trong mã client**. ```bash POST https://apiv1.captcha.la/v1/validate X-App-Key: YOUR_APP_KEY X-App-Secret: YOUR_APP_SECRET Content-Type: application/json { "pass_token": "" } ``` Xem [Tài liệu API](../api-reference) để biết chi tiết endpoint xác thực và quy trình `X-App-Key` / `X-App-Secret`. ## Khắc phục sự cố - **Không tìm thấy `Captchala.xcframework`** `.xcframework` và `.bundle` phải nằm cạnh `Example.xcodeproj`. Demo tham chiếu chúng theo tên file; giữ nguyên vị trí khi cập nhật SDK. - **Không thấy destination Mac Catalyst** Trong Xcode, bật *Mac (Mac Catalyst)* trong *Supported Destinations* của target. Target demo có `SUPPORTS_MACCATALYST = YES`. - **Modal không hiển thị** Hãy truyền một `UIViewController` thật vào `verify(from:)`. Demo duyệt `UIApplication.connectedScenes` để tìm key-window controller trên cùng — sao chép helper đó nếu bạn chỉ có SwiftUI `View`. - **Cần chuỗi quyền riêng tư trong `Info.plist` trên macOS** Với target Catalyst / macOS gốc, bật capability sandbox **Outgoing Connections (Client)**. SDK chỉ gọi HTTPS, không truy cập microphone hay camera. ## Yêu cầu - iOS 15+ (device or simulator) - macOS 11+ (Big Sur) via Mac Catalyst, macOS 13+ for native targets - Xcode 15+ - Swift 5.7+ (async/await)