Skip to content

Conversation

@abnegate
Copy link
Member

@abnegate abnegate commented Oct 9, 2025

This PR contains updates to the Apple SDK for version 13.2.0.

Summary by CodeRabbit

  • New Features
    • Added transaction support for Databases and TablesDB: create, list, get, update (commit/rollback), delete, and batch operations.
    • Introduced optional transactionId across document/row CRUD and increment/decrement operations.
    • Added Transaction and TransactionList models.
  • Documentation
    • New examples for transaction lifecycle and operations; updated CRUD examples to include transactionId.
    • Updated README to reference version 13.2.0.
    • Added examples for both Databases and TablesDB transaction workflows.
  • Chores
    • Bumped SDK version to 13.2.0 and updated default headers.
    • Updated CHANGELOG with transaction support entry.

@coderabbitai
Copy link

coderabbitai bot commented Oct 9, 2025

Walkthrough

This PR updates the SDK version to 13.2.0 and adds transaction support across Databases and TablesDB. It introduces new service methods for listing, creating, getting, updating, deleting transactions, and creating operations within a transaction. Many CRUD methods now accept an optional transactionId and pass it in API parameters. Two new public models are added: Transaction and TransactionList. Documentation examples are updated to include transaction usage across Databases and TablesDB. README and CHANGELOG are updated to reflect the new version and feature.

Possibly related PRs

Suggested reviewers

  • ItzNotABug

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title states the Apple SDK bump to version 13.2.0, which directly corresponds to the version changes across code, docs, and changelog, and is concise and clear. It avoids extraneous details and focuses on the primary change from a versioning perspective. Although it does not call out the new transaction support feature explicitly, it accurately reflects the significant version update.
Docstring Coverage ✅ Passed Docstring coverage is 87.50% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dev

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (4)
Sources/AppwriteModels/TransactionList.swift (2)

7-10: Consider removing redundant string enum values.

The CodingKeys enum has string values that match the case names, which can be omitted for cleaner code.

Apply this diff:

     enum CodingKeys: String, CodingKey {
-        case total = "total"
-        case transactions = "transactions"
+        case total
+        case transactions
     }

19-25: Make the initializer public for external instantiation.

The initializer is internal, which prevents external code from creating TransactionList instances directly. If this model is intended to be part of the public API, the initializer should be public.

Apply this diff if external instantiation is needed:

