-
Notifications
You must be signed in to change notification settings - Fork 30
[ECO-5634] Messages update, delete and versions #2146
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
WalkthroughThis PR implements the mutable messages write API for the Ably iOS SDK, introducing support for updating, deleting, and retrieving message versions across REST and realtime channels. It adds a new Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
|
I know this one's in draft but I'm going to start taking a look at it now, because next week I'll need to build the Realtime equivalent on top of this. |
You mean |
5086d4a to
b63cc8f
Compare
No, I mean that the |
b63cc8f to
79f6161
Compare
79f6161 to
1fff24c
Compare
My question though is whether this is how all of our existing REST calls work; do we really not have any common helper methods for doing this sort of thing?
I don't think so. We don't have imminent plans for releasing ably-swift (for example, I'm currently not working on it) and it'll be a while before it's at the same level of maturity as ably-cocoa. And we'll be continuing to add new features to ably-cocoa for the foreseeable future. We need to ensure that ably-cocoa remains maintainable. |
Yes, there is a lot of duplication re REST requests in ably-cocoa. There is also common helper method |
Do you think we should have an issue for improving this? |
That is; how much of an implementation burden does the current state of affairs cause? Was it a lot of effort to add these new REST calls? How confident are you that the methods you've implemented are doing all the things that they should be doing? |
The implementation is done by Claude (from the second attempt tho). Architectural part was heavily edited/guided by me.
Well, I've reviewed them, tests here resembles JS tests and they are passing. |
I think yes, auditing all the rest requests and trying to find common places for optimization would be a proper thing to do. |
f3d7f5f to
375059c
Compare
375059c to
2ee6398
Compare
Please can you make an issue for this? I worry about the amount of extra code added here |
2ee6398 to
1ef1dde
Compare
|
1ef1dde to
c6474a3
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (6)
Source/PrivateHeaders/Ably/ARTMessageOperation+Private.h (1)
1-3: Prefer#import <Foundation/Foundation.h>and verifyARTMessageOperation.hvisibilityTo keep this private header friendly to all toolchains (including the static analysis configuration that reported an error), it would be safer and more consistent with typical Ably Cocoa headers to use a standard import:
-@import Foundation; -#import <Ably/ARTMessageOperation.h> -#import "ARTDictionarySerializable.h" +#import <Foundation/Foundation.h> +#import <Ably/ARTMessageOperation.h> +#import "ARTDictionarySerializable.h"Also, given the analyzer warning
'Ably/ARTMessageOperation.h' file not found, please double‑check thatARTMessageOperation.his added to the appropriate header phase and exposed in all targets that include this private header so<Ably/…>resolves correctly.Source/include/Ably/ARTChannelProtocol.h (1)
4-5: New channel message operation APIs look coherent and well-typed
- Importing
ARTStringifiableand forward‑declaringARTMessageOperationare appropriate for the new signatures.updateMessage:…params:callback:anddeleteMessage:…params:callback:cleanly model operation metadata plus optional stringifiable params, while relying on the existingARTCallbackpattern.getMessageWithSerial:callback:andgetMessageVersionsWithSerial:callback:correctly use the newARTMessageErrorCallbackand existingARTPaginatedMessagesCallback, matching their intended behaviors.From a public API perspective, these additions are consistent with the surrounding channel methods and with the callback typedefs defined in
ARTTypes.h.If you plan further polish, consider expanding the docstrings slightly (for example, clarifying what kinds of values are expected in
paramsand howmessage.serialis obtained) to make the new operations self‑explanatory to SDK consumers.Also applies to: 9-10, 73-113
Source/include/Ably/ARTMessageOperation.h (1)
3-22: Considercopysemantics for properties and trimming unused importsFor an immutable,
NS_SWIFT_SENDABLEtype it would be safer to declare the string/dictionary properties ascopyand copy the inputs in the initializer, to avoid external mutation via NSMutableString/NSMutableDictionary instances passed in.Also,
ARTStringifiable.hisn’t used in this header; if nothing transitively relies on it being re‑exported, you can drop that import to keep the public surface narrower.Source/ARTMessageOperation.m (2)
16-21: Copy currently shares underlying objects; consider defensive copies
copyWithZone:assignsclientId,descriptionText, andmetadataby reference:operation->_clientId = self.clientId; operation->_descriptionText = self.descriptionText; operation->_metadata = self.metadata;If callers ever pass mutable instances into the initializer, both the original and the copy will see subsequent mutations. If you want
ARTMessageOperationto behave immutably, prefer copying here (and in the initializer) instead.
45-53: Unimplemented dictionary factories may fail silently in release
createFromDictionary:andinitWithDictionary:currentlyNSAssert(false)and returnnil. In release builds this just returnsnilwith no signal if someone accidentally wires these up viaARTDictionarySerializable.If these aren’t needed yet, consider either:
- Explicitly documenting them as unsupported (e.g. comment +
ARTLogWarn), or- Implementing them now using the same keys as
writeToDictionary:.This avoids surprising
nilin future refactors.Source/ARTRestChannel.m (1)
104-118: REST message update/delete/get/version wiring looks correct and consistentThe new ARTRestChannel surface methods correctly forward to the internal counterparts with
wrapperSDKAgents:nil, andARTRestChannelInternalnow centralises update/delete in_updateMessage:isDelete:operation:params:wrapperSDKAgents:callback:with:
- Proper serial presence validation for RSL12/RSL13.
- Request bodies that match the spec (serial, optional operation/name/data/encoding/extras) and reuse of
dataEncoderfor the message payload.- Correct endpoint/method selection for update (
PATCH /messages/{serial}) vs delete (POST /messages/{serial}/delete), with params encoded into the query string.- Callbacks consistently marshalled back onto
_userQueue, and wrapperSDKAgents threaded through toexecuteRequest/executePaginated.Given there’s already a follow-up issue to de-duplicate REST request construction, this structure looks good for now.
Also applies to: 408-662
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Jira integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (16)
Ably.xcodeproj/project.pbxproj(16 hunks)Source/ARTChannel.m(1 hunks)Source/ARTMessageOperation.m(1 hunks)Source/ARTRealtimeChannel.m(2 hunks)Source/ARTRestChannel.m(3 hunks)Source/ARTSummaryTypes.m(1 hunks)Source/ARTWrapperSDKProxyRealtimeChannel.m(1 hunks)Source/Ably.modulemap(1 hunks)Source/PrivateHeaders/Ably/ARTChannel.h(2 hunks)Source/PrivateHeaders/Ably/ARTMessageOperation+Private.h(1 hunks)Source/include/Ably/ARTChannelProtocol.h(2 hunks)Source/include/Ably/ARTMessageOperation.h(1 hunks)Source/include/Ably/ARTTypes.h(1 hunks)Source/include/Ably/AblyPublic.h(1 hunks)Source/include/module.modulemap(1 hunks)Test/AblyTests/Tests/MessageUpdatesDeletesTests.swift(1 hunks)
🧰 Additional context used
🪛 Clang (14.0.6)
Source/PrivateHeaders/Ably/ARTMessageOperation+Private.h
[error] 1-1: expected identifier or '('
(clang-diagnostic-error)
[error] 2-2: 'Ably/ARTMessageOperation.h' file not found
(clang-diagnostic-error)
Source/include/Ably/ARTMessageOperation.h
[error] 1-1: 'Foundation/Foundation.h' file not found
(clang-diagnostic-error)
🪛 SwiftLint (0.57.0)
Test/AblyTests/Tests/MessageUpdatesDeletesTests.swift
[Warning] 5-5: Imports should be unique
(duplicate_imports)
[Error] 126-126: Force casts should be avoided
(force_cast)
[Error] 252-252: Force casts should be avoided
(force_cast)
🔇 Additional comments (11)
Ably.xcodeproj/project.pbxproj (2)
384-392: ARTMessageOperation is correctly wired across targets and build phasesThe new
ARTMessageOperationfiles (public header, private header, and.m) are added consistently to:
PBXFileReferenceand theTypesgroup,- all three frameworks’
Headersphases with appropriate Public/Private attributes,- all three frameworks’
Sourcesphases.This matches the existing pattern for other message-related types (e.g.,
ARTMessageVersion) and should make the new type available on iOS, macOS, and tvOS without build issues.Also applies to: 1241-1243, 1866-1868, 3081-3082
393-395: Confirm whether ARTDictionarySerializable is intentionally header‑only
ARTDictionarySerializable.his added as:
- a
PBXFileReferenceunderPrivateHeaders/Ably,- a member of the
Typesgroup,- a Private header in the iOS/macOS/tvOS
Headersbuild phases.There is no
ARTDictionarySerializable.mreferenced anywhere in this project file. If the type is meant to be header‑only (e.g. a protocol or category with no implementation), this is fine; if an implementation file exists, it still needs aPBXFileReference,PBXBuildFile, andSourcesentries for each target to actually compile.Please double‑check whether an
.mis expected here and wire it up if so.Also applies to: 1244-1244, 1935-1935, 2169-2171, 2501-2501, 2712-2712
Source/ARTSummaryTypes.m (1)
2-2: ImportingARTDictionarySerializableis appropriate hereThe new import cleanly reflects that the summary types conform to
ARTDictionarySerializable, without changing runtime behavior. Looks correct and consistent with the protocol usage throughout this file.Source/include/Ably/ARTTypes.h (1)
504-505: NewARTMessageErrorCallbacktypedef is consistent with existing patternsThe message+error callback shape and nullability mirror other result/error callbacks in this header and are suitable for single‑message lookup APIs.
Source/include/Ably/AblyPublic.h (1)
48-48: ExposingARTMessageOperationvia the public umbrella is correctIncluding
ARTMessageOperation.hhere makes the new message operation API available to SDK consumers in the expected way alongside other message types.Source/include/module.modulemap (1)
142-143: SPM Private module now correctly exposes new internal headersAdding
ARTDictionarySerializable.handARTMessageOperation+Private.hto theAbly.Privateexplicit module keeps the SPM module map aligned with the new internal types and matches the Xcode module map changes.Source/ARTChannel.m (1)
156-182: New abstract channel methods are wired consistently with existing designThe four new methods for update/delete/lookups follow the existing pattern for abstract operations on
ARTChannel(assert‑only bodies to enforce subclass overrides) and align with the public protocol variants that don’t takewrapperSDKAgents. As long asARTRestChannelandARTRealtimeChanneloverride these, this is a clean extension of the existing design.Please confirm both concrete channel implementations override all four methods so these code paths are never hit in production.
Source/Ably.modulemap (1)
142-143: Xcode module map correctly updated for new private headersIncluding
ARTDictionarySerializable.handARTMessageOperation+Private.hin theAbly.Privatemodule keeps the Xcode module map consistent with SPM and ensures internal code usingimport Ably.Privatecan see these types.Source/ARTWrapperSDKProxyRealtimeChannel.m (1)
107-136: Wrapper forwarding for message operations looks consistentThe new
getMessageWithSerial,updateMessage,deleteMessage, andgetMessageVersionsWithSerialmethods correctly delegate tounderlyingChannel.internaland pass throughproxyOptions.agents, matching the internal API shape and keeping wrapperSDK agent propagation consistent.Source/PrivateHeaders/Ably/ARTChannel.h (1)
48-66: Channel-level message APIs align with internal implementationsThe added
updateMessage,deleteMessage,getMessageWithSerial, andgetMessageVersionsWithSerialsignatures match the corresponding internal/rest implementations (includingARTStringifiableparams andNSStringDictionary *wrapperSDKAgents), which should keep the public/internal surfaces in sync.Source/ARTRealtimeChannel.m (1)
147-161: Realtime message operation APIs are wired cleanly through to RESTThe new
updateMessage,deleteMessage,getMessageWithSerial, andgetMessageVersionsWithSerialoverloads onARTRealtimeChannelandARTRealtimeChannelInternalconsistently delegate to the REST channel, threadingwrapperSDKAgentswhere provided and defaulting tonilotherwise. This keeps the public realtime API thin while reusing the REST implementation for edits/deletes/versions.Also applies to: 470-492
| } | ||
| } | ||
|
|
||
| static func rest(_ test: Test, channelPrefix: String = "mutable:") throws -> TestEnvironment { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do we need a channel prefix?
| } | ||
| } | ||
|
|
||
| private func waitUntilMessageBecomesAvailableInHistory() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks flaky. why can't we wait to receive it on a Realtime connection?
|
|
||
| var bodyDict: [String: Any]! | ||
|
|
||
| switch extractBodyAsMsgPack(request) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do you need to switch this? can't you just call get on the result and let it bubble up out of the test as an error?
| // RTL31: RealtimeChannel#getMessageVersions function: same as RestChannel#getMessageVersions | ||
| func test__RTL29__RTL31__updateMessage__and__getMessageVersions() throws { | ||
| let test = Test() | ||
| try _test__rest_and_realtime__updateMessage__and__getMessageVersions(.realtime(test)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
where do you disconnect the realtime client for these realtime tests? doesn't it just leak?
| private func _test__rest_and_realtime__getMessage(_ testEnvironment: TestEnvironment) throws { | ||
| let channel = testEnvironment.channel | ||
|
|
||
| var publishedMessage: ARTMessage! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would suggest not using implicitly unwrapped optionals (ditto force-unwrapping) unless we're sure that we're exiting early if they're nil, else we're going to crash the test
Summary by CodeRabbit
Release Notes
✏️ Tip: You can customize this high-level summary in your review settings.