Swift SDK
Swift Quick Start
WowSQL Swift SDK — Get started in minutes
Get your API keys: In your WowSQL project dashboard, go to Settings → API to find your anon key (wowsql_anon_...) and service role key (wowsql_service_...). The anon key is safe for client-side use (respects Row Level Security). The service role key bypasses RLS and should only be used server-side.
Installation
Official package: View on GitHub →
Swift Package Manager: Add to your Package.swift:
// swift-tools-version: 5.9
import PackageDescription
let package = Package(
name: "MyApp",
platforms: [.iOS(.v13), .macOS(.v10_15)],
dependencies: [
.package(url: "https://github.com/WowSQL/WowSQL-swift.git", from: "1.0.0")
],
targets: [
.target(
name: "MyApp",
dependencies: ["WowSQL"]
),
]
)
Or in Xcode: File → Add Package Dependencies → https://github.com/WowSQL/WowSQL-swift
Initialize Client
import WowSQL
// Initialize client with your anon key (respects Row Level Security)
// URL resolves to: https://myproject.wowsqlconnect.com
// SDK talks to PostgREST at /rest/v1/
let client = WowSQLClient(
projectURL: "myproject",
apiKey: "wowsql_anon_your_key_here"
)
// Or with full URL
let clientFull = WowSQLClient(
projectURL: "https://myproject.wowsqlconnect.com",
apiKey: "wowsql_anon_your_key_here"
)
// Always use select() before get()/execute()
client.table("users").select("*").get { _ in }
Query Data
Basic Queries
client.table("users").select("*").get { result in /* users */ }
client.table("users").select("*").gte("age", 18).get { result in /* adults */ }
client.table("users").select("id", "name").get { result in /* names */ }
Advanced Queries
client.table("users")
.select("*")
.filter("status", "eq", "active")
.filter("age", "gt", 18)
.order("created_at", "desc")
.limit(20)
.offset(0)
.get { result in
if case .success(let users) = result {
print("Found \(users.count) users")
}
}
Filter Helper Methods
client.table("users")
.eq("status", "active")
.gt("age", 18)
.like("email", "%@gmail.com")
.in("role", ["admin", "moderator"])
.between("created_at", "2024-01-01", "2024-12-31")
.get { _ in }
GROUP BY and Aggregates
Basic GROUP BY
client.table("products")
.select("category", "COUNT(*) as count", "AVG(price) as avg_price")
.groupBy("category")
.get { _ in }
HAVING Clause
client.table("products")
.select("category", "COUNT(*) as count")
.groupBy("category")
.having("COUNT(*)", "gt", 10)
.get { _ in }
CRUD Operations
Create Record
client.table("users").create([
"name": "John Doe",
"email": "john@example.com",
"age": 30
]) { result in
if case .success(let r) = result { print("Created ID: \(r["id"] ?? "")") }
}
Update Record
client.table("users").update(123, ["email": "newemail@example.com"]) { _ in }
Delete Record
client.table("users").delete(123) { _ in }
Get Single Record by ID
client.table("users").getById(123) { result in
if case .success(let user) = result { print("User: \(user["name"] ?? "")") }
}
Get First Record
client.table("users").filter("status", "eq", "active").first { result in
if case .success(let user) = result, let u = user { print("First: \(u["name"] ?? "")") }
}
Filter Operators
Available operators: eq, neq, gt, gte, lt, lte, like, is, in, not_in, between, not_between. Use .eq("col", value), .filter("col", "op", value), etc.
Error Handling
client.table("users").select("*").get { result in
switch result {
case .success(let users):
print("Success: \(users.data.count) users")
case .failure(let error):
print("Error: \(error.localizedDescription)")
}
}
Authentication
Authentication endpoints are served at /auth/v1/. Use your anon key for client-side auth operations.
Sign Up & Sign In
// Auth endpoints: POST /auth/v1/signup, POST /auth/v1/login
// Header: apikey: wowsql_anon_your_key_here
let auth = WowSQLAuth(projectURL: "myproject", apiKey: "wowsql_anon_your_key_here")
auth.signUp(email: "user@example.com", password: "SecurePassword123", fullName: "John Doe") { result in /* access_token */ }
auth.signIn(email: "user@example.com", password: "SecurePassword123") { result in /* user id */ }
Password Reset
auth.forgotPassword(email: "user@example.com") { _ in }
auth.resetPassword(token: "reset_token", newPassword: "NewSecurePassword123") { _ in }
OAuth Authentication
Setup checklist — do this once per provider, per project:
1. Dashboard → Project → Auth → Providers: enable the provider, paste Client ID and Client Secret (no leading/trailing spaces).
2. Copy the Redirect URI shown: https://<slug>.wowsqlconnect.com/auth/v1/oauth/<provider>/callback and register it in your provider's developer console.
3. The callback URL is handled entirely by WowSQL — your app does not send an API key on it. WowSQL exchanges the code and redirects to your frontend_redirect_uri with ?access_token=&refresh_token=.
// ── Step 1: Get the authorization URL ────────────────────────────────────────
auth.getOAuthAuthorizationUrl(provider: "google", redirectUri: "https://yourapp.com/auth/callback") { result in
switch result {
case .success(let oauth):
// Open oauth.authorizationUrl in ASWebAuthenticationSession or SFSafariViewController
// Google calls: https://<slug>.wowsqlconnect.com/auth/v1/oauth/google/callback
// WowSQL exchanges the code and redirects to your redirectUri.
break
case .failure(let error):
print("OAuth error: \(error)")
}
}
// ── Step 2: App — handle the redirect ────────────────────────────────────────
// WowSQL redirects to: https://yourapp.com/auth/callback?access_token=...&refresh_token=...
// Intercept the URL in your ASWebAuthenticationSession callback and parse the tokens.
// ── (Advanced) Manual code exchange — only if you handle the callback yourself
auth.exchangeOAuthCallback(provider: "google", code: "authorization_code_from_callback",
redirectUri: "https://yourapp.com/auth/callback") { result in
if case .success(let session) = result { print("OAuth login: \(session.user.id)") }
}
Initialize Storage
// Storage endpoints are at /storage/v1/
let storage = WowSQLStorage(
projectURL: "myproject",
apiKey: "wowsql_anon_your_key_here"
)
File Operations
// Upload file — POST /storage/v1/object/{bucket}/{path}
storage.uploadFromPath("path/to/document.pdf", remotePath: "uploads/document.pdf", folder: "documents") { result in }
// Download file — GET /storage/v1/object/{bucket}/{path}
storage.getFileUrl("uploads/document.pdf", expiresIn: 3600) { result in }
// List files — POST /storage/v1/object/list/{bucket}
storage.listFiles(prefix: "uploads/") { result in }
// Delete file
storage.deleteFile("uploads/document.pdf") { _ in }
// Check quota
storage.getQuota() { result in }
Download or View File
Via REST: use GET /storage/v1/object/{bucket}/{path} for download, or GET /storage/v1/object/public/{bucket}/{path} for public file access.
Schema DDL (WowSQLSchema)
Requires service role key. createTable requires primaryKey to name a column whose type is UUID. Use auto_increment on that column for DEFAULT gen_random_uuid(). Raw CREATE TABLE via execute SQL cannot use SERIAL PRIMARY KEY (or similar integer PK). Existing tables are unchanged.
let schema = WowSQLSchema(
projectURL: "myproject",
apiKey: "wowsql_service_your_key_here"
)
try await schema.createTable("items", columns: [
["name": "id", "type": "UUID", "auto_increment": true],
["name": "title", "type": "TEXT"],
], primaryKey: "id")
Example API Call (REST)
The SDK wraps PostgREST. Here's what the underlying HTTP calls look like:
Query (GET)
GET https://myproject.wowsqlconnect.com/rest/v1/users?select=id,email,name&status=eq.active&order=created_at.desc&limit=10
Headers:
apikey: wowsql_anon_your_key_here
Content-Type: application/json
Insert (POST)
POST https://myproject.wowsqlconnect.com/rest/v1/users
Headers:
apikey: wowsql_anon_your_key_here
Content-Type: application/json
Prefer: return=representation
Body:
{
"name": "John Doe",
"email": "john@example.com",
"age": 30
}
Update (PATCH)
PATCH https://myproject.wowsqlconnect.com/rest/v1/users?id=eq.123
Headers:
apikey: wowsql_anon_your_key_here
Content-Type: application/json
Prefer: return=representation
Body:
{
"email": "newemail@example.com"
}
Delete (DELETE)
DELETE https://myproject.wowsqlconnect.com/rest/v1/users?id=eq.123
Headers:
apikey: wowsql_anon_your_key_here