appwrite-swift
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseAppwrite Swift SDK
Appwrite Swift SDK
Installation
安装
swift
// Swift Package Manager — Package.swift
.package(url: "https://github.com/appwrite/sdk-for-swift", branch: "main")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)
Apple 平台客户端
swift
import Appwrite
let client = Client()
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1")
.setProject("[PROJECT_ID]")swift
import Appwrite
let client = Client()
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1")
.setProject("[PROJECT_ID]")Server-side (Swift)
服务端 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"]!)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)
// Signup
let user = try await account.create(userId: ID.unique(), email: "user@example.com", password: "password123", name: "User Name")
// Login
let session = try await account.createEmailPasswordSession(email: "user@example.com", password: "password123")
// OAuth
try await account.createOAuth2Session(provider: .google)
// Get current user
let me = try await account.get()
// Logout
try await account.deleteSession(sessionId: "current")swift
let account = Account(client)
// 注册
let user = try await account.create(userId: ID.unique(), email: "user@example.com", password: "password123", name: "User Name")
// 登录
let session = try await account.createEmailPasswordSession(email: "user@example.com", password: "password123")
// OAuth 登录
try await account.createOAuth2Session(provider: .google)
// 获取当前用户
let me = try await account.get()
// 登出
try await account.deleteSession(sessionId: "current")User Management (server-side)
服务端用户管理
swift
let users = Users(client)
// Create user
let user = try await users.create(userId: ID.unique(), email: "user@example.com", password: "password123", name: "User Name")
// List users
let list = try await users.list(queries: [Query.limit(25)])
// Get user
let fetched = try await users.get(userId: "[USER_ID]")
// Delete user
try await users.delete(userId: "[USER_ID]")swift
let users = Users(client)
// 创建用户
let user = try await users.create(userId: ID.unique(), email: "user@example.com", password: "password123", name: "User Name")
// 列出用户
let list = try await users.list(queries: [Query.limit(25)])
// 获取用户信息
let fetched = try await users.get(userId: "[USER_ID]")
// 删除用户
try await users.delete(userId: "[USER_ID]")Database Operations
数据库操作
Note: Use(not the deprecatedTablesDBclass) for all new code. Only useDatabasesif the existing codebase already relies on it or the user explicitly requests it.DatabasesTip: Prefer named parameters (e.g.,) for all SDK method calls. Only use positional arguments if the existing codebase already uses them or the user explicitly requests it.databaseId: "..."
swift
let tablesDB = TablesDB(client)
// Create database (server-side only)
let db = try await tablesDB.create(databaseId: ID.unique(), name: "My Database")
// Create row
let doc = try await tablesDB.createRow(databaseId: "[DATABASE_ID]", tableId: "[TABLE_ID]", rowId: ID.unique(), data: [
"title": "Hello",
"done": false
])
// Query rows
let results = try await tablesDB.listRows(databaseId: "[DATABASE_ID]", tableId: "[TABLE_ID]", queries: [
Query.equal("done", value: false),
Query.limit(10)
])
// Get row
let row = try await tablesDB.getRow(databaseId: "[DATABASE_ID]", tableId: "[TABLE_ID]", rowId: "[ROW_ID]")
// Update row
try await tablesDB.updateRow(databaseId: "[DATABASE_ID]", tableId: "[TABLE_ID]", rowId: "[ROW_ID]", data: ["done": true])
// Delete row
try await tablesDB.deleteRow(databaseId: "[DATABASE_ID]", tableId: "[TABLE_ID]", rowId: "[ROW_ID]")注意: 所有新代码请使用(而非已弃用的TablesDB类)。仅当现有代码库已依赖Databases或用户明确要求时,才使用Databases。Databases提示: 所有 SDK 方法调用优先使用命名参数(例如:)。仅当现有代码库已使用位置参数或用户明确要求时,才使用位置参数。databaseId: "..."
swift
let tablesDB = TablesDB(client)
// 创建数据库(仅服务端可用)
let db = try await tablesDB.create(databaseId: ID.unique(), name: "My Database")
// 创建行
let doc = try await tablesDB.createRow(databaseId: "[DATABASE_ID]", tableId: "[TABLE_ID]", rowId: ID.unique(), data: [
"title": "Hello",
"done": false
])
// 查询行
let results = try await tablesDB.listRows(databaseId: "[DATABASE_ID]", tableId: "[TABLE_ID]", queries: [
Query.equal("done", value: false),
Query.limit(10)
])
// 获取行
let row = try await tablesDB.getRow(databaseId: "[DATABASE_ID]", tableId: "[TABLE_ID]", rowId: "[ROW_ID]")
// 更新行
try await tablesDB.updateRow(databaseId: "[DATABASE_ID]", tableId: "[TABLE_ID]", rowId: "[ROW_ID]", data: ["done": true])
// 删除行
try await tablesDB.deleteRow(databaseId: "[DATABASE_ID]", tableId: "[TABLE_ID]", rowId: "[ROW_ID]")String Column Types
字符串列类型
Note: The legacytype is deprecated. Use explicit column types for all new columns.string
| Type | Max characters | Indexing | Storage |
|---|---|---|---|
| 16,383 | Full index (if size ≤ 768) | Inline in row |
| 16,383 | Prefix only | Off-page |
| 4,194,303 | Prefix only | Off-page |
| 1,073,741,823 | Prefix only | Off-page |
- is stored inline and counts towards the 64 KB row size limit. Prefer for short, indexed fields like names, slugs, or identifiers.
varchar - ,
text, andmediumtextare stored off-page (only a 20-byte pointer lives in the row), so they don't consume the row size budget.longtextis not required for these types.size
swift
// Create table with explicit string column types
try 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],
]
)注意: 旧版类型已弃用。所有新列请使用明确的列类型。string
| 类型 | 最大字符数 | 索引方式 | 存储方式 |
|---|---|---|---|
| 16,383 | 全索引(若大小 ≤ 768) | 行内存储 |
| 16,383 | 仅前缀索引 | 页外存储 |
| 4,194,303 | 仅前缀索引 | 页外存储 |
| 1,073,741,823 | 仅前缀索引 | 页外存储 |
- 存储在行内,会占用 64 KB 的行大小限制。适合用于短文本、需要索引的字段,如名称、别名或标识符。
varchar - 、
text和mediumtext存储在页外(行内仅保留一个 20 字节的指针),因此不会占用行大小配额。这些类型不需要指定longtext。size
swift
// 创建包含明确字符串列类型的表
try 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
// Filtering
Query.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 <= 100
Query.isNull("field") // is null
Query.isNotNull("field") // is not null
Query.startsWith("field", value: "prefix") // starts with
Query.endsWith("field", value: "suffix") // ends with
Query.contains("field", value: "sub") // contains
Query.search("field", value: "keywords") // full-text search (requires index)
// Sorting
Query.orderAsc("field")
Query.orderDesc("field")
// Pagination
Query.limit(25) // max rows (default 25, max 100)
Query.offset(0) // skip N rows
Query.cursorAfter("[ROW_ID]") // cursor pagination (preferred)
Query.cursorBefore("[ROW_ID]")
// Selection & Logic
Query.select(["field1", "field2"])
Query.or([Query.equal("a", value: 1), Query.equal("b", value: 2)]) // OR
Query.and([Query.greaterThan("age", value: 18), Query.lessThan("age", value: 65)]) // AND (default)swift
// 过滤
Query.equal("field", value: "value") // ==(传入数组可实现 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 <= 字段 <= 100
Query.isNull("field") // 为空
Query.isNotNull("field") // 不为空
Query.startsWith("field", value: "prefix") // 以指定前缀开头
Query.endsWith("field", value: "suffix") // 以指定后缀结尾
Query.contains("field", value: "sub") // 包含指定子串
Query.search("field", value: "keywords") // 全文搜索(需要提前创建索引)
// 排序
Query.orderAsc("field")
Query.orderDesc("field")
// 分页
Query.limit(25) // 最大行数(默认25,最大100)
Query.offset(0) // 跳过N行
Query.cursorAfter("[ROW_ID]") // 游标分页(推荐使用)
Query.cursorBefore("[ROW_ID]")
// 字段选择与逻辑组合
Query.select(["field1", "field2"])
Query.or([Query.equal("a", value: 1), Query.equal("b", value: 2)]) // 或逻辑
Query.and([Query.greaterThan("age", value: 18), Query.lessThan("age", value: 65)]) // 与逻辑(默认)File Storage
文件存储
swift
let storage = Storage(client)
// Upload file
let file = try await storage.createFile(bucketId: "[BUCKET_ID]", fileId: ID.unique(), file: InputFile.fromPath("/path/to/file.png"))
// List files
let files = try await storage.listFiles(bucketId: "[BUCKET_ID]")
// Delete file
try await storage.deleteFile(bucketId: "[BUCKET_ID]", fileId: "[FILE_ID]")swift
let storage = Storage(client)
// 上传文件
let file = try await storage.createFile(bucketId: "[BUCKET_ID]", fileId: ID.unique(), file: InputFile.fromPath("/path/to/file.png"))
// 列出文件
let files = try await storage.listFiles(bucketId: "[BUCKET_ID]")
// 删除文件
try await storage.deleteFile(bucketId: "[BUCKET_ID]", fileId: "[FILE_ID]")InputFile Factory Methods
InputFile 工厂方法
swift
InputFile.fromPath("/path/to/file.png") // from filesystem path
InputFile.fromData(data, filename: "file.png", mimeType: "image/png") // from Dataswift
InputFile.fromPath("/path/to/file.png") // 从文件系统路径创建
InputFile.fromData(data, filename: "file.png", mimeType: "image/png") // 从 Data 对象创建Teams
团队管理
swift
let teams = Teams(client)
// Create team
let team = try await teams.create(teamId: ID.unique(), name: "Engineering")
// List teams
let list = try await teams.list()
// Create membership (invite user by email)
let membership = try await teams.createMembership(
teamId: "[TEAM_ID]",
roles: ["editor"],
email: "user@example.com"
)
// List memberships
let members = try await teams.listMemberships(teamId: "[TEAM_ID]")
// Update membership roles
try await teams.updateMembership(teamId: "[TEAM_ID]", membershipId: "[MEMBERSHIP_ID]", roles: ["admin"])
// Delete team
try await teams.delete(teamId: "[TEAM_ID]")Role-based access: Usefor all team members orRole.team("[TEAM_ID]")for a specific team role when setting permissions.Role.team("[TEAM_ID]", "editor")
swift
let teams = Teams(client)
// 创建团队
let team = try await teams.create(teamId: ID.unique(), name: "Engineering")
// 列出团队
let list = try await teams.list()
// 创建团队成员(通过邮箱邀请用户)
let membership = try await teams.createMembership(
teamId: "[TEAM_ID]",
roles: ["editor"],
email: "user@example.com"
)
// 列出团队成员
let members = try await teams.listMemberships(teamId: "[TEAM_ID]")
// 更新团队成员角色
try await teams.updateMembership(teamId: "[TEAM_ID]", membershipId: "[MEMBERSHIP_ID]", roles: ["admin"])
// 删除团队
try await teams.delete(teamId: "[TEAM_ID]")基于角色的权限控制: 设置权限时,使用表示所有团队成员,使用Role.team("[TEAM_ID]")表示团队中的特定角色成员。Role.team("[TEAM_ID]", "editor")
Real-time Subscriptions (client-side)
实时订阅(客户端)
swift
let realtime = Realtime(client)
let subscription = realtime.subscribe(channels: ["databases.[DATABASE_ID].tables.[TABLE_ID].rows"]) { response in
print(response.events) // e.g. ["databases.*.tables.*.rows.*.create"]
print(response.payload) // the affected resource
}
// Subscribe to multiple channels
let multi = realtime.subscribe(channels: [
"databases.[DATABASE_ID].tables.[TABLE_ID].rows",
"buckets.[BUCKET_ID].files",
]) { response in /* ... */ }
// Cleanup
subscription.close()Available channels:
| Channel | Description |
|---|---|
| Changes to the authenticated user's account |
| All rows in a table |
| A specific row |
| All files in a bucket |
| A specific file |
| Changes to teams the user belongs to |
| A specific team |
| The user's team memberships |
| Function execution updates |
Response fields: (array), (resource), (matched), (ISO 8601).
eventspayloadchannelstimestampswift
let realtime = Realtime(client)
let subscription = realtime.subscribe(channels: ["databases.[DATABASE_ID].tables.[TABLE_ID].rows"]) { response in
print(response.events) // 示例:["databases.*.tables.*.rows.*.create"]
print(response.payload) // 受影响的资源数据
}
// 订阅多个频道
let multi = realtime.subscribe(channels: [
"databases.[DATABASE_ID].tables.[TABLE_ID].rows",
"buckets.[BUCKET_ID].files",
]) { response in /* ... */ }
// 取消订阅
subscription.close()可用频道:
| 频道 | 描述 |
|---|---|
| 已认证用户的账户变更 |
| 表中所有行的变更 |
| 特定行的变更 |
| 存储桶中所有文件的变更 |
| 特定文件的变更 |
| 用户所属团队的变更 |
| 特定团队的变更 |
| 用户的团队成员身份变更 |
| 函数执行状态更新 |
响应字段:(数组)、(资源数据)、(匹配的频道)、(ISO 8601 格式时间戳)。
eventspayloadchannelstimestampServerless Functions (server-side)
无服务器函数(服务端)
swift
let functions = Functions(client)
// Execute function
let execution = try await functions.createExecution(functionId: "[FUNCTION_ID]", body: "{\"key\": \"value\"}")
// List executions
let executions = try await functions.listExecutions(functionId: "[FUNCTION_ID]")swift
let functions = Functions(client)
// 执行函数
let execution = try await functions.createExecution(functionId: "[FUNCTION_ID]", body: "{\"key\": \"value\"}")
// 列出函数执行记录
let executions = try await functions.listExecutions(functionId: "[FUNCTION_ID]")Writing a Function Handler (Swift runtime)
Swift 运行时函数处理器编写
swift
// Sources/main.swift — Appwrite Function entry point
func 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
}swift
// Sources/main.swift — Appwrite 函数入口
func main(context: RuntimeContext) async throws -> RuntimeOutput {
// context.req.body — 原始请求体(字符串)
// context.req.bodyJson — 解析后的 JSON ([String: Any]?)
// context.req.headers — 请求头 ([String: String])
// context.req.method — HTTP 请求方法
// context.req.path — URL 路径
// context.req.query — 查询参数 ([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") // 返回纯文本
// context.res.empty() // 返回 204 空响应
// context.res.redirect("https://...") // 重定向
}Server-Side Rendering (SSR) Authentication
服务端渲染(SSR)认证
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)
}使用服务端 Swift(Vapor、Hummingbird 等)的 SSR 应用需使用服务端 SDK处理认证,需要两个客户端:
- 管理员客户端:使用 API 密钥,可创建会话、绕过请求频率限制(可复用的单例)
- 会话客户端:使用会话 Cookie,代表用户执行操作(每个请求创建一个,不可共享)
swift
import Appwrite
// 管理员客户端(可复用)
let adminClient = Client()
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1")
.setProject("[PROJECT_ID]")
.setKey(Environment.get("APPWRITE_API_KEY")!)
// 会话客户端(每个请求创建)
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)
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
}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 名称必须为 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
}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()
// 返回用户信息 JSON
}OAuth2 SSR Flow
SSR 流程中的 OAuth2 登录
swift
// Step 1: Redirect to OAuth provider
app.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 session
app.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, andisSecureto prevent XSS. The cookie name must besameSite: .strict.a_session_<PROJECT_ID>
Forwarding user agent: Callto record the end-user's browser info for debugging and security.sessionClient.setForwardedUserAgent(req.headers.first(name: .userAgent) ?? "")
swift
// 步骤1:重定向到 OAuth 提供商
app.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)
}
// 步骤2:处理回调 — 交换令牌获取会话
app.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 安全注意事项: 始终启用、isHTTPOnly和isSecure以防止 XSS 攻击。Cookie 名称必须为sameSite: .strict。a_session_<PROJECT_ID>
转发用户代理: 调用可记录终端用户的浏览器信息,用于调试和安全审计。sessionClient.setForwardedUserAgent(req.headers.first(name: .userAgent) ?? "")
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:
| Code | Meaning |
|---|---|
| Unauthorized — missing or invalid session/API key |
| Forbidden — insufficient permissions |
| Not found — resource does not exist |
| Conflict — duplicate ID or unique constraint |
| Rate limited — too many requests |
swift
import Appwrite
// AppwriteException 已包含在主模块导入中
do {
let row = try await tablesDB.getRow(databaseId: "[DATABASE_ID]", tableId: "[TABLE_ID]", rowId: "[ROW_ID]")
} catch let error as AppwriteException {
print(error.message) // 易读错误信息
print(error.code) // HTTP 状态码(整数)
print(error.type) // 错误类型(例如 "document_not_found")
print(error.response) // 完整响应体
}常见错误码:
| 状态码 | 含义 |
|---|---|
| 未授权 — 会话/API密钥缺失或无效 |
| 禁止访问 — 权限不足 |
| 未找到 — 资源不存在 |
| 冲突 — 重复ID或违反唯一约束 |
| 请求超限 — 请求频率过高 |
Permissions & Roles (Critical)
权限与角色(重点)
Appwrite uses permission strings to control access to resources. Each permission pairs an action (, , , , or which grants create + update + delete) with a role target. By default, no user has access unless permissions are explicitly set at the document/file level or inherited from the collection/bucket settings. Permissions are arrays of strings built with the and helpers.
readupdatedeletecreatewritePermissionRoleswift
import Appwrite
// Permission and Role are included in the main module importAppwrite 使用权限字符串控制资源访问权限。每个权限由操作(、、、,或包含创建、更新、删除的 )和角色目标组成。默认情况下,所有用户均无访问权限,除非在文档/文件级别明确设置权限,或从集合/存储桶设置中继承权限。权限是由 和 工具类构建的字符串数组。
readupdatedeletecreatewritePermissionRoleswift
import Appwrite
// Permission 和 Role 已包含在主模块导入中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
]
)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]")), // 特定用户可读取
Permission.update(Role.user("[USER_ID]")), // 特定用户可更新
Permission.read(Role.team("[TEAM_ID]")), // 所有团队成员可读取
Permission.read(Role.any()), // 任何人(包括访客)可读取
]
)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 document/file-level permissions when you need per-resource access control. If all documents in a collection share the same rules, configure permissions at the collection/bucket level and leave document permissions empty.
Common mistakes:
- Forgetting permissions — the resource becomes inaccessible to all users (including the creator)
withRole.any()/write/update— allows any user, including unauthenticated guests, to modify or remove the resourcedelete on sensitive data — makes the resource publicly readablePermission.read(Role.any())
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]")),
]
)权限设置时机: 当需要针对单个资源进行访问控制时,在文档/文件级别设置权限。如果集合中的所有文档共享相同的权限规则,可在集合/存储桶级别配置权限,文档级别留空即可。
常见错误:
- 忘记设置权限 — 资源对所有用户(包括创建者)不可访问
- 对
/write/update使用delete— 允许任何用户(包括未认证访客)修改或删除资源Role.any()- 对敏感数据使用
— 导致资源可公开读取Permission.read(Role.any())