REST API Backend

The REST API backend allows iOS and macOS apps to connect to a remote SwiftDataServer instance. Use the same SwiftData API you know on the client, with your server handling persistence.

Installation

Add the REST backend to your target:

.product(name: "RESTBackend", package: "SwiftDataServer")

Configuration

import SwiftDataServer
import RESTBackend

let backend = RESTBackend(
    baseURL: URL(string: "https://api.example.com")!,
    accessToken: "your-access-token"
)

let container = try ModelContainer(for: User.self, Post.self)
try await container.connect(to: backend)

Authentication

Configure authentication headers for your API:

// Bearer token authentication
let backend = RESTBackend(
    baseURL: URL(string: "https://api.example.com")!,
    accessToken: "your-jwt-token"
)

// Custom headers
let backend = RESTBackend(
    baseURL: URL(string: "https://api.example.com")!,
    headers: [
        "Authorization": "Bearer \(token)",
        "X-API-Key": "your-api-key"
    ]
)

Usage

Once configured, use the standard SwiftData APIs:

let context = container.mainContext()

// Fetch all users
let users = try await context.fetchAsync(FetchDescriptor<User>())

// Create a new user
let user = User(name: "Alice", email: "alice@example.com")
context.insert(user)
try await context.saveAsync()

// Query with predicates
let predicate = #Predicate<User> { $0.age > 18 }
let adults = try await context.fetchAsync(
    FetchDescriptor<User>(predicate: predicate)
)

Server Setup

Your server needs to expose the SwiftDataServer REST API. With Vapor:

import Vapor
import SwiftDataServer
import VaporIntegration

func configure(_ app: Application) async throws {
    // ... database setup ...

    // Register REST API routes
    try app.swiftData.registerRESTRoutes()
}

This exposes endpoints for all registered models at /api/v1/{model}.

Offline Support

Enable local caching for offline access:

let backend = RESTBackend(
    baseURL: URL(string: "https://api.example.com")!,
    accessToken: "your-token",
    cachePolicy: .cacheFirst,        // Use cache when offline
    cacheDuration: 3600              // 1 hour
)

Error Handling

do {
    let users = try await context.fetchAsync(FetchDescriptor<User>())
} catch let error as RESTBackendError {
    switch error {
    case .networkError(let underlying):
        print("Network error: \(underlying)")
    case .unauthorized:
        print("Invalid or expired token")
    case .serverError(let status, let message):
        print("Server error \(status): \(message)")
    }
}

Requirements

  • iOS 17+ / macOS 14+
  • A SwiftDataServer instance with REST routes enabled
  • Network connectivity (or cached data with offline support)

Next Steps