# Como integrar validação de CPF em apps iOS com Swift e URLSession

> Aprenda a integrar a validação de CPF via API em aplicativos iOS usando Swift e URLSession com exemplos completos e boas práticas.

**Publicado:** 23/09/2024
**Autor:** Redação CPFHub.io
**URL:** https://cpfhub.io/blog/como-integrar-validacao-de-cpf-em-apps-ios-com-swift-e-urlsession

---


Para integrar validação de CPF em apps iOS com Swift, crie um `CpfService` que usa `URLSession` com async/await, defina as structs `CpfResponse` e `CpfData` conformando ao protocolo `Codable` e faça um `GET` para `https://api.cpfhub.io/cpf/{CPF}` com o header `x-api-key`. O plano gratuito da CPFHub.io oferece 50 consultas por mês sem cartão de crédito, suficiente para testes e apps em fase inicial.

## Introdução

Aplicativos iOS que operam no mercado brasileiro frequentemente precisam validar o CPF de seus usuários durante processos de cadastro, onboarding ou transações financeiras. Fintechs, marketplaces, aplicativos de mobilidade e plataformas de saúde são exemplos de segmentos que dependem dessa validação para garantir a identidade do usuário e prevenir fraudes.

O Swift, linguagem nativa da Apple para desenvolvimento iOS, oferece o URLSession como a forma padrão de realizar requisições HTTP.

---

## Pré-requisitos

* **Xcode 15+** -- Ambiente de desenvolvimento da Apple.
* **Swift 5.9+** -- Versão recomendada para projetos modernos.
* **Conta na CPFHub.io** -- Para obter a chave de API. O plano gratuito oferece 50 consultas/mês.
* **App Transport Security** -- HTTPS é obrigatório no iOS, e a API da CPFHub.io já opera com HTTPS.

---

## Modelando a resposta da API

Crie as structs que representam o JSON retornado pela API, conformando ao protocolo `Codable`:

```swift
struct CpfResponse: Codable {
 let success: Bool
 let data: CpfData?
}

struct CpfData: Codable {
 let cpf: String
 let name: String
 let nameUpper: String
 let gender: String
 let birthDate: String
 let day: Int
 let month: Int
 let year: Int
}
```

A resposta esperada da API:

```json
{
 "success": true,
 "data": {
 "cpf": "12345678900",
 "name": "João da Silva",
 "nameUpper": "JOÃO DA SILVA",
 "gender": "M",
 "birthDate": "15/06/1990",
 "day": 15,
 "month": 6,
 "year": 1990
 }
}
```

---

## Criando o serviço de consulta com URLSession

Abaixo está a implementação de um serviço que encapsula a chamada à API usando URLSession com async/await:

```swift
import Foundation

enum CpfServiceError: Error, LocalizedError {
 case cpfInvalido
 case chaveNaoConfigurada
 case erroHTTP(statusCode: Int)
 case timeout
 case respostInvalida
 case cpfNaoEncontrado

 var errorDescription: String? {
 switch self {
 case .cpfInvalido:
 return "CPF inválido. Informe 11 dígitos numéricos."
 case .chaveNaoConfigurada:
 return "Chave de API não configurada."
 case .erroHTTP(let code):
 return "Erro HTTP: \(code)"
 case .timeout:
 return "A requisição excedeu o tempo limite."
 case .respostInvalida:
 return "Resposta inválida da API."
 case .cpfNaoEncontrado:
 return "CPF não encontrado na base de dados."
 }
 }
}

class CpfService {

 private let apiKey: String
 private let baseURL = "https://api.cpfhub.io/cpf/"
 private let session: URLSession

 init(apiKey: String) {
 self.apiKey = apiKey

 let config = URLSessionConfiguration.default
 config.timeoutIntervalForRequest = 10
 config.timeoutIntervalForResource = 15
 self.session = URLSession(configuration: config)
 }

 func consultarCpf(_ cpf: String) async throws -> CpfData {
 let cpfLimpo = cpf.replacingOccurrences(
 of: "[^0-9]", with: "", options: .regularExpression
 )

 guard cpfLimpo.count == 11 else {
 throw CpfServiceError.cpfInvalido
 }

 guard !apiKey.isEmpty else {
 throw CpfServiceError.chaveNaoConfigurada
 }

 guard let url = URL(string: "\(baseURL)\(cpfLimpo)") else {
 throw CpfServiceError.cpfInvalido
 }

 var request = URLRequest(url: url)
 request.httpMethod = "GET"
 request.setValue(apiKey, forHTTPHeaderField: "x-api-key")
 request.setValue("application/json", forHTTPHeaderField: "Accept")

 do {
 let (data, response) = try await session.data(for: request)

 guard let httpResponse = response as? HTTPURLResponse else {
 throw CpfServiceError.respostInvalida
 }

 switch httpResponse.statusCode {
 case 200:
 let cpfResponse = try JSONDecoder().decode(
 CpfResponse.self, from: data
 )
 guard cpfResponse.success, let cpfData = cpfResponse.data else {
 throw CpfServiceError.cpfNaoEncontrado
 }
 return cpfData
 default:
 throw CpfServiceError.erroHTTP(
 statusCode: httpResponse.statusCode
 )
 }
 } catch let error as URLError where error.code == .timedOut {
 throw CpfServiceError.timeout
 }
 }
}
```

---

## Integrando com SwiftUI

Para aplicativos que utilizam SwiftUI, crie um ViewModel com `@Published` para gerenciar o estado:

