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. Precisa do SDK Swift? Conte pelo suporte →
Abordagem: URLSession + Keychain
No iOS nativo, o fluxo é:
- Fazer login via
URLSessionchamando a API REST do SuperDB - Armazenar os tokens no Keychain do iOS — o equivalente seguro ao SecureStore do Expo. São dois:
access_token(sessão) edata_plane_token(dados) - Mandar o
data_plane_tokenemAuthorization: Bearernas chamadas aapi.superdb.com.br— oaccess_tokennão é aceito lá
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 // sessão — auth.superdb.com.br
let dataPlaneToken: String // dados — api.superdb.com.br
let refreshToken: String
let user: UserInfo?
enum CodingKeys: String, CodingKey {
case accessToken = "access_token"
case dataPlaneToken = "data_plane_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..."
// O slug do projeto. É ELE que diz ao servidor de qual tenant se trata — a
// anon key não faz isso. Sem este header, toda chamada de auth devolve
// 404 tenant_not_found, fácil de confundir com URL errada.
private let projeto = "meu-projeto"
private let tokenKey = "superdb_access_token"
private let dataPlaneKey = "superdb_data_plane_token"
private var defaultHeaders: [String: String] {
["Content-Type": "application/json", "X-SuperDB-Project": projeto, "apikey": anonKey]
}
// MARK: - Keychain helpers
// São duas contas no Keychain porque são dois tokens com destinos
// diferentes: sessão e dados.
private func save(_ token: String, conta: String) {
let data = Data(token.utf8)
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: conta,
kSecValueData as String: data
]
SecItemDelete(query as CFDictionary)
SecItemAdd(query as CFDictionary, nil)
}
private func read(conta: String) -> String? {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: conta,
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)
}
/// Guarde os DOIS depois do login: `save(r.accessToken, ...)` e
/// `save(r.dataPlaneToken, ...)`.
func saveTokens(_ r: AuthResponse) {
save(r.accessToken, conta: tokenKey)
save(r.dataPlaneToken, conta: dataPlaneKey)
}
/// Sessão — auth.superdb.com.br.
func getToken() -> String? { read(conta: tokenKey) }
/// Dados — api.superdb.com.br. Vale 1 hora; renove em
/// POST /auth/v1/token/refresh com o refresh_token.
func getDataPlaneToken() -> String? { read(conta: dataPlaneKey) }
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/signin")!)
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)
saveTokens(authResponse) // guarda a sessão E o token de dados
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"
func fetch<T: Decodable>(
from table: String,
type: T.Type,
query: String = ""
) async throws -> [T] {
// É o data_plane_token, não o access_token. Mandar o de sessão
// aqui devolve 401 — os dois vêm no mesmo corpo do login.
guard let token = SuperDBAuth.shared.getDataPlaneToken() else {
throw URLError(.userAuthenticationRequired)
}
// Correto: api.superdb.com.br/ (sem /rest/v1/)
// O tenant sai do próprio token; não há header de schema a montar.
let urlString = "\(dataURL)/\(table)\(query)"
var request = URLRequest(url: URL(string: urlString)!)
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
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
// Dados pedem o data_plane_token — não o de sessão.
let token = SuperDBAuth.shared.getDataPlaneToken() ?? ""
// Dados: api.superdb.com.br/ (sem /rest/v1/)
AF.request(
"https://api.superdb.com.br/posts",
headers: ["Authorization": "Bearer \(token)"]
)
.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
- Peça o SDK Swift pelo suporte
- Referência completa da API REST
- Como funcionam os JWT tokens
- Expo (React Native) — SDK JS disponível hoje
Essa página ajudou?