--- title: SDK iOS / macOS --- # SDK iOS / macOS SDK para plataformas Apple, **iOS 15+** y **macOS 11+ (Big Sur)** mediante Mac Catalyst, distribuido como un `.xcframework` más un `.bundle` de recursos localizados. Drop-in para proyectos SwiftUI, UIKit y AppKit. ## Demo en GitHub ::: tip 📦 [Captcha-La/iosmacos-demo](https://github.com/Captcha-La/iosmacos-demo) — ejemplo completo y ejecutable con cada paso de integración. ::: ## Instalación Añade CaptchaLa a tu `Podfile` (CocoaPods 1.10+): ```ruby # Podfile platform :ios, '13.0' target 'YourApp' do use_frameworks! pod 'Captchala', '~> 1.0.2' end ``` ```bash pod install ``` O bien, si prefieres la integración manual: Descarga la última versión de iOS desde el [panel de CaptchaLa](https://dash.captcha.la). El archivo contiene: - `Captchala.xcframework` — el SDK compilado - `Captchala.bundle` — recursos localizados Coloca ambos junto a tu `.xcodeproj`. El proyecto de la demo los referencia por nombre de fichero; no hace falta enlazado manual adicional siempre que su ubicación permanezca estable. Para la integración manual, descarga el xcframework directamente: [dash.captcha.la/downloads](https://dash.captcha.la/downloads) ```text YourApp/ ├── YourApp.xcodeproj ├── YourApp/ ├── Captchala.xcframework └── Captchala.bundle ``` Abre el proyecto y ejecútalo en el simulador, en un dispositivo o en **My Mac (Mac Catalyst)**: ```bash open YourApp.xcodeproj # Cmd-R in Xcode ``` ## Inicio rápido ```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 y macOS nativo El mismo código Swift se ejecuta en iOS, Mac Catalyst y macOS nativo. En Catalyst pasa cualquier `UIViewController`. En macOS nativo usa `NSViewController` y llama a `.verify()` sin argumentos — el SDK se presenta en su propio `NSWindow`. ::: ## Superficie de la API | Símbolo | Propósito | | --- | --- | | `CaptchalaClient.shared` | Singleton compartido. Todos los puntos de entrada dependen de él. | | `CaptchalaConfigBuilder()` | Builder fluido. Configura `appKey`, `action`, `lang`, `theme`, `enableVoice`, `enableOfflineMode`, `serverToken`. | | `initialize(config:)` | Aplica la configuración construida. Devuelve `self` para encadenar `setDelegate`. | | `setDelegate(_:)` | Proporciona un `NSObject` que cumpla `CaptchalaDelegate`. El SDK mantiene una referencia débil. | | `verify(from: presenter)` | Presenta el CAPTCHA sobre el UIViewController indicado (iOS / Catalyst). El SDK abre una hoja modal. | | `CaptchalaResult` | Devuelto vía `captcha(didSucceedWith:)`. Campos: `passToken`, `challengeId`, `ttl`, `isOffline`, `isClientOnly`. | | `onServerTokenExpired { … }` | Closure asíncrono que vuelve a obtener un `server_token` nuevo si el anterior caduca a mitad del desafío. | ## Validación en el servidor Reenvía `result.passToken` (o `result.token`) a tu backend y valídalo contra la API de CaptchaLa. Nunca expongas `X-App-Secret` en el código del cliente. ```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": "" } ``` Consulta la [Referencia de la API](/es/api-reference) para el endpoint de validación completo y el flujo de `X-App-Key` / `X-App-Secret`. ## Solución de problemas - **`Captchala.xcframework` no encontrado** El `.xcframework` y el `.bundle` deben estar junto a `Example.xcodeproj`. La demo los referencia por nombre de fichero; mantén su ubicación estable al actualizar el SDK. - **Destino Mac Catalyst ausente** En Xcode, activa *Mac (Mac Catalyst)* en *Supported Destinations* de tu target. La demo se entrega con `SUPPORTS_MACCATALYST = YES`. - **El modal no aparece** Pasa un `UIViewController` real a `verify(from:)`. La demo recorre `UIApplication.connectedScenes` para encontrar el controlador de la key-window activa más superior — copia ese helper si solo tienes una `View` de SwiftUI. - **Cadenas de privacidad de `Info.plist` en macOS** Para los targets Catalyst / macOS nativo activa la capacidad sandbox **Outgoing Connections (Client)**. El SDK solo realiza llamadas HTTPS, sin acceso a micrófono ni cámara. ## Requisitos - iOS 15+ (dispositivo o simulador) - macOS 11+ (Big Sur) vía Mac Catalyst, macOS 13+ para targets nativos - Xcode 15+ - Swift 5.7+ (async/await)