Defining Models

Models in SwiftDataServer are defined using the @Model macro, providing 100% API compatibility with Apple's SwiftData.

Basic Model

import SwiftDataServer

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

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

    var age: Int = 0
    var isActive: Bool = true
    var createdAt: Date = Date()

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

What @Model Generates

The @Model macro automatically generates:

  • id property - A unique UUID identifier
  • PersistentModel conformance - Required protocol for persistence
  • Codable conformance - Automatic encoding/decoding
  • Schema metadata - Maps properties to database columns
  • Change tracking - Tracks modified properties for efficient updates

Supported Property Types

Swift Type Database Type
String TEXT / VARCHAR
Int, Int8, Int16, Int32, Int64 INTEGER / BIGINT
Double, Float DOUBLE PRECISION / REAL
Bool BOOLEAN
Date TIMESTAMP
UUID UUID
Data BYTEA / BLOB
Optional<T> NULLABLE column
[T] (Codable arrays) JSON

Default Values

All stored properties must have default values so SwiftDataServer can deserialize objects from the database:

@Model
final class Article {
    var title: String = ""              // Required - empty string default
    var viewCount: Int = 0             // Required - zero default
    var publishedAt: Date? = nil       // Optional - nil default
    var tags: [String] = []            // Array - empty array default
    var createdAt: Date = Date()       // Date - current date default
}

Custom Initializers

Define custom initializers to set properties at creation time:

@Model
final class User {
    var name: String = ""
    var email: String = ""
    var createdAt: Date = Date()

    // Custom initializer
    init(name: String, email: String) {
        self.name = name
        self.email = email
        // createdAt uses default value
    }

    // Multiple initializers are allowed
    convenience init(email: String) {
        self.init(name: "Unknown", email: email)
    }
}

Computed Properties

Computed properties are not persisted but can be useful for derived values:

@Model
final class User {
    var firstName: String = ""
    var lastName: String = ""
    var birthDate: Date = Date()

    // Computed - not persisted
    var fullName: String {
        "\(firstName) \(lastName)"
    }

    var age: Int {
        Calendar.current.dateComponents([.year], from: birthDate, to: Date()).year ?? 0
    }
}

Transient Properties

Use @Transient to exclude stored properties from persistence:

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

    @Transient
    var temporaryToken: String?        // Not saved to database

    @Transient
    var cachedData: [String: Any]?     // Not saved to database
}

Enums

Use raw value enums for type-safe status fields:

enum Status: String, Codable {
    case pending
    case active
    case suspended
}

@Model
final class User {
    var name: String = ""
    var statusRawValue: String = Status.pending.rawValue

    @Transient
    var status: Status {
        get { Status(rawValue: statusRawValue) ?? .pending }
        set { statusRawValue = newValue.rawValue }
    }
}

Next Steps