Quick Start

Get up and running with SwiftDataServer in just a few steps.

1. Define a Model

Use the @Model macro to define your persistent models:

import SwiftDataServer

@Model
final class User {
    var name: String = ""

    @Attribute(.unique)
    var email: String = ""

    var age: Int = 0
    var createdAt: Date = Date()

    init(name: String, email: String, age: Int) {
        self.name = name
        self.email = email
        self.age = age
    }
}

The @Model macro automatically generates an id, PersistentModel conformance, Codable conformance, schema metadata, and change tracking. The @Attribute(.unique) ensures email addresses are unique in the database.

2. Configure the Database

import SwiftDataServer
import PostgreSQLBackend

// Create backend
let backend = PostgreSQLBackend(
    hostname: "localhost",
    port: 5432,
    username: "postgres",
    password: "password",
    database: "myapp"
)

// Connect
try await backend.connect()

// Create container
let container = try ModelContainer(
    for: User.self,
    configurations: ModelConfiguration(backend: backend)
)

// Run migrations
try await backend.migrate(schema: container.schema)

3. Perform CRUD Operations

let context = container.mainContext()

// Create
let user = User(name: "Alice", email: "alice@example.com", age: 28)
context.insert(user)
try context.save()

// Read all users
let allUsers = try context.fetch(FetchDescriptor<User>())

// Read with predicate
let adults = #Predicate<User> { $0.age >= 18 }
let adultUsers = try context.fetch(
    FetchDescriptor<User>(predicate: adults)
)

// Update
user.name = "Alice Smith"
try context.save()

// Delete
context.delete(user)
try context.save()

4. Query with Predicates

The #Predicate macro provides type-safe querying:

// Simple comparison
let adults = #Predicate<User> { $0.age >= 18 }

// Complex conditions
let activeAdults = #Predicate<User> { user in
    user.age >= 18 && user.active == true
}

// String operations
let searchResults = #Predicate<User> { user in
    user.name.contains("John") || user.email.hasPrefix("john")
}

// Nil checks
let hasEmail = #Predicate<User> { $0.email != nil }

5. Sort and Paginate

let descriptor = FetchDescriptor<User>()
    .filter(adults)
    .sorted(by: SortDescriptor(propertyName: "name"))
    .limit(20)
    .offset(40)

// Or use pagination helper
let page3 = FetchDescriptor<User>.all.paginated(page: 3, pageSize: 20)

Next Steps