iOS / macOS SDK
Apple-platform SDK for iOS 15+ and macOS 11+ (Big Sur) via Mac Catalyst, shipped as an .xcframework plus a .bundle of localized assets. Drop-in for SwiftUI, UIKit, and AppKit projects.
Demo on GitHub
📦
Captcha-La/iosmacos-demo — full runnable example with every integration step.
Install
Add CaptchaLa to your Podfile (CocoaPods 1.10+):
# Podfile
platform :ios, '13.0'
target 'YourApp' do
use_frameworks!
pod 'Captchala', '~> 1.0.2'
endpod installOr, if you prefer manual integration:
Download the latest iOS release from the CaptchaLa dashboard. The archive contains:
Captchala.xcframework— the compiled SDKCaptchala.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.
For manual integration, download the xcframework directly: dash.captcha.la/downloads
YourApp/
├── YourApp.xcodeproj
├── YourApp/
├── Captchala.xcframework
└── Captchala.bundleOpen the project and run on the simulator, a device, or My Mac (Mac Catalyst):
open YourApp.xcodeproj
# Cmd-R in XcodeQuick start
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)
}
}
}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 surface
| Symbol | Purpose |
|---|---|
CaptchalaClient.shared | Shared singleton. All entry points hang off this. |
CaptchalaConfigBuilder() | Fluent builder. Set appKey, action, lang, theme, enableVoice, enableOfflineMode, serverToken. |
initialize(config:) | Apply the built config. Returns self so you can chain setDelegate. |
setDelegate(_:) | Provide an NSObject conforming to CaptchalaDelegate. The SDK keeps a weak reference. |
verify(from: presenter) | Present the CAPTCHA over the given UIViewController (iOS / Catalyst). The SDK pushes a modal sheet. |
CaptchalaResult | Returned via captcha(didSucceedWith:). Fields: passToken, challengeId, ttl, isOffline, isClientOnly. |
onServerTokenExpired { … } | Async closure that re-fetches a fresh server_token if the prior one expires mid-challenge. |
Server-side validation
Forward result.passToken (or result.token) to your backend and validate it against the CaptchaLa API. Never expose X-App-Secret in client code.
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": "<result.passToken>", "client_ip": "<end-user IP>" }See the API Reference for the full validation endpoint and X-App-Key / X-App-Secret flow.
Troubleshooting
Captchala.xcframeworknot found
The.xcframeworkand.bundlemust sit next toExample.xcodeproj. The demo references both by file name; keep their location stable when updating the SDK.Mac Catalyst destination missing
In Xcode, enable Mac (Mac Catalyst) under your target's Supported Destinations. The demo target ships withSUPPORTS_MACCATALYST = YES.Modal not appearing
Pass a realUIViewControllertoverify(from:). The demo walksUIApplication.connectedScenesto find the topmost active key-window controller — copy that helper if you only have a SwiftUIView.Info.plistprivacy strings on macOS
For Catalyst / native macOS targets enable the Outgoing Connections (Client) sandbox capability. The SDK only makes HTTPS calls, no microphone or camera access.
Requirements
- iOS 15+ (device or simulator)
- macOS 11+ (Big Sur) via Mac Catalyst, macOS 13+ for native targets
- Xcode 15+
- Swift 5.7+ (async/await)