Skip to content

feat: Apple SDK update for version 18.0.0#118

Merged
ArnabChatterjee20k merged 3 commits into
mainfrom
dev
Jun 1, 2026
Merged

feat: Apple SDK update for version 18.0.0#118
ArnabChatterjee20k merged 3 commits into
mainfrom
dev

Conversation

@ArnabChatterjee20k

@ArnabChatterjee20k ArnabChatterjee20k commented Jun 1, 2026

Copy link
Copy Markdown
Member

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

@greptile-apps

greptile-apps Bot commented Jun 1, 2026

Copy link
Copy Markdown

Greptile Summary

This PR updates the Apple SDK to version 18.0.0, replacing the Theme enum with BrowserTheme, making Presence and PresenceList concrete (non-generic) types with an optional metadata field, and simplifying the data-extraction logic in Document.from(map:) and Row.from(map:).

  • BrowserTheme replaces Theme and Presences service methods drop their generic nestedType overloads — both changes are clean breaking-change upgrades.
  • The simplified one-liner in Document.from(map:) and Row.from(map:) introduces a regression: when map["data"] is present but is not a [String: Any] (e.g., an array), the as? [String: Any] cast silently returns nil and the entire parent map is passed to JSONDecoder instead of the actual data payload — silently decoding wrong fields or crashing via try!.

Confidence Score: 4/5

Safe to merge once the data-extraction fallback in Document and Row is fixed; all other changes are straightforward.

The simplification of from(map:) in both Document.swift and Row.swift drops the JSONSerialization.isValidJSONObject branch. When the server returns a non-dictionary data value (e.g., an array), the entire parent document map is fed to JSONDecoder as the data payload, producing silently wrong decoded values or a try! crash.

Sources/AppwriteModels/Document.swift and Sources/AppwriteModels/Row.swift — both have the same data-extraction fallback regression.

Important Files Changed

Filename Overview
Sources/AppwriteModels/Document.swift Simplified from(map:) data extraction loses the non-dict JSON branch, causing the whole parent map to be used as fallback instead of the actual data value when it is not a dictionary.
Sources/AppwriteModels/Row.swift Same data-extraction regression as Document.swift — map["data"] as? [String: Any] ?? map silently discards non-dict data payloads in favour of the full parent map.
Sources/AppwriteModels/Presence.swift Converted from generic Presence<T> to concrete class with optional [String: AnyCodable] metadata; init(from decoder:) correctly uses decodeIfPresent, though from(map:) always produces a non-nil metadata value.
Sources/AppwriteModels/PresenceList.swift Generic type parameter removed cleanly; uses concrete [Presence] throughout.
Sources/Appwrite/Services/Presences.swift Generic overloads removed; all service methods simplified to return concrete Presence/PresenceList types.
Sources/AppwriteEnums/BrowserTheme.swift Renamed from Theme to BrowserTheme; enum cases and conformances unchanged.
Sources/Appwrite/Services/Avatars.swift theme parameter type updated from AppwriteEnums.Theme to AppwriteEnums.BrowserTheme; no other changes.
Sources/Appwrite/Client.swift SDK version bumped from 17.2.0 to 18.0.0.

Reviews (2): Last reviewed commit: "chore: update Apple SDK to 18.0.0" | Re-trigger Greptile

return try! JSONSerialization.data(withJSONObject: [:] as [String: Any], options: [])
}
}())
metadata: (map["metadata"] as? [String: Any] ?? [:]).mapValues { AnyCodable($0) }

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 When map["metadata"] is absent or nil, the ?? [:] fallback converts it to an empty dictionary, so metadata is always non-nil on the from(map:) path. The init(from decoder:) path uses decodeIfPresent, which correctly returns nil for a missing key. Any caller checking presence.metadata != nil (or using metadata in an if let / guard let) will always see a truthy value when the object was built via from(map:), even when the server returned no metadata.

Suggested change
metadata: (map["metadata"] as? [String: Any] ?? [:]).mapValues { AnyCodable($0) }
metadata: (map["metadata"] as? [String: Any]).map { $0.mapValues { AnyCodable($0) } }

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

return try! JSONSerialization.data(withJSONObject: [:] as [String: Any], options: [])
}
}())
data: try! JSONDecoder().decode(T.self, from: JSONSerialization.data(withJSONObject: map["data"] as? [String: Any] ?? map, options: []))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 The new fallback map["data"] as? [String: Any] ?? map silently substitutes the entire parent map whenever map["data"] exists but is not a dictionary (e.g., a JSON array). In the previous code, JSONSerialization.isValidJSONObject(raw) handled that branch correctly and would still serialize the actual data value. Now, if a server ever returns an array-typed data, the entire document map (including $id, $collectionId, etc.) is passed to JSONDecoder instead — either producing a wrong T silently or crashing the try!.

Suggested change
data: try! JSONDecoder().decode(T.self, from: JSONSerialization.data(withJSONObject: map["data"] as? [String: Any] ?? map, options: []))
data: try! JSONDecoder().decode(T.self, from: {
let raw = map["data"]
if let dict = raw as? [String: Any] {
return try! JSONSerialization.data(withJSONObject: dict, options: [])
} else if let raw = raw, JSONSerialization.isValidJSONObject(raw) {
return try! JSONSerialization.data(withJSONObject: raw, options: [])
} else {
return try! JSONSerialization.data(withJSONObject: [:] as [String: Any], options: [])
}
}())

return try! JSONSerialization.data(withJSONObject: [:] as [String: Any], options: [])
}
}())
data: try! JSONDecoder().decode(T.self, from: JSONSerialization.data(withJSONObject: map["data"] as? [String: Any] ?? map, options: []))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Same regression as Document.swift: the ?? map fallback is reached both when data is absent and when data is present but is not a [String: Any] (e.g., an array). In the latter case the entire row map is passed to JSONDecoder instead of the actual data payload, silently decoding wrong data or crashing. The previous implementation correctly handled non-dict JSON via JSONSerialization.isValidJSONObject.

Suggested change
data: try! JSONDecoder().decode(T.self, from: JSONSerialization.data(withJSONObject: map["data"] as? [String: Any] ?? map, options: []))
data: try! JSONDecoder().decode(T.self, from: {
let raw = map["data"]
if let dict = raw as? [String: Any] {
return try! JSONSerialization.data(withJSONObject: dict, options: [])
} else if let raw = raw, JSONSerialization.isValidJSONObject(raw) {
return try! JSONSerialization.data(withJSONObject: raw, options: [])
} else {
return try! JSONSerialization.data(withJSONObject: [:] as [String: Any], options: [])
}
}())

@ArnabChatterjee20k ArnabChatterjee20k merged commit c7db5ab into main Jun 1, 2026
1 check 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.

3 participants