feat: Apple SDK update for version 18.1.0#119
Conversation
Greptile SummaryThis PR updates the Apple SDK to version 18.1.0, adding new
Confidence Score: 3/5The PR introduces a runtime decode crash in Membership (non-optional field absent by default) and sends empty-string project headers when setProject is not called — both identified in prior review rounds and still unresolved. The new User email fields and the Functions.swift changes are correctly implemented, and the query-string fix in Client.swift is sound. However, Membership.userAccessedAt is decoded as a required non-optional String even though the server only includes it when a team owner explicitly enables the privacy toggle — every membership response under default settings will throw a DecodingError at decode time. In addition, removing the addHeader call from setProject means any caller that forgets setProject will silently send X-Appwrite-Project: '' on every request rather than receiving a clear configuration error. These two regressions affect core data models and authentication on every request. Sources/AppwriteModels/Membership.swift requires the userAccessedAt field to be made optional; Sources/Appwrite/Client.swift needs attention around the setProject/X-Appwrite-Project header strategy. Important Files Changed
Reviews (5): Last reviewed commit: "chore: update Apple SDK to 18.1.0" | Re-trigger Greptile |
| let apiHeaders: [String: String] = [ | ||
| "content-type": "application/json", | ||
| "accept": "application/json" | ||
| ] |
There was a problem hiding this comment.
approve and reject omit the X-Appwrite-Project header
approve (line 39-42) and reject (line 234-237) pass the project ID only as a hardcoded URL query parameter (?project=...), while createGrant and getGrant send it via the X-Appwrite-Project header instead. If the consent-screen endpoints require both, or if the project interpolation in the URL resolves to an empty string (when client.config["project"] is nil), these calls will silently misidentify the project. If the query-param approach is intentional for these OAuth2 protocol endpoints, a comment explaining the asymmetry would help maintainers.
| open func setProject(_ value: String) -> Client { | ||
| config["project"] = value | ||
| _ = addHeader(key: "X-Appwrite-Project", value: value) | ||
| return self | ||
| } |
There was a problem hiding this comment.
setProject no longer adds the header globally; per-method fallback sends empty string
Previously setProject called addHeader(key: "X-Appwrite-Project", value: value) so the header was injected into every request automatically. That line was removed, and every service method now reads client.config["project"] ?? "". If a caller never invokes setProject, every request will now send X-Appwrite-Project: "" instead of omitting the header entirely. An empty-string project header will reach the server and may produce a misleading 404/401 rather than a clear "project not configured" error. Consider at minimum an assertion or a guard in setProject, or restoring the addHeader call and removing the per-method duplication.
| /// Multi factor authentication status, true if the user has MFA enabled or false otherwise. Hide this attribute by toggling membership privacy in the Console. | ||
| public let mfa: Bool | ||
| /// Most recent access date in ISO 8601 format. Show this attribute by toggling membership privacy in the Console. | ||
| public let userAccessedAt: String |
There was a problem hiding this comment.
userAccessedAt decoded as non-optional, crashes when field is absent
The doc comment says "Show this attribute by toggling membership privacy in the Console" — unlike mfa, userName, etc., which say "Hide" (opt-out, present by default), userAccessedAt is opt-in and absent from the response unless the team owner explicitly enables it. Using try container.decode(String.self, forKey: .userAccessedAt) will throw DecodingError.keyNotFound for all memberships where the privacy setting is off (the default). Similarly, map["userAccessedAt"] as! String at line 165 will crash at runtime. This field should be String? with decodeIfPresent and a as? String cast in from(map:), mirroring how the new User email fields are handled.
| return try await client.chunkedUpload( | ||
| path: apiPath, | ||
| headers: apiHeaders, | ||
| params: apiParams, | ||
| converter: converter | ||
| headers: &apiHeaders, | ||
| params: &apiParams, | ||
| paramName: paramName, | ||
| idParamName: idParamName, | ||
| converter: converter, | ||
| onProgress: onProgress | ||
| ) |
There was a problem hiding this comment.
paramName is undefined — compile error
paramName is passed to chunkedUpload at line 107 but is never declared in createExecution's scope. Compare with Storage.createFile, which declares let paramName = "file" immediately before the same call. The missing declaration means this file will not compile. Since createExecution has no actual file parameter (unlike storage uploads), it is unclear whether paramName should be "body" or whether the entire switch from client.call to client.chunkedUpload is intentional — chunkedUpload is designed for streaming large binary files in chunks, while the execution body field is a plain optional String.
This PR contains updates to the Apple SDK for version 18.1.0.