Swift

作者 appwriteab3c90b37c95無授權條款10 個星標收錄於 2026年10月8日更新於 2026年10月8日儲存庫7 週前更新

Appwrite Swift SDK skill. Use when building native iOS, macOS, watchOS, or tvOS apps, or server-side Swift applications with Appwrite. Covers client-side auth (email, OAuth), database queries, file uploads, real-time subscriptions with async/await, and server-side admin via API keys for user management, database administration, storage, and functions.

AI 產生的概覽

在 Apple 平台與伺服器端 Swift 應用程式中使用 Appwrite Swift SDK 的參考指南。

功能
提供 Appwrite SDK 的 Swift 程式碼範例與指引,涵蓋用戶端設定、身分驗證、資料庫與資料表操作、查詢、檔案儲存、團隊、即時訂閱、無伺服器函式、SSR 身分驗證、錯誤處理與權限。它只是純說明性參考,不包含指令碼或資源。
適用情境
適用於建置與 Appwrite 整合的原生 iOS、macOS、watchOS 或 tvOS 應用程式,或伺服器端 Swift 應用程式。也適用於在 Swift 中實作 Appwrite 身分驗證、資料庫查詢、儲存、即時更新或權限規則。
執行需求
此技能不附帶指令碼。使用範例需要透過 Swift Package Manager 引入 Appwrite Swift SDK,並需要 Appwrite 專案端點與專案 ID;伺服器端管理操作還需要 API 金鑰。執行時需要連線至 Appwrite 執行個體的網路存取。

Appwrite Swift SDK

Installation

swift
// Swift Package Manager — Package.swift.package(url: "https://github.com/appwrite/sdk-for-swift", branch: "main")

Setting Up the Client

Client-side (Apple platforms)

swift
import Appwrite
let client = Client()    .setEndpoint("https://<REGION>.cloud.appwrite.io/v1")    .setProject("[PROJECT_ID]")

Server-side (Swift)

swift
import Appwrite
let client = Client()    .setEndpoint("https://<REGION>.cloud.appwrite.io/v1")    .setProject(ProcessInfo.processInfo.environment["APPWRITE_PROJECT_ID"]!)    .setKey(ProcessInfo.processInfo.environment["APPWRITE_API_KEY"]!)

Code Examples

Authentication (client-side)

swift
let account = Account(client)
// Signuplet user = try await account.create(userId: ID.unique(), email: "[email protected]", password: "password123", name: "User Name")
// Loginlet session = try await account.createEmailPasswordSession(email: "[email protected]", password: "password123")
// OAuthtry await account.createOAuth2Session(provider: .google)
// Get current userlet me = try await account.get()
// Logouttry await account.deleteSession(sessionId: "current")

User Management (server-side)

swift
let users = Users(client)
// Create userlet user = try await users.create(userId: ID.unique(), email: "[email protected]", password: "password123", name: "User Name")
// List userslet list = try await users.list(queries: [Query.limit(25)])
// Get userlet fetched = try await users.get(userId: "[USER_ID]")
// Delete usertry await users.delete(userId: "[USER_ID]")

Database Operations

Note: Use TablesDB (not the deprecated Databases class) for all new code. Only use Databases if the existing codebase already relies on it or the user explicitly requests it.

Tip: Prefer named parameters (e.g., databaseId: "...") for all SDK method calls. Only use positional arguments if the existing codebase already uses them or the user explicitly requests it.

swift
let tablesDB = TablesDB(client)
// Create database (server-side only)let db = try await tablesDB.create(databaseId: ID.unique(), name: "My Database")
// Create rowlet doc = try await tablesDB.createRow(databaseId: "[DATABASE_ID]", tableId: "[TABLE_ID]", rowId: ID.unique(), data: [    "title": "Hello",    "done": false])
// Query rowslet results = try await tablesDB.listRows(databaseId: "[DATABASE_ID]", tableId: "[TABLE_ID]", queries: [    Query.equal("done", value: false),    Query.limit(10)])
// Get rowlet row = try await tablesDB.getRow(databaseId: "[DATABASE_ID]", tableId: "[TABLE_ID]", rowId: "[ROW_ID]")
// Update rowtry await tablesDB.updateRow(databaseId: "[DATABASE_ID]", tableId: "[TABLE_ID]", rowId: "[ROW_ID]", data: ["done": true])
// Delete rowtry await tablesDB.deleteRow(databaseId: "[DATABASE_ID]", tableId: "[TABLE_ID]", rowId: "[ROW_ID]")
String Column Types

Note: The legacy string type is deprecated. Use explicit column types for all new columns.

TypeMax charactersIndexingStorage
varchar16,383Full index (if size ≤ 768)Inline in row
text16,383Prefix onlyOff-page
mediumtext4,194,303Prefix onlyOff-page
longtext1,073,741,823Prefix onlyOff-page
  • varchar is stored inline and counts towards the 64 KB row size limit. Prefer for short, indexed fields like names, slugs, or identifiers.
  • text, mediumtext, and longtext are stored off-page (only a 20-byte pointer lives in the row), so they don't consume the row size budget. size is not required for these types.
swift
// Create table with explicit string column typestry await tablesDB.createTable(    databaseId: "[DATABASE_ID]",    tableId: ID.unique(),    name: "articles",    columns: [        ["key": "title",    "type": "varchar",    "size": 255, "required": true],        ["key": "summary",  "type": "text",                    "required": false],        ["key": "body",     "type": "mediumtext",              "required": false],        ["key": "raw_data", "type": "longtext",                "required": false],    ])

Query Methods

swift
// FilteringQuery.equal("field", value: "value")          // == (or pass array for IN)Query.notEqual("field", value: "value")       // !=Query.lessThan("field", value: 100)           // <Query.lessThanEqual("field", value: 100)      // <=Query.greaterThan("field", value: 100)        // >Query.greaterThanEqual("field", value: 100)   // >=Query.between("field", start: 1, end: 100)    // 1 <= field <= 100Query.isNull("field")                         // is nullQuery.isNotNull("field")                      // is not nullQuery.startsWith("field", value: "prefix")    // starts withQuery.endsWith("field", value: "suffix")      // ends withQuery.contains("field", value: "sub")         // containsQuery.search("field", value: "keywords")      // full-text search (requires index)
// SortingQuery.orderAsc("field")Query.orderDesc("field")
// PaginationQuery.limit(25)                               // max rows (default 25, max 100)Query.offset(0)                               // skip N rowsQuery.cursorAfter("[ROW_ID]")                 // cursor pagination (preferred)Query.cursorBefore("[ROW_ID]")
// Selection & LogicQuery.select(["field1", "field2"])Query.or([Query.equal("a", value: 1), Query.equal("b", value: 2)])   // ORQuery.and([Query.greaterThan("age", value: 18), Query.lessThan("age", value: 65)])  // AND (default)

File Storage

swift
let storage = Storage(client)
// Upload filelet file = try await storage.createFile(bucketId: "[BUCKET_ID]", fileId: ID.unique(), file: InputFile.fromPath("/path/to/file.png"))
// List fileslet files = try await storage.listFiles(bucketId: "[BUCKET_ID]")
// Delete filetry await storage.deleteFile(bucketId: "[BUCKET_ID]", fileId: "[FILE_ID]")
InputFile Factory Methods
swift
InputFile.fromPath("/path/to/file.png")                    // from filesystem pathInputFile.fromData(data, filename: "file.png", mimeType: "image/png")  // from Data

Teams

swift
let teams = Teams(client)
// Create teamlet team = try await teams.create(teamId: ID.unique(), name: "Engineering")
// List teamslet list = try await teams.list()
// Create membership (invite user by email)let membership = try await teams.createMembership(    teamId: "[TEAM_ID]",    roles: ["editor"],    email: "[email protected]")
// List membershipslet members = try await teams.listMemberships(teamId: "[TEAM_ID]")
// Update membership rolestry await teams.updateMembership(teamId: "[TEAM_ID]", membershipId: "[MEMBERSHIP_ID]", roles: ["admin"])
// Delete teamtry await teams.delete(teamId: "[TEAM_ID]")

Role-based access: Use Role.team("[TEAM_ID]") for all team members or Role.team("[TEAM_ID]", "editor") for a specific team role when setting permissions.

Real-time Subscriptions (client-side)

swift
let realtime = Realtime(client)
// Subscribe to row changeslet subscription = try await realtime.subscribe(channels: [    Channel.tablesdb("[DATABASE_ID]").table("[TABLE_ID]").row()]) { response in    print(response.events)   // e.g. ["tablesdb.*.tables.*.rows.*.create"]    print(response.payload)  // the affected resource}
// Subscribe to multiple channelslet multi = try await realtime.subscribe(channels: [    Channel.tablesdb("[DATABASE_ID]").table("[TABLE_ID]").row(),    Channel.files(),]) { response in /* ... */ }
// Cleanuptry await subscription.close()

Available channels:

ChannelDescription
accountChanges to the authenticated user's account
tablesdb.[DB_ID].tables.[TABLE_ID].rowsAll rows in a table
tablesdb.[DB_ID].tables.[TABLE_ID].rows.[ROW_ID]A specific row
buckets.[BUCKET_ID].filesAll files in a bucket
buckets.[BUCKET_ID].files.[FILE_ID]A specific file
teamsChanges to teams the user belongs to
teams.[TEAM_ID]A specific team
membershipsThe user's team memberships
functions.[FUNCTION_ID].executionsFunction execution updates

Response fields: events (array), payload (resource), channels (matched), timestamp (ISO 8601).

Serverless Functions (server-side)

swift
let functions = Functions(client)
// Execute functionlet execution = try await functions.createExecution(functionId: "[FUNCTION_ID]", body: "{\"key\": \"value\"}")
// List executionslet executions = try await functions.listExecutions(functionId: "[FUNCTION_ID]")
Writing a Function Handler (Swift runtime)
swift
// Sources/main.swift — Appwrite Function entry pointfunc main(context: RuntimeContext) async throws -> RuntimeOutput {    // context.req.body        — raw body (String)    // context.req.bodyJson    — parsed JSON ([String: Any]?)    // context.req.headers     — headers ([String: String])    // context.req.method      — HTTP method    // context.req.path        — URL path    // context.req.query       — query params ([String: String])
    context.log("Processing: \(context.req.method) \(context.req.path)")
    if context.req.method == "GET" {        return context.res.json(["message": "Hello from Appwrite Function!"])    }
    return context.res.json(["success": true])       // JSON    // context.res.text("Hello")                     // plain text    // context.res.empty()                           // 204    // context.res.redirect("https://...")            // 302}

Server-Side Rendering (SSR) Authentication

SSR apps using server-side Swift (Vapor, Hummingbird, etc.) use the server SDK to handle auth. You need two clients:

  • Admin client — uses an API key, creates sessions, bypasses rate limits (reusable singleton)
  • Session client — uses a session cookie, acts on behalf of a user (create per-request, never share)
swift
import Appwrite
// Admin client (reusable)let adminClient = Client()    .setEndpoint("https://<REGION>.cloud.appwrite.io/v1")    .setProject("[PROJECT_ID]")    .setKey(Environment.get("APPWRITE_API_KEY")!)
// Session client (create per-request)let sessionClient = Client()    .setEndpoint("https://<REGION>.cloud.appwrite.io/v1")    .setProject("[PROJECT_ID]")
if let session = req.cookies["a_session_[PROJECT_ID]"]?.string {    sessionClient.setSession(session)}
Email/Password Login (Vapor)
swift
app.post("login") { req async throws -> Response in    let body = try req.content.decode(LoginRequest.self)    let account = Account(adminClient)    let session = try await account.createEmailPasswordSession(        email: body.email,        password: body.password    )
    // Cookie name must be a_session_<PROJECT_ID>    let response = Response(status: .ok, body: .init(string: "{\"success\": true}"))    response.cookies["a_session_[PROJECT_ID]"] = HTTPCookies.Value(        string: session.secret,        isHTTPOnly: true,        isSecure: true,        sameSite: .strict,        path: "/"    )    return response}
Authenticated Requests
swift
app.get("user") { req async throws -> Response in    guard let session = req.cookies["a_session_[PROJECT_ID]"]?.string else {        throw Abort(.unauthorized)    }
    let sessionClient = Client()        .setEndpoint("https://<REGION>.cloud.appwrite.io/v1")        .setProject("[PROJECT_ID]")        .setSession(session)
    let account = Account(sessionClient)    let user = try await account.get()    // Return user as JSON}
OAuth2 SSR Flow
swift
// Step 1: Redirect to OAuth providerapp.get("oauth") { req async throws -> Response in    let account = Account(adminClient)    let redirectUrl = try await account.createOAuth2Token(        provider: .github,        success: "https://example.com/oauth/success",        failure: "https://example.com/oauth/failure"    )    return req.redirect(to: redirectUrl)}
// Step 2: Handle callback — exchange token for sessionapp.get("oauth", "success") { req async throws -> Response in    let userId = try req.query.get(String.self, at: "userId")    let secret = try req.query.get(String.self, at: "secret")
    let account = Account(adminClient)    let session = try await account.createSession(userId: userId, secret: secret)
    let response = Response(status: .ok, body: .init(string: "{\"success\": true}"))    response.cookies["a_session_[PROJECT_ID]"] = HTTPCookies.Value(        string: session.secret,        isHTTPOnly: true, isSecure: true, sameSite: .strict, path: "/"    )    return response}

Cookie security: Always use isHTTPOnly, isSecure, and sameSite: .strict to prevent XSS. The cookie name must be a_session_<PROJECT_ID>.

Forwarding user agent: Call sessionClient.setForwardedUserAgent(req.headers.first(name: .userAgent) ?? "") to record the end-user's browser info for debugging and security.

Error Handling

swift
import Appwrite// AppwriteException is included in the main module
do {    let row = try await tablesDB.getRow(databaseId: "[DATABASE_ID]", tableId: "[TABLE_ID]", rowId: "[ROW_ID]")} catch let error as AppwriteException {    print(error.message)     // human-readable message    print(error.code)        // HTTP status code (Int)    print(error.type)        // error type (e.g. "document_not_found")    print(error.response)    // full response body}

Common error codes:

CodeMeaning
401Unauthorized — missing or invalid session/API key
403Forbidden — insufficient permissions
404Not found — resource does not exist
409Conflict — duplicate ID or unique constraint
429Rate limited — too many requests

Permissions & Roles (Critical)

Appwrite uses permission strings to control access to resources. Each permission pairs an action (read, update, delete, create, or write which grants create + update + delete) with a role target. By default, no user has access unless permissions are explicitly set at the row/file level or inherited from the table/bucket settings. Permissions are arrays of strings built with the Permission and Role helpers.

swift
import Appwrite// Permission and Role are included in the main module import

Database Row with Permissions

swift
let doc = try await tablesDB.createRow(    databaseId: "[DATABASE_ID]",    tableId: "[TABLE_ID]",    rowId: ID.unique(),    data: ["title": "Hello World"],    permissions: [        Permission.read(Role.user("[USER_ID]")),     // specific user can read        Permission.update(Role.user("[USER_ID]")),   // specific user can update        Permission.read(Role.team("[TEAM_ID]")),     // all team members can read        Permission.read(Role.any()),                 // anyone (including guests) can read    ])

File Upload with Permissions

swift
let file = try await storage.createFile(    bucketId: "[BUCKET_ID]",    fileId: ID.unique(),    file: InputFile.fromPath("/path/to/file.png"),    permissions: [        Permission.read(Role.any()),        Permission.update(Role.user("[USER_ID]")),        Permission.delete(Role.user("[USER_ID]")),    ])

When to set permissions: Set row/file-level permissions when you need per-resource access control. If all rows in a table share the same rules, configure permissions at the table/bucket level and leave row permissions empty.

Common mistakes:

  • Forgetting permissions — the resource becomes inaccessible to all users (including the creator)
  • Role.any() with write/update/delete — allows any user, including unauthenticated guests, to modify or remove the resource
  • Permission.read(Role.any()) on sensitive data — makes the resource publicly readable

來源與署名

來源:appwrite/claude-plugin位於skills/swift提交ab3c90b

授權條款: 無授權條款

內容歸原作者所有。SourceWeft 從公開儲存庫中收錄這些內容。

檢舉或申請下架