Crovly

iOS SDK

Add Crovly captcha to native iOS apps with Swift and SwiftUI. WKWebView integration with async/await token retrieval.

Installation

Swift Package Manager

Add the package in Xcode: File > Add Package Dependencies, then enter:

https://github.com/crovly/ios

Or add to your Package.swift:

dependencies: [
    .package(url: "https://github.com/crovly/ios", from: "1.0.0")
]

CocoaPods

pod 'CrovlyCaptcha', '~> 1.0'

Get your site key at app.crovly.com.

SwiftUI

import SwiftUI
import CrovlyCaptcha

struct LoginView: View {
    @State private var token: String?

    var body: some View {
        VStack(spacing: 16) {
            TextField("Email", text: .constant(""))
            SecureField("Password", text: .constant(""))

            CrovlyCaptchaRepresentable(
                configuration: CrovlyConfiguration(siteKey: "YOUR_SITE_KEY"),
                onVerify: { token in self.token = token },
                onError: { error in print("Error:", error) },
                onExpire: { self.token = nil }
            )
            .frame(height: 80)

            Button("Sign In") {
                handleLogin()
            }
            .disabled(token == nil)
        }
        .padding()
    }
}

UIKit

import UIKit
import CrovlyCaptcha

class LoginViewController: UIViewController, CrovlyDelegate {
    private var token: String?

    override func viewDidLoad() {
        super.viewDidLoad()

        let config = CrovlyConfiguration(
            siteKey: "YOUR_SITE_KEY",
            theme: .auto,
            size: .normal
        )

        let captchaView = CrovlyCaptchaView(configuration: config)
        captchaView.delegate = self
        captchaView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(captchaView)

        NSLayoutConstraint.activate([
            captchaView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
            captchaView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
            captchaView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
            captchaView.heightAnchor.constraint(equalToConstant: 80),
        ])
    }

    // MARK: - CrovlyDelegate

    func crovlyDidVerify(token: String) {
        self.token = token
        submitButton.isEnabled = true
    }

    func crovlyDidError(error: String) {
        print("Captcha error:", error)
    }

    func crovlyDidExpire() {
        self.token = nil
        submitButton.isEnabled = false
    }
}

Configuration

let config = CrovlyConfiguration(
    siteKey: "YOUR_SITE_KEY",
    theme: .dark,       // .light, .dark, or .auto
    size: .normal,      // .normal or .invisible
    lang: "tr",         // nil for auto-detect
    apiUrl: "https://get.crovly.com"
)
ParameterTypeDefaultDescription
siteKeyStringYour public site key (required)
themeCrovlyTheme.autoWidget theme
sizeCrovlySize.normalWidget size mode
langString?Auto-detectLanguage code
apiUrlStringhttps://get.crovly.comWidget script CDN

Use CrovlyCaptchaViewController to present the captcha as a sheet:

let config = CrovlyConfiguration(siteKey: "YOUR_SITE_KEY")
let captchaVC = CrovlyCaptchaViewController(configuration: config)
captchaVC.delegate = self
captchaVC.dismissOnVerify = true  // Auto-dismiss after verification

present(captchaVC, animated: true)

On iOS 15+, it automatically uses .medium() sheet detent for a compact presentation.

Delegate Protocol

protocol CrovlyDelegate: AnyObject {
    func crovlyDidVerify(token: String)        // Required
    func crovlyDidError(error: String)         // Optional
    func crovlyDidExpire()                     // Optional
}

Methods

MethodDescription
reset()Reset the widget for a new verification

Backend Verification

After getting the token, verify it on your server using any of the server-side SDKs:

// On your backend (e.g. Vapor)
let body = ["token": token, "expectedIp": clientIP]
let response = try await client.post("https://api.crovly.com/verify-token") { req in
    req.headers.bearerAuthorization = .init(token: secretKey)
    try req.content.encode(body)
}

Requirements

  • iOS 14.0+
  • Swift 5.7+
  • Xcode 14+

On this page