feat: Apple SDK update for version 17.1.0#111
Conversation
Greptile SummaryThis PR updates the Apple SDK to version 17.1.0, adding
Confidence Score: 3/5The Realtime reconnect path has a silent failure mode that can leave all subscriptions undelivered after a disconnect. The Sources/Appwrite/Services/Realtime.swift — specifically Important Files Changed
Reviews (1): Last reviewed commit: "chore: update Apple SDK to 17.1.0" | Re-trigger Greptile |
| private func handleResponseConnected(from json: [String: Any]) { | ||
| guard let data = json["data"] as? [String: Any], | ||
| let subscriptions = data["subscriptions"] as? [String: String] else { | ||
| guard json["data"] is [String: Any] else { | ||
| return | ||
| } | ||
|
|
||
| // Store subscription ID mappings from backend | ||
| // Format: { "0": "sub_a1f9", "1": "sub_b83c", ... } | ||
| slotToSubscriptionId.removeAll() | ||
| subscriptionIdToSlot.removeAll() | ||
| for (slotStr, subscriptionId) in subscriptions { | ||
| if let slot = Int(slotStr) { | ||
| slotToSubscriptionId[slot] = subscriptionId | ||
| subscriptionIdToSlot[subscriptionId] = slot | ||
| } | ||
|
|
||
| for subscriptionId in activeSubscriptions.keys { | ||
| enqueuePendingSubscribe(subscriptionId: subscriptionId) | ||
| } | ||
| sendPendingSubscribes() | ||
| } |
There was a problem hiding this comment.
Guard condition silently blocks reconnect re-subscription
The guard json["data"] is [String: Any] was carried over from the old protocol, but the new implementation never reads the content of data — it's only checking the type. If the server ever sends a connected message where data is absent, null, or any non-dictionary type (e.g., a protocol version bump), the guard exits early and sendPendingSubscribes() is never called. All active subscriptions would then be silently lost for the lifetime of that reconnected socket — callers continue running with no events delivered and no error surfaced.
| private func generateUniqueSubscriptionId() throws -> String { | ||
| let attempts = activeSubscriptions.count + 1 | ||
| for _ in 0..<attempts { |
There was a problem hiding this comment.
generateUniqueSubscriptionId loop bound is unexpectedly tight
The loop runs at most activeSubscriptions.count + 1 times. With 0 active subscriptions that's a single attempt; with any collision (however improbable with UUIDs) the function throws and the subscribe call propagates an error to the caller. The bound should be at least a fixed minimum (e.g., 10) so a freshly-created Realtime instance always has more than one chance.
| private func generateUniqueSubscriptionId() throws -> String { | |
| let attempts = activeSubscriptions.count + 1 | |
| for _ in 0..<attempts { | |
| private func generateUniqueSubscriptionId() throws -> String { | |
| let attempts = max(activeSubscriptions.count + 1, 10) | |
| for _ in 0..<attempts { |
| userId: map["userId"] as! String, | ||
| userName: map["userName"] as! String, | ||
| userEmail: map["userEmail"] as! String, | ||
| userPhone: map["userPhone"] as! String, |
There was a problem hiding this comment.
Non-optional
userPhone force-cast may crash when privacy is toggled
The field is documented as "Hide this attribute by toggling membership privacy in the Console." If the server omits or returns null for userPhone when privacy is enabled, map["userPhone"] as! String will produce a runtime crash. The existing userEmail field follows the same pattern, but adding another non-optional force-cast field that can be hidden amplifies the risk. Consider decoding as String? (or at least using as? String ?? "") and making the property optional to match the server's contract when privacy is on.
|
Closing — changes not substantial enough to warrant a release. |
This PR contains updates to the SDK for version 17.1.0.
What's Changed
setCookie()method toClientfor forwarding incomingCookieheaders in server-side runtimessetCompression()method toClientto toggle automatic response decompressionfusionauth,keycloak, andkickOAuth providers toOAuthProviderenumX-Appwrite-Response-Formatheader to1.9.4