Skip to content

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+):

ruby
# Podfile
platform :ios, '13.0'

target 'YourApp' do
  use_frameworks!
  pod 'Captchala', '~> 1.0.2'
end
bash
pod install

Or, if you prefer manual integration:

Download the latest iOS release from the CaptchaLa dashboard. 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.

For manual integration, download the xcframework directly: 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

Quick start

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)
        }
    }
}

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

SymbolPurpose
CaptchalaClient.sharedShared 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.
CaptchalaResultReturned 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.

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": "<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.xcframework not found
    The .xcframework and .bundle must sit next to Example.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 with SUPPORTS_MACCATALYST = YES.

  • Modal not appearing
    Pass a real UIViewController to verify(from:). The demo walks UIApplication.connectedScenes to find the topmost active key-window controller — copy that helper if you only have a SwiftUI View.

  • Info.plist privacy 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)

MIT-licensed examples · CaptchaLa is operated independently