> ## Documentation Index
> Fetch the complete documentation index at: https://documentacao.legitimuz.com/llms.txt
> Use this file to discover all available pages before exploring further.

# iOS

> Abra o fluxo de verificação num app iOS e receba os eventos da sessão em Swift.

Ao final deste guia o fluxo abre dentro do seu app iOS, com a câmera liberada e os eventos chegando
ao seu código Swift.

<Info>
  O SDK iOS está em **preview** (`v1.0.0-alpha.1`). A superfície pode mudar entre versões de
  preview. Fale com o [suporte](https://painel.legitimuz.com/support) antes de usá-lo em produção.
</Info>

## Pré-requisitos

* iOS 13 ou superior.
* Uma `access_credential` emitida pelo seu backend, como em
  [criar verificação](/api/create-verification).
* O bundle identifier do app registrado e verificado em
  [Integrações → Segurança](/platform/domains#app-bundles).

<Warning>
  A chave de API nunca vai para o app. Ele recebe só a `access_credential`, que vale para uma
  verificação e expira.
</Warning>

## Configure o projeto

<Steps>
  <Step title="Adicione o pacote">
    Em **File → Add Package Dependencies**, informe a URL do repositório e fixe a versão exata:

    ```
    https://github.com/Legitimuz-Tech/legitimuz-sdk-ios-dist
    ```

    O produto a adicionar é **LegitimuzSDK**. Ele traz o FaceTecSDK junto, não adicione o FaceTec
    separadamente.
  </Step>

  <Step title="Declare o uso da câmera">
    Sem `NSCameraUsageDescription`, o app é encerrado pelo sistema ao pedir a câmera.

    ```xml title="Info.plist" theme={null}
    <key>NSCameraUsageDescription</key>
    <string>Precisamos da câmera para fotografar seu documento e confirmar que é você.</string>
    ```

    Escreva o texto para o titular, não para a App Review. É ele que aparece no alerta.
  </Step>

  <Step title="Peça a permissão antes de abrir">
    ```swift title="VerificacaoViewController.swift" theme={null}
    import AVFoundation

    private func pedirCameraEIniciar() {
        switch AVCaptureDevice.authorizationStatus(for: .video) {
        case .authorized:
            iniciarVerificacao()
        case .notDetermined:
            AVCaptureDevice.requestAccess(for: .video) { [weak self] concedida in
                DispatchQueue.main.async {
                    concedida ? self?.iniciarVerificacao() : self?.mostrarExplicacao()
                }
            }
        default:
            mostrarExplicacao()
        }
    }
    ```
  </Step>

  <Step title="Busque a credencial no seu backend">
    ```swift title="VerificacaoService.swift" theme={null}
    func obterCredencial(refId: String) async throws -> String {
        var req = URLRequest(url: URL(string: "https://seu-backend.exemplo.com.br/verificacoes")!)
        req.httpMethod = "POST"
        req.setValue("application/json", forHTTPHeaderField: "Content-Type")
        req.httpBody = try JSONEncoder().encode(["ref_id": refId])

        let (dados, _) = try await URLSession.shared.data(for: req)
        return try JSONDecoder().decode(RespostaVerificacao.self, from: dados).accessCredential
    }
    ```
  </Step>

  <Step title="Abra a sessão">
    ```swift title="VerificacaoViewController.swift" theme={null}
    private func iniciarVerificacao() {
        Task {
            let credencial = try await service.obterCredencial(refId: "pedido-4471")

            let verificacao = LegitimuzVerification(accessCredential: credencial)
            verificacao.onEvent = { nome, _ in
                print("Legitimuz: \(nome)")
            }
            verificacao.onComplete = { [weak self] _ in
                // O desfecho confiável chega pelo webhook, no seu backend.
                self?.dismiss(animated: true)
            }
            verificacao.onError = { codigo, mensagem in
                print("Legitimuz erro \(codigo): \(mensagem)")
            }

            present(verificacao.viewController, animated: true)
        }
    }
    ```
  </Step>
</Steps>

## Confira que funcionou

O fluxo abre na primeira etapa e o console registra `session.started`. No dashboard, a verificação
sai de `not_opened` e passa a `started`.

## Eventos

Os mesmos da web. O catálogo completo está em [eventos](/guides/web/best-practices/events).

| Evento                       | Quando dispara                     |
| ---------------------------- | ---------------------------------- |
| `session.started`            | o fluxo abriu a primeira etapa     |
| `session.stepTransitioned`   | o servidor trocou a etapa          |
| `document.capture.completed` | a captura do documento foi enviada |
| `liveness.completed`         | a prova de vida foi concluída      |
| `session.completed`          | o fluxo terminou                   |
| `session.error`              | um erro ocorreu na sessão          |

<Warning>
  **Não decida nada a partir de `session.completed`.** O desfecho confiável chega ao seu backend por
  [`verification.decided`](/webhooks/events#verificationdecided).
</Warning>

## Casos de borda

<AccordionGroup>
  <Accordion title="O titular negou a câmera">
    No iOS a negativa é definitiva dentro do app: só as Ajustes resolvem. Explique por que a câmera
    é necessária e leve o titular para lá.

    ```swift theme={null}
    if let url = URL(string: UIApplication.openSettingsURLString) {
        UIApplication.shared.open(url)
    }
    ```
  </Accordion>

  <Accordion title="O app foi para segundo plano">
    A sessão é retomável: ao voltar, ela reabre na etapa onde o titular parou, desde que a
    credencial não tenha expirado.
  </Accordion>

  <Accordion title="A credencial expirou">
    `onError` chega com um código de credencial inválida. Peça uma credencial nova ao seu backend e
    comece outra sessão, não reaproveite a antiga.
  </Accordion>

  <Accordion title="A sessão recusa abrir">
    Quase sempre é origem. Confirme que o bundle identifier está registrado e **verificado**, e que
    a verificação foi criada com o `integration_origin_public_id` dessa origem.
  </Accordion>
</AccordionGroup>

## Próximo passo

<Columns cols={2}>
  <Card title="Android" icon="brand-android" href="/sdks/android">
    O mesmo fluxo em Kotlin.
  </Card>

  <Card title="Receber a decisão" icon="webhook" href="/webhooks/introduction">
    Onde o desfecho realmente chega.
  </Card>
</Columns>