```swift
import SwiftUI

@MainActor
class CpfViewModel: ObservableObject {

 @Published var cpfInput: String = ""
 @Published var resultado: CpfData?
 @Published var erro: String?
 @Published var carregando: Bool = false

 private let service: CpfService

 init() {
 self.service = CpfService(apiKey: "SUA_CHAVE_DE_API")
 }

 func consultar() {
 erro = nil
 resultado = nil
 carregando = true

 Task {
 do {
 let dados = try await service.consultarCpf(cpfInput)
 self.resultado = dados
 } catch {
 self.erro = error.localizedDescription
 }
 self.carregando = false
 }
 }
}
```

### Tela de consulta em SwiftUI

```swift
struct ConsultaCpfView: View {

 @StateObject private var viewModel = CpfViewModel()

 var body: some View {
 NavigationView {
 VStack(spacing: 20) {
 TextField("CPF (somente números)", text: $viewModel.cpfInput)
 .keyboardType(.numberPad)
 .textFieldStyle(.roundedBorder)
 .padding(.horizontal)

 Button(action: { viewModel.consultar() }) {
 Text(viewModel.carregando ? "Consultando..." : "Consultar")
 .frame(maxWidth: .infinity)
 }
 .buttonStyle(.borderedProminent)
 .disabled(viewModel.carregando)
 .padding(.horizontal)

 if let dados = viewModel.resultado {
 VStack(alignment: .leading, spacing: 8) {
 Text("Nome: \(dados.name)")
 Text("CPF: \(dados.cpf)")
 Text("Nascimento: \(dados.birthDate)")
 Text("Gênero: \(dados.gender)")
 }
 .padding()
 }

 if let erro = viewModel.erro {
 Text(erro)
 .foregroundColor(.red)
 .padding()
 }

 Spacer()
 }
 .navigationTitle("Validação de CPF")
 }
 }
}
```

---

## Testando com cURL

Para validar rapidamente o comportamento da API antes de integrar no app:

```bash
curl -X GET https://api.cpfhub.io/cpf/12345678900 \
 -H "x-api-key: SUA_CHAVE_DE_API" \
 -H "Accept: application/json" \
 --max-time 10
```

---

## Boas práticas para iOS

* **Chave de API** -- Em produção, nunca armazene a chave diretamente no código. Utilize um back-end intermediário que faz o proxy da requisição, protegendo a chave no servidor.

* **Timeout** -- Configure `timeoutIntervalForRequest` no URLSessionConfiguration para evitar travamentos.

* **Thread principal** -- Nunca faça chamadas de rede na main thread. Utilize async/await ou completionHandlers para manter a interface responsiva.

* **Validação local** -- Valide os dígitos verificadores do CPF localmente antes de consultar a API, economizando consultas.

* **Monitoramento de consumo** -- Controle o número de consultas realizadas no mês para evitar cobranças de excedente inesperadas (R$ 0,15/consulta adicional).

---

## Perguntas frequentes

### Como proteger a chave de API em um app iOS?
A chave de API nunca deve ficar embutida no código do app, pois pode ser extraída por engenharia reversa. A abordagem correta é criar um back-end intermediário (proxy) que faz a chamada à CPFHub.io no servidor e expõe apenas um endpoint autenticado para o app. Assim, a `x-api-key` fica protegida no ambiente de servidor, fora do alcance de usuários maliciosos.

### A API CPFHub.io retorna 429 quando o limite de consultas é atingido?
Não. A CPFHub.io não bloqueia requisições nem retorna 429. Quando o limite do plano é ultrapassado, as consultas continuam funcionando normalmente e o excedente é cobrado a R$ 0,15 por consulta. Para controlar o consumo no app, mantenha um contador local ou use o painel em [app.cpfhub.io](https://app.cpfhub.io/settings/billing).

### Como lidar com timeout na chamada à API via URLSession?
Configure `timeoutIntervalForRequest` (tempo para receber o primeiro byte) e `timeoutIntervalForResource` (tempo total da operação) no `URLSessionConfiguration`. Para a CPFHub.io, com latência média de ~900ms, valores de 10s e 15s respectivamente são adequados. Trate o erro `URLError.timedOut` e exiba uma mensagem clara ao usuário.

### Como garantir conformidade com a LGPD em apps iOS que validam CPF?
Informe ao usuário no momento do cadastro que o CPF será consultado para verificação de identidade, documente a base legal para o tratamento e não armazene o número do CPF no dispositivo além do necessário para a operação, conforme orienta a [ANPD](https://www.gov.br/anpd). Utilize o Secure Enclave ou Keychain para armazenar a chave de API com segurança no dispositivo.

### Leia também

- [Como validar CPF no frontend com React e API REST](https://cpfhub.io/blog/como-validar-cpf-no-frontend-com-react-e-api-rest)
- [SLA de API de CPF: níveis de disponibilidade e o que exigir do seu provedor](https://cpfhub.io/blog/sla-api-cpf-niveis-disponibilidade)
- [API de CPF grátis para desenvolvedores: como começar em 5 minutos](https://cpfhub.io/blog/api-cpf-gratis-desenvolvedores-comecar-5-minutos)
- [10 erros mais comuns ao integrar uma API de CPF e como evitá-los](https://cpfhub.io/blog/10-erros-mais-comuns-ao-integrar-uma-api-de-cpf)

---

## Conclusão

Integrar a validação de CPF em aplicativos iOS usando Swift e URLSession é um processo direto que se beneficia dos recursos modernos da linguagem, como async/await e Codable. Com a API da CPFHub.io, o app recebe nome, data de nascimento e gênero do titular em uma única chamada, tornando o onboarding mais rápido e seguro.

Cadastre-se em [cpfhub.io](https://www.cpfhub.io/) — 50 consultas mensais gratuitas, sem cartão de crédito — e adicione validação de CPF ao seu app iOS com Swift em menos de uma hora.