-    init(
+    public init(
         total: Int,
         transactions: [Transaction]
     ) {
Sources/AppwriteModels/Transaction.swift (2)

25-26: Consider using an enum for transaction status.

The documentation indicates that status has a fixed set of values: "pending, committing, committed, rolled_back, failed". Using a String type allows invalid values and requires string comparisons.

Consider defining a status enum for type safety:

public enum TransactionStatus: String, Codable {
    case pending
    case committing
    case committed
    case rolledBack = "rolled_back"
    case failed
}

Then update the property:

-    /// Current status of the transaction. One of: pending, committing, committed, rolled_back, failed.
-    public let status: String
+    /// Current status of the transaction.
+    public let status: TransactionStatus

This provides compile-time validation and better IDE support.


35-49: Make the initializer public for external instantiation.

The initializer is internal, which prevents external code from creating Transaction instances directly. If this model is intended to be part of the public API, the initializer should be public.

Apply this diff if external instantiation is needed:

-    init(
+    public init(
         id: String,
         createdAt: String,
         updatedAt: String,
         status: String,
         operations: Int,
         expiresAt: String
     ) {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 907ac8a and d61ce34.

📒 Files selected for processing (35)
  • CHANGELOG.md (1 hunks)
  • README.md (1 hunks)
  • Sources/Appwrite/Client.swift (1 hunks)
  • Sources/Appwrite/Services/Databases.swift (37 hunks)
  • Sources/Appwrite/Services/TablesDb.swift (35 hunks)
  • Sources/AppwriteModels/Transaction.swift (1 hunks)
  • Sources/AppwriteModels/TransactionList.swift (1 hunks)
  • docs/examples/databases/create-document.md (1 hunks)
  • docs/examples/databases/create-operations.md (1 hunks)
  • docs/examples/databases/create-transaction.md (1 hunks)
  • docs/examples/databases/decrement-document-attribute.md (1 hunks)
  • docs/examples/databases/delete-document.md (1 hunks)
  • docs/examples/databases/delete-transaction.md (1 hunks)
  • docs/examples/databases/get-document.md (1 hunks)
  • docs/examples/databases/get-transaction.md (1 hunks)
  • docs/examples/databases/increment-document-attribute.md (1 hunks)
  • docs/examples/databases/list-documents.md (1 hunks)
  • docs/examples/databases/list-transactions.md (1 hunks)
  • docs/examples/databases/update-document.md (1 hunks)
  • docs/examples/databases/update-transaction.md (1 hunks)
  • docs/examples/databases/upsert-document.md (1 hunks)
  • docs/examples/tablesdb/create-operations.md (1 hunks)
  • docs/examples/tablesdb/create-row.md (1 hunks)
  • docs/examples/tablesdb/create-transaction.md (1 hunks)
  • docs/examples/tablesdb/decrement-row-column.md (1 hunks)
  • docs/examples/tablesdb/delete-row.md (1 hunks)
  • docs/examples/tablesdb/delete-transaction.md (1 hunks)
  • docs/examples/tablesdb/get-row.md (1 hunks)
  • docs/examples/tablesdb/get-transaction.md (1 hunks)
  • docs/examples/tablesdb/increment-row-column.md (1 hunks)
  • docs/examples/tablesdb/list-rows.md (1 hunks)
  • docs/examples/tablesdb/list-transactions.md (1 hunks)
  • docs/examples/tablesdb/update-row.md (1 hunks)
  • docs/examples/tablesdb/update-transaction.md (1 hunks)
  • docs/examples/tablesdb/upsert-row.md (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
Sources/AppwriteModels/TransactionList.swift (1)
Sources/AppwriteModels/Transaction.swift (3)
  • from (84-93)
  • encode (62-71)
  • toMap (73-82)
Sources/Appwrite/Services/Databases.swift (2)
Sources/Appwrite/Services/TablesDb.swift (6)
  • listTransactions (19-41)
  • createTransaction (51-75)
  • getTransaction (85-106)
  • updateTransaction (118-146)
  • deleteTransaction (156-173)
  • createOperations (184-210)
Sources/Appwrite/Client.swift (1)
  • call (301-332)
Sources/AppwriteModels/Transaction.swift (1)
Sources/AppwriteModels/TransactionList.swift (3)
  • from (48-53)
  • encode (34-39)
  • toMap (41-46)
Sources/Appwrite/Services/TablesDb.swift (2)
Sources/Appwrite/Services/Databases.swift (6)
  • listTransactions (19-41)
  • createTransaction (51-75)
  • getTransaction (85-106)
  • updateTransaction (118-146)
  • deleteTransaction (156-173)
  • createOperations (184-210)
Sources/Appwrite/Client.swift (1)
  • call (301-332)
🪛 markdownlint-cli2 (0.18.1)
docs/examples/databases/create-operations.md

12-12: Hard tabs
Column: 1

(MD010, no-hard-tabs)


13-13: Hard tabs
Column: 1

(MD010, no-hard-tabs)


14-14: Hard tabs
Column: 1

(MD010, no-hard-tabs)


15-15: Hard tabs
Column: 1

(MD010, no-hard-tabs)


16-16: Hard tabs
Column: 1

(MD010, no-hard-tabs)


17-17: Hard tabs
Column: 1

(MD010, no-hard-tabs)


18-18: Hard tabs
Column: 1

(MD010, no-hard-tabs)


19-19: Hard tabs
Column: 1

(MD010, no-hard-tabs)


20-20: Hard tabs
Column: 1

(MD010, no-hard-tabs)


21-21: Hard tabs
Column: 1

(MD010, no-hard-tabs)

🪛 SwiftLint (0.57.0)
Sources/AppwriteModels/TransactionList.swift

[Error] 50-50: Force casts should be avoided

(force_cast)


[Error] 51-51: Force casts should be avoided

(force_cast)


[Warning] 8-8: String enum values can be omitted when they are equal to the enumcase name

(redundant_string_enum_value)


[Warning] 9-9: String enum values can be omitted when they are equal to the enumcase name

(redundant_string_enum_value)

Sources/Appwrite/Services/Databases.swift

[Error] 31-31: Force casts should be avoided

(force_cast)


[Error] 65-65: Force casts should be avoided

(force_cast)


[Error] 96-96: Force casts should be avoided

(force_cast)


[Error] 136-136: Force casts should be avoided

(force_cast)


[Error] 200-200: Force casts should be avoided

(force_cast)

Sources/AppwriteModels/Transaction.swift

[Error] 86-86: Force casts should be avoided

(force_cast)


[Error] 87-87: Force casts should be avoided

(force_cast)


[Error] 88-88: Force casts should be avoided

(force_cast)


[Error] 89-89: Force casts should be avoided

(force_cast)


[Error] 90-90: Force casts should be avoided

(force_cast)


[Error] 91-91: Force casts should be avoided

(force_cast)

Sources/Appwrite/Services/TablesDb.swift

[Error] 31-31: Force casts should be avoided

(force_cast)


[Error] 65-65: Force casts should be avoided

(force_cast)


[Error] 96-96: Force casts should be avoided

(force_cast)


[Error] 136-136: Force casts should be avoided

(force_cast)


[Error] 200-200: Force casts should be avoided

(force_cast)

🔇 Additional comments (14)
docs/examples/tablesdb/decrement-row-column.md (1)

15-16: Docs accurately show the new optional transactionId.

Example stays consistent with the updated SDK signature and makes the new transactional capability clear.

docs/examples/databases/create-transaction.md (1)

1-12: LGTM!

The documentation example correctly demonstrates creating a transaction with an optional TTL parameter. The code structure follows the standard pattern and clearly shows the new transaction API usage.

docs/examples/tablesdb/get-transaction.md (1)

1-12: LGTM!

The documentation example correctly demonstrates retrieving a transaction by ID. The code is clear and follows the established pattern for TablesDB operations.

docs/examples/databases/update-document.md (1)

14-15: LGTM!

The addition of the optional transactionId parameter is correctly demonstrated. The parameter placement and syntax are appropriate.

docs/examples/databases/get-document.md (1)

13-14: LGTM!

The addition of the optional transactionId parameter is correctly demonstrated and follows the established pattern for optional parameters.

docs/examples/tablesdb/delete-row.md (1)

12-13: LGTM!

The addition of the optional transactionId parameter for TablesDB operations is correctly demonstrated and consistent with the Databases API examples.

README.md (1)

34-35: Version bump aligns with 13.2.0 release

The SwiftPM example now matches the advertised 13.2.0 SDK release.

docs/examples/databases/get-transaction.md (1)

1-11: Sample reflects new getTransaction API

Initialization flow and call signature mirror the added Databases.getTransaction endpoint.

docs/examples/databases/delete-transaction.md (1)

1-11: Delete transaction example matches API

The snippet demonstrates the new deleteTransaction call exactly as exposed in the SDK.

docs/examples/tablesdb/increment-row-column.md (1)

15-17: Increment example covers new optional arguments

Including max and transactionId mirrors the expanded TablesDB.incrementRowColumn signature.

CHANGELOG.md (1)

3-6: Changelog entry is on point

The 13.2.0 note accurately captures the Databases/TablesDB transaction feature.

docs/examples/databases/delete-document.md (1)

12-14: Document deletion snippet reflects transactional option

Passing transactionId illustrates the new optional parameter on deleteDocument.

docs/examples/tablesdb/get-row.md (1)

13-15: Get row example showcases optional parameters

The snippet correctly highlights both queries and transactionId additions to getRow.

docs/examples/databases/list-transactions.md (1)

1-11: List transactions sample aligns with new API

The example mirrors the Databases.listTransactions method and illustrates optional queries usage.

Comment on lines +20 to 22
permissions: ["read("any")"], // optional
transactionId: "<TRANSACTION_ID>" // optional
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix the invalid permissions string literal

["read("any")"] will not compile in Swift because the inner quotes terminate the string. Either escape the quotes ("read(\"any\")") or, better, use the official helpers (e.g. Permission.read(Role.any())) so the example builds cleanly.

🤖 Prompt for AI Agents
In docs/examples/databases/create-document.md around lines 20 to 22, the
permissions literal `["read("any")"]` is invalid Swift (inner quotes terminate
the string); replace it with a valid expression — either escape the inner quotes
(`"read(\"any\")"`) or, preferably, use the official helpers (e.g.
Permission.read(Role.any())) so the example compiles cleanly; keep transactionId
as-is since it’s optional.

Comment on lines +9 to +22
let transaction = try await databases.createOperations(
transactionId: "<TRANSACTION_ID>",
operations: [
{
"action": "create",
"databaseId": "<DATABASE_ID>",
"collectionId": "<COLLECTION_ID>",
"documentId": "<DOCUMENT_ID>",
"data": {
"name": "Walter O'Brien"
}
}
] // optional
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix the syntax error in the operations array.

The operations parameter uses JSON-like syntax with quoted keys and hard tabs, which is not valid Swift code. Swift requires proper dictionary and array literals with unquoted keys.

Apply this diff to fix the syntax:

 let transaction = try await databases.createOperations(
     transactionId: "<TRANSACTION_ID>",
     operations: [
-	    {
-	        "action": "create",
-	        "databaseId": "<DATABASE_ID>",
-	        "collectionId": "<COLLECTION_ID>",
-	        "documentId": "<DOCUMENT_ID>",
-	        "data": {
-	            "name": "Walter O'Brien"
-	        }
-	    }
-	] // optional
+        [
+            "action": "create",
+            "databaseId": "<DATABASE_ID>",
+            "collectionId": "<COLLECTION_ID>",
+            "documentId": "<DOCUMENT_ID>",
+            "data": [
+                "name": "Walter O'Brien"
+            ]
+        ]
+    ] // optional
 )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let transaction = try await databases.createOperations(
transactionId: "<TRANSACTION_ID>",
operations: [
{
"action": "create",
"databaseId": "<DATABASE_ID>",
"collectionId": "<COLLECTION_ID>",
"documentId": "<DOCUMENT_ID>",
"data": {
"name": "Walter O'Brien"
}
}
] // optional
)
let transaction = try await databases.createOperations(
transactionId: "<TRANSACTION_ID>",
operations: [
[
"action": "create",
"databaseId": "<DATABASE_ID>",
"collectionId": "<COLLECTION_ID>",
"documentId": "<DOCUMENT_ID>",
"data": [
"name": "Walter O'Brien"
]
]
] // optional
)
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

12-12: Hard tabs
Column: 1

(MD010, no-hard-tabs)


13-13: Hard tabs
Column: 1

(MD010, no-hard-tabs)


14-14: Hard tabs
Column: 1

(MD010, no-hard-tabs)


15-15: Hard tabs
Column: 1

(MD010, no-hard-tabs)


16-16: Hard tabs
Column: 1

(MD010, no-hard-tabs)


17-17: Hard tabs
Column: 1

(MD010, no-hard-tabs)


18-18: Hard tabs
Column: 1

(MD010, no-hard-tabs)


19-19: Hard tabs
Column: 1

(MD010, no-hard-tabs)


20-20: Hard tabs
Column: 1

(MD010, no-hard-tabs)


21-21: Hard tabs
Column: 1

(MD010, no-hard-tabs)

🤖 Prompt for AI Agents
In docs/examples/databases/create-operations.md around lines 9 to 22, the
operations array uses JSON-like syntax (quoted keys with braces and tabs) which
is invalid Swift; replace the JSON-style element with a proper Swift array of
dictionaries using Swift dictionary literals (e.g. ["action": "create",
"databaseId": "...", "collectionId": "...", "documentId": "...", "data":
["name": "..."]]) so the operations parameter is an Array of [String: Any]
dictionaries, remove hard tabs and ensure proper Swift punctuation and quoting
for string keys and nested dictionaries.

Comment on lines +14 to 16
permissions: ["read("any")"], // optional
transactionId: "<TRANSACTION_ID>" // optional
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Correct the permissions example

["read("any")"] is an invalid Swift literal (unescaped quotes). Please escape the quotes or switch to the idiomatic permissions builder so developers can copy-paste the snippet without compilation errors.

🤖 Prompt for AI Agents
docs/examples/databases/upsert-document.md around lines 14 to 16: the
permissions example uses unescaped inner quotes which is not valid Swift; fix it
by either escaping the inner double quotes with backslashes in the string
literal, or replace the string literal with the idiomatic permissions builder
form (use the read permission builder with the any parameter inside the
permissions array) so the snippet compiles and is copy-paste ready.

Comment on lines +9 to +22
let transaction = try await tablesDB.createOperations(
transactionId: "<TRANSACTION_ID>",
operations: [
{
"action": "create",
"databaseId": "<DATABASE_ID>",
"tableId": "<TABLE_ID>",
"rowId": "<ROW_ID>",
"data": {
"name": "Walter O'Brien"
}
}
] // optional
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix invalid Swift literal in operations example.

Within the Swift code block, dictionaries must be written with square brackets, e.g. ["key": "value"]. Using braces ({ ... }) yields a compile error for anyone copying this snippet. Please replace the braces with bracketed dictionary literals so the example builds.

🤖 Prompt for AI Agents
In docs/examples/tablesdb/create-operations.md around lines 9 to 22, the Swift
example uses JSON-style braces for dictionaries inside the operations array
(e.g. { "action": "create", ... }) which is invalid Swift; replace each
brace-enclosed object with Swift dictionary literal syntax using square brackets
and colon-separated keys/values (e.g. ["action": "create", ...]) so the snippet
compiles when copied.

Comment on lines +84 to +93
public static func from(map: [String: Any] ) -> Transaction {
return Transaction(
id: map["$id"] as! String,
createdAt: map["$createdAt"] as! String,
updatedAt: map["$updatedAt"] as! String,
status: map["status"] as! String,
operations: map["operations"] as! Int,
expiresAt: map["expiresAt"] as! String
)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Replace force casts with safe alternatives to prevent crashes.

The from(map:) method uses force casts (as!) on all properties that will crash at runtime if the map has missing keys or wrong types. This is particularly problematic when deserializing data from API responses.

Apply this diff to use safe casting with proper error handling:

 public static func from(map: [String: Any] ) -> Transaction {
-        return Transaction(
-            id: map["$id"] as! String,
-            createdAt: map["$createdAt"] as! String,
-            updatedAt: map["$updatedAt"] as! String,
-            status: map["status"] as! String,
-            operations: map["operations"] as! Int,
-            expiresAt: map["expiresAt"] as! String
-        )
+        guard let id = map["$id"] as? String else {
+            fatalError("Transaction.from: '$id' is missing or not a String")
+        }
+        guard let createdAt = map["$createdAt"] as? String else {
+            fatalError("Transaction.from: '$createdAt' is missing or not a String")
+        }
+        guard let updatedAt = map["$updatedAt"] as? String else {
+            fatalError("Transaction.from: '$updatedAt' is missing or not a String")
+        }
+        guard let status = map["status"] as? String else {
+            fatalError("Transaction.from: 'status' is missing or not a String")
+        }
+        guard let operations = map["operations"] as? Int else {
+            fatalError("Transaction.from: 'operations' is missing or not an Int")
+        }
+        guard let expiresAt = map["expiresAt"] as? String else {
+            fatalError("Transaction.from: 'expiresAt' is missing or not a String")
+        }
+        return Transaction(
+            id: id,
+            createdAt: createdAt,
+            updatedAt: updatedAt,
+            status: status,
+            operations: operations,
+            expiresAt: expiresAt
+        )
     }

Alternatively, consider making this method throwing to allow callers to handle deserialization errors gracefully.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public static func from(map: [String: Any] ) -> Transaction {
return Transaction(
id: map["$id"] as! String,
createdAt: map["$createdAt"] as! String,
updatedAt: map["$updatedAt"] as! String,
status: map["status"] as! String,
operations: map["operations"] as! Int,
expiresAt: map["expiresAt"] as! String
)
}
public static func from(map: [String: Any] ) -> Transaction {
guard let id = map["$id"] as? String else {
fatalError("Transaction.from: '$id' is missing or not a String")
}
guard let createdAt = map["$createdAt"] as? String else {
fatalError("Transaction.from: '$createdAt' is missing or not a String")
}
guard let updatedAt = map["$updatedAt"] as? String else {
fatalError("Transaction.from: '$updatedAt' is missing or not a String")
}
guard let status = map["status"] as? String else {
fatalError("Transaction.from: 'status' is missing or not a String")
}
guard let operations = map["operations"] as? Int else {
fatalError("Transaction.from: 'operations' is missing or not an Int")
}
guard let expiresAt = map["expiresAt"] as? String else {
fatalError("Transaction.from: 'expiresAt' is missing or not a String")
}
return Transaction(
id: id,
createdAt: createdAt,
updatedAt: updatedAt,
status: status,
operations: operations,
expiresAt: expiresAt
)
}
🧰 Tools
🪛 SwiftLint (0.57.0)

[Error] 86-86: Force casts should be avoided

(force_cast)


[Error] 87-87: Force casts should be avoided

(force_cast)


[Error] 88-88: Force casts should be avoided

(force_cast)


[Error] 89-89: Force casts should be avoided

(force_cast)


[Error] 90-90: Force casts should be avoided

(force_cast)


[Error] 91-91: Force casts should be avoided

(force_cast)

🤖 Prompt for AI Agents
In Sources/AppwriteModels/Transaction.swift around lines 84 to 93, the
from(map:) implementation uses force casts (as!) which will crash on missing or
wrong-typed values; replace them with safe casts and explicit error handling by
either: 1) making the method throwing (e.g., from(map:) throws -> Transaction)
and using guard-let statements to safely cast each required field (id,
createdAt, updatedAt, status, operations, expiresAt), converting numeric types
(NSNumber) to Int where necessary and throwing a descriptive deserialization
error if any guard fails; or 2) change the signature to return an optional (->
Transaction?) and use guard-let safe casts to return nil on failure; ensure
error messages identify the missing/invalid key and keep types exact (String for
date fields, Int for operations) before constructing and returning the
Transaction.

Comment on lines +48 to +53
public static func from(map: [String: Any] ) -> TransactionList {
return TransactionList(
total: map["total"] as! Int,
transactions: (map["transactions"] as! [[String: Any]]).map { Transaction.from(map: $0) }
)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Replace force casts with safe alternatives to prevent crashes.

The from(map:) method uses force casts (as!) that will crash at runtime if the map has missing keys, wrong types, or malformed structure. This is particularly risky when deserializing data from external sources.

Apply this diff to use safe casting with proper error handling:

 public static func from(map: [String: Any] ) -> TransactionList {
-        return TransactionList(
-            total: map["total"] as! Int,
-            transactions: (map["transactions"] as! [[String: Any]]).map { Transaction.from(map: $0) }
-        )
+        guard let total = map["total"] as? Int else {
+            fatalError("TransactionList.from: 'total' is missing or not an Int")
+        }
+        guard let transactionsArray = map["transactions"] as? [[String: Any]] else {
+            fatalError("TransactionList.from: 'transactions' is missing or not an array")
+        }
+        return TransactionList(
+            total: total,
+            transactions: transactionsArray.map { Transaction.from(map: $0) }
+        )
     }

Alternatively, consider making this method throwing to allow callers to handle deserialization errors gracefully.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public static func from(map: [String: Any] ) -> TransactionList {
return TransactionList(
total: map["total"] as! Int,
transactions: (map["transactions"] as! [[String: Any]]).map { Transaction.from(map: $0) }
)
}
public static func from(map: [String: Any] ) -> TransactionList {
guard let total = map["total"] as? Int else {
fatalError("TransactionList.from: 'total' is missing or not an Int")
}
guard let transactionsArray = map["transactions"] as? [[String: Any]] else {
fatalError("TransactionList.from: 'transactions' is missing or not an array")
}
return TransactionList(
total: total,
transactions: transactionsArray.map { Transaction.from(map: $0) }
)
}
🧰 Tools
🪛 SwiftLint (0.57.0)

[Error] 50-50: Force casts should be avoided

(force_cast)


[Error] 51-51: Force casts should be avoided

(force_cast)

🤖 Prompt for AI Agents
In Sources/AppwriteModels/TransactionList.swift around lines 48 to 53, the
from(map:) implementation uses force casts (as!) which will crash for missing or
malformed data; change to safe casting and explicit validation: use guard let /
if let to extract total as Int and transactions as [[String: Any]], map each
transaction via a throwing or failable Transaction deserializer, and either make
from(map:) throwing (propagating parsing errors) or return an optional
TransactionList? if validation fails; ensure you return a clear error or nil
instead of force-casting.

@abnegate abnegate merged commit 86d0b2d into main Oct 9, 2025
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants