Pular para o conteúdo
🍎 iOS / SWIFT

SuperDB com iOS nativo.

SDK Swift ainda não disponível — use URLSession + Keychain diretamente com a REST API hoje.

🚧

SDK Swift ainda não disponível. Não existe um pacote Swift Package Manager oficial para o SuperDB ainda.

Enquanto isso, a integração via URLSession funciona perfeitamente — o SuperDB usa APIs REST padrão HTTP/JSON. Quer ajudar a criar o SDK? GitHub Discussions →

Abordagem: URLSession + Keychain

No iOS nativo, o fluxo é:

  1. Fazer login via URLSession chamando a API REST do SuperDB
  2. Armazenar o JWT no Keychain do iOS — o equivalente seguro ao SecureStore do Expo
  3. Incluir o JWT no header Authorization: Bearer em todas as requisições subsequentes

Se preferir usar Alamofire, a lógica é idêntica — apenas a camada de HTTP muda.

Serviço de autenticação

SuperDBAuth.swift
import Foundation
import Security

struct AuthResponse: Codable {
    let accessToken: String
    let refreshToken: String
    let user: UserInfo?

    enum CodingKeys: String, CodingKey {
        case accessToken = "access_token"
        case refreshToken = "refresh_token"
        case user
    }
}

struct UserInfo: Codable {
    let id: String
    let email: String?
}

class SuperDBAuth {
    static let shared = SuperDBAuth()

    private let baseURL = "https://auth.superdb.com.br"
    private let anonKey = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
    private let tokenKey = "superdb_access_token"

    private var defaultHeaders: [String: String] {
        ["Content-Type": "application/json", "apikey": anonKey]
    }

    // MARK: - Keychain helpers
    func saveToken(_ token: String) {
        let data = Data(token.utf8)
        let query: [String: Any] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrAccount as String: tokenKey,
            kSecValueData as String: data
        ]
        SecItemDelete(query as CFDictionary)
        SecItemAdd(query as CFDictionary, nil)
    }

    func getToken() -> String? {
        let query: [String: Any] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrAccount as String: tokenKey,
            kSecReturnData as String: true,
            kSecMatchLimit as String: kSecMatchLimitOne
        ]
        var result: AnyObject?
        SecItemCopyMatching(query as CFDictionary, &result)
        guard let data = result as? Data else { return nil }
        return String(data: data, encoding: .utf8)
    }

    func deleteToken() {
        let query: [String: Any] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrAccount as String: tokenKey
        ]
        SecItemDelete(query as CFDictionary)
    }

    // MARK: - Auth
    func signIn(email: String, password: String) async throws -> AuthResponse {
        var request = URLRequest(url: URL(string: "\(baseURL)/auth/v1/token?grant_type=password")!)
        request.httpMethod = "POST"
        defaultHeaders.forEach { request.setValue($1, forHTTPHeaderField: $0) }
        request.httpBody = try JSONEncoder().encode(["email": email, "password": password])

        let (data, response) = try await URLSession.shared.data(for: request)
        guard let httpResponse = response as? HTTPURLResponse,
              httpResponse.statusCode == 200 else {
            throw URLError(.badServerResponse)
        }

        let authResponse = try JSONDecoder().decode(AuthResponse.self, from: data)
        saveToken(authResponse.accessToken)
        return authResponse
    }

    func signUp(email: String, password: String) async throws -> AuthResponse {
        var request = URLRequest(url: URL(string: "\(baseURL)/auth/v1/signup")!)
        request.httpMethod = "POST"
        defaultHeaders.forEach { request.setValue($1, forHTTPHeaderField: $0) }
        request.httpBody = try JSONEncoder().encode(["email": email, "password": password])

        let (data, response) = try await URLSession.shared.data(for: request)
        guard let httpResponse = response as? HTTPURLResponse,
              (200...201).contains(httpResponse.statusCode) else {
            throw URLError(.badServerResponse)
        }
        return try JSONDecoder().decode(AuthResponse.self, from: data)
    }

    func signOut() async {
        deleteToken()
    }
}

Queries no banco de dados

SuperDBDatabase.swift
class SuperDBDatabase {
    // Dados: api.superdb.com.br (NÃO auth.superdb.com.br)
    // Path: raiz da tabela, sem prefixo /rest/v1/
    private let dataURL = "https://api.superdb.com.br"
    private let anonKey = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

    func fetch<T: Decodable>(
        from table: String,
        type: T.Type,
        query: String = ""
    ) async throws -> [T] {
        guard let token = SuperDBAuth.shared.getToken() else {
            throw URLError(.userAuthenticationRequired)
        }

        // Correto: api.superdb.com.br/ (sem /rest/v1/)
        let urlString = "\(dataURL)/\(table)\(query)"
        var request = URLRequest(url: URL(string: urlString)!)
        request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
        request.setValue(anonKey, forHTTPHeaderField: "apikey")
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")

        let (data, _) = try await URLSession.shared.data(for: request)
        return try JSONDecoder().decode([T].self, from: data)
    }
}

// Uso:
struct Post: Codable {
    let id: Int
    let title: String
    let body: String?
}

// Em um ViewModel ou View:
let db = SuperDBDatabase()
let posts = try await db.fetch(
    from: "posts",
    type: Post.self,
    query: "?order=created_at.desc&limit=20"
)

Alternativa com Alamofire

Se preferir usar Alamofire (Swift Package Manager: https://github.com/Alamofire/Alamofire):

Com Alamofire
import Alamofire

let token = SuperDBAuth.shared.getToken() ?? ""

// Dados: api.superdb.com.br/ (sem /rest/v1/)
AF.request(
    "https://api.superdb.com.br/posts",
    headers: [
        "Authorization": "Bearer \(token)",
        "apikey": anonKey
    ]
)
.responseDecodable(of: [Post].self) { response in
    switch response.result {
    case .success(let posts):
        print("Posts: \(posts)")
    case .failure(let error):
        print("Erro: \(error)")
    }
}

Quando o SDK Swift estiver disponível

Essa página ajudou?