Skip to content

[ECO-5474] Publish and get annotations#2080

Merged
lawrence-forooghian merged 4 commits intomainfrom
feature/2077-annotations
Oct 16, 2025
Merged

[ECO-5474] Publish and get annotations#2080
lawrence-forooghian merged 4 commits intomainfrom
feature/2077-annotations

Conversation

@maratal
Copy link
Collaborator

@maratal maratal commented Aug 3, 2025

Closes #2077, #1466, #2114

Summary by CodeRabbit

  • New Features
    • Added message Annotations: publish, delete, fetch, and subscribe (Realtime and REST). Includes idempotent publishing and size validation with clear errors.
    • Introduced Message Summaries with typed models and helpers for distinct/unique/multiple counts.
    • Exposed channel modes on Realtime channels and decode modes on attach.
  • Improvements
    • Enhanced serialization to encode/decode annotations consistently across transports.
  • Tests
    • New end-to-end tests covering annotations lifecycle, channel modes decoding, and summary type parsing.

@coderabbitai
Copy link

coderabbitai bot commented Aug 3, 2025

Note

Reviews paused

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds Annotations and Summary support across REST and Realtime: new REST annotations API, encoder/decoder for annotations, factory methods and sizing in ARTAnnotation, channel modes handling, exposure via REST/Realtime channels, new Summary types, private/public headers and module maps, constants, utilities, and comprehensive tests.

Changes

Cohort / File(s) Summary
Project integration
Ably.xcodeproj/project.pbxproj
Registers new ARTRestAnnotations and ARTSummaryTypes headers/sources in build phases/groups.
Annotation model updates
Source/ARTAnnotation.m, Source/include/Ably/ARTAnnotation.h, Source/PrivateHeaders/Ably/ARTAnnotation+Private.h
Renames encode/decode methods, adds size/id helpers, and multiple factory initializers for messageSerial-based annotations.
Encoder support for annotations
Source/ARTJsonLikeEncoder.m, Source/PrivateHeaders/Ably/ARTJsonLikeEncoder.h, Source/PrivateHeaders/Ably/ARTEncoder.h
Adds encode/decode for annotation and arrays; dictionary transforms; exposes methods in headers.
REST Annotations module
Source/ARTRestAnnotations.m, Source/include/Ably/ARTRestAnnotations.h, Source/PrivateHeaders/Ably/ARTRestAnnotations+Private.h
Implements query object, public wrapper, and internal logic for get/publish/delete by message or serial; idempotent ID generation; size checks; callbacks/queues.
Realtime Annotations integration
Source/ARTRealtimeAnnotations.m, Source/include/Ably/ARTRealtimeAnnotations.h, Source/PrivateHeaders/Ably/ARTRealtimeAnnotations+Private.h
Forwards get/publish/delete to internals; subscription flow adjustments; uses new encodeData/decoder; exposes weak realtime reference.
REST Channel exposure and id length constant
Source/ARTRestChannel.m, Source/include/Ably/ARTRestChannel.h, Source/PrivateHeaders/Ably/ARTRestChannel+Private.h, Source/ARTConstants.m, Source/PrivateHeaders/Ably/ARTConstants.h
Adds channel.annotations accessor; uses new ARTIdempotentLibraryGeneratedIdLength constant; removes local constant.
Channel modes (Realtime)
Source/ARTRealtimeChannel.m, Source/include/Ably/ARTRealtimeChannel.h, Source/PrivateHeaders/Ably/ARTRealtimeChannel+Private.h, Source/ARTProtocolMessage.m, Source/PrivateHeaders/Ably/ARTProtocolMessage.h
Adds channel mode flags handling, accessors, and grant checks; protocol message exposes channelModes derived from flags; updates attach handling.
Summary types
Source/ARTSummaryTypes.m, Source/include/Ably/ARTSummaryTypes.h
Introduces ARTSummary* data models, conversion functions, and ARTMessageAnnotations category accessors.
Wrapper proxies
Source/ARTWrapperSDKProxyRealtimeAnnotations.m, Source/ARTWrapperSDKProxyRealtimeChannel.m
Adds forwarding methods for annotations and channel modes.
Module maps and umbrella
Source/Ably.modulemap, Source/include/module.modulemap, Source/include/Ably/AblyPublic.h
Adds ARTRestAnnotations+Private to Private module; imports ARTRestAnnotations and ARTSummaryTypes publicly.
Utilities
Source/ARTNSDictionary+ARTDictionaryUtil.m, Source/PrivateHeaders/Ably/ARTNSDictionary+ARTDictionaryUtil.h
Adds NSDictionary artMap: transform helper.
Type removals
Source/include/Ably/ARTTypes.h
Removes ARTAnnotationErrorCallback typedef.
Tests
Test/AblyTests/Tests/RealtimeAnnotationsTests.swift, Test/AblyTests/Tests/RestAnnotationsTests.swift, Test/AblyTests/Tests/SummaryTypesTests.swift, Test/AblyTests/Tests/RealtimeClientChannelTests.swift, Test/AblyTests/Codables/TestAppSetup.swift
Adds end-to-end tests for annotations (realtime/rest), summary types parsing, channel modes decoding, and test app setup field.
Submodule
Test/AblyTests/ably-common
Updates submodule commit reference.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor App
  participant RC as ARTRestChannel
  participant RA as ARTRestAnnotationsInternal
  participant ENC as ARTJsonLikeEncoder
  participant S as Ably REST API

  App->>RC: annotations
  RC-->>App: ARTRestAnnotations
  App->>RA: publishForMessageSerial(serial, annotation)
  RA->>RA: Validate serial / generate id (idempotent?)
  RA->>ENC: Encode annotation data
  ENC-->>RA: NSData or error
  RA->>S: POST /messages/{serial}/annotations
  alt Success
    S-->>RA: 201 + payload
    RA-->>App: callback(nil)
  else Error
    S-->>RA: Error
    RA-->>App: callback(ARTErrorInfo)
  end
Loading
sequenceDiagram
  autonumber
  actor App
  participant RT as ARTRealtimeAnnotationsInternal
  participant CH as ARTRealtimeChannelInternal
  participant ENC as ARTJsonLikeEncoder
  participant WS as Ably Realtime

  App->>RT: publishForMessageSerial(serial, annotation)
  RT->>RT: Create immutable annotation(action)
  RT->>CH: Check connection/channel state
  RT->>ENC: Encode annotation
  ENC-->>RT: NSData
  RT->>WS: Send ProtocolMessage{annotations:[...]}
  WS-->>RT: ACK/NACK
  alt ACK
    RT-->>App: callback(nil)
  else NACK
    RT-->>App: callback(ARTErrorInfo)
  end
Loading
sequenceDiagram
  autonumber
  participant WS as Ably Realtime
  participant CH as ARTRealtimeChannelInternal
  participant PM as ARTProtocolMessage

  WS-->>CH: ProtocolMessage ATTACHED (flags)
  CH->>PM: read channelModes (flags & 0xFFFFFF00)
  CH->>CH: update modes
  CH-->>Client: channel.modes reflects publish/subscribe/annotationSubscribe
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

Suggested reviewers

Poem

A hop, a bop, I tag a note—
Reactions ride each message boat.
Summaries bloom in tidy rows,
Flags and modes like wind that blows.
REST and Realtime share the trail—
Encode, decode, we never fail.
🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning The pull request includes unrelated modifications such as a new generic artMap method on NSDictionary, an optional mutableMessages property added to TestAppSetup, and a submodule reference update, none of which are part of the annotations implementation objectives. Please remove or isolate these unrelated changes into separate pull requests so that this one remains focused on the annotations feature implementation.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title clearly indicates that this pull request implements publishing and fetching of annotations, which are key aspects of the changeset, and is concise and specific to the functionality being added.
Linked Issues Check ✅ Passed The pull request fully addresses the implementation of annotations as described in issues #2077 and ECO-5474 by adding REST and realtime publish, get, and delete operations, integrating encoding/decoding in the JSON encoder, updating channel mode handling, and introducing summary types per the specification.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.

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.

@github-actions github-actions bot temporarily deployed to staging/pull/2080/features August 3, 2025 21:27 Inactive
@maratal maratal changed the title [ECO-5474 ]Publish and get annotations [ECO-5474] Publish and get annotations Aug 3, 2025
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: 5

🧹 Nitpick comments (7)
Source/include/Ably/ARTRestChannel.h (1)

58-62: Consider adding concise API docs for the new annotations property

Every other public surface on ARTRestChannel carries at least a one-liner.
A short description such as “Provides annotation publish / fetch / delete operations for this channel” will make generated docs clearer and align with the existing style.

Source/include/Ably/ARTRestAnnotations.h (2)

12-22: Consider adding a public convenience initializer.

The class only exposes a private initializer marked with :nodoc:, but users will need a way to create instances. Consider adding a public convenience method or making the existing initializer public.

+/**
+ * Creates a new annotations query with the specified limit.
+ *
+ * @param limit An upper limit on the number of annotations returned.
+ */
++ (instancetype)queryWithLimit:(NSUInteger)limit;
+
+/**
+ * Creates a new annotations query with default limit.
+ */
++ (instancetype)query;
+
-/// :nodoc:
+/**
+ * Designated initializer.
+ *
+ * @param limit An upper limit on the number of annotations returned.
+ */
- (instancetype)initWithLimit:(NSUInteger)limit;

74-75: Fix documentation formatting inconsistency.

The comment block starting at line 74 has inconsistent spacing compared to other method documentation blocks.

- /**
+/**
  * Get all annotations for a given message (as a paginated result).
Ably.xcodeproj/project.pbxproj (1)

1449-1451: Sanity-check path & group for new REST annotation sources

The new file refs for:

  • include/Ably/ARTRestAnnotations.h
  • PrivateHeaders/Ably/ARTRestAnnotations+Private.h
  • ARTRestAnnotations.m

sit at the root of the project hierarchy. For consistency with existing files (e.g. ARTRestPresence.*) they usually live under Source/ and PrivateHeaders/.

No functional impact, but keeping the physical layout parallel improves discoverability.

Source/include/Ably/ARTRealtimeAnnotations.h (1)

56-56: Fix typo in documentation.

- * @param callback A callback for retriving an `ARTPaginatedResult` containing annotations.
+ * @param callback A callback for retrieving an `ARTPaginatedResult` containing annotations.
Test/Tests/RealtimeAnnotationsTests.swift (1)

101-101: Replace unused closure parameter with underscore.

-            realtimeChannel.once(.attached) { stateChange in
+            realtimeChannel.once(.attached) { _ in
Test/Tests/RestAnnotationsTests.swift (1)

106-106: Replace unused closure parameters with underscore.

-            realtimeChannel.once(.attached) { stateChange in
+            realtimeChannel.once(.attached) { _ in

Also applies to: 256-256

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d33f1c3 and 3fe9352.

📒 Files selected for processing (27)
  • Ably.xcodeproj/project.pbxproj (18 hunks)
  • Source/ARTAnnotation.m (3 hunks)
  • Source/ARTConstants.m (1 hunks)
  • Source/ARTJsonLikeEncoder.m (4 hunks)
  • Source/ARTRealtimeAnnotations.m (5 hunks)
  • Source/ARTRestAnnotations.m (1 hunks)
  • Source/ARTRestChannel.m (7 hunks)
  • Source/ARTWrapperSDKProxyRealtimeAnnotations.m (1 hunks)
  • Source/Ably.modulemap (1 hunks)
  • Source/PrivateHeaders/Ably/ARTAnnotation+Private.h (1 hunks)
  • Source/PrivateHeaders/Ably/ARTConstants.h (1 hunks)
  • Source/PrivateHeaders/Ably/ARTEncoder.h (2 hunks)
  • Source/PrivateHeaders/Ably/ARTJsonLikeEncoder.h (1 hunks)
  • Source/PrivateHeaders/Ably/ARTRealtimeAnnotations+Private.h (1 hunks)
  • Source/PrivateHeaders/Ably/ARTRestAnnotations+Private.h (1 hunks)
  • Source/PrivateHeaders/Ably/ARTRestChannel+Private.h (1 hunks)
  • Source/include/Ably/ARTAnnotation.h (1 hunks)
  • Source/include/Ably/ARTRealtimeAnnotations.h (2 hunks)
  • Source/include/Ably/ARTRestAnnotations.h (1 hunks)
  • Source/include/Ably/ARTRestChannel.h (2 hunks)
  • Source/include/Ably/ARTTypes.h (0 hunks)
  • Source/include/Ably/AblyPublic.h (1 hunks)
  • Source/include/module.modulemap (1 hunks)
  • Test/Codables/TestAppSetup.swift (1 hunks)
  • Test/Tests/RealtimeAnnotationsTests.swift (1 hunks)
  • Test/Tests/RestAnnotationsTests.swift (1 hunks)
  • ably-common (1 hunks)
💤 Files with no reviewable changes (1)
  • Source/include/Ably/ARTTypes.h
🧰 Additional context used
🧠 Learnings (12)
📓 Common learnings
Learnt from: maratal
PR: ably/ably-cocoa#1973
File: Source/include/Ably/ARTBaseQuery.h:4-8
Timestamp: 2024-09-12T20:12:29.428Z
Learning: As a general rule, `NS_SWIFT_SENDABLE` annotations are not needed for private Objective-C interfaces in the ably-cocoa project.
Learnt from: maratal
PR: ably/ably-cocoa#1973
File: Source/include/Ably/ARTBaseQuery.h:4-8
Timestamp: 2024-10-08T15:37:26.766Z
Learning: As a general rule, `NS_SWIFT_SENDABLE` annotations are not needed for private Objective-C interfaces in the ably-cocoa project.
Learnt from: maratal
PR: ably/ably-cocoa#2066
File: Source/ARTJsonLikeEncoder.m:341-351
Timestamp: 2025-06-16T15:39:16.005Z
Learning: In the Ably Cocoa SDK, when decoding annotation actions from integers, unknown/invalid action values should default to ARTAnnotationCreate rather than failing or using an unknown state. This is an intentional design decision for the annotation system.
📚 Learning: in `artrealtimechannelinternal`, the `internalpresence` property is declared in the private header `...
Learnt from: maratal
PR: ably/ably-cocoa#1995
File: Source/ARTRealtimeChannel.m:227-227
Timestamp: 2024-11-04T23:10:23.027Z
Learning: In `ARTRealtimeChannelInternal`, the `internalPresence` property is declared in the private header `ARTRealtimeChannel+Private.h`, so it does not need to be redeclared in `ARTRealtimeChannel.m`.

Applied to files:

  • Source/PrivateHeaders/Ably/ARTConstants.h
  • Source/include/Ably/AblyPublic.h
  • Source/include/Ably/ARTAnnotation.h
  • Source/ARTConstants.m
  • Source/PrivateHeaders/Ably/ARTRealtimeAnnotations+Private.h
  • Source/PrivateHeaders/Ably/ARTRestChannel+Private.h
  • Source/include/Ably/ARTRestChannel.h
  • Source/Ably.modulemap
  • Source/include/module.modulemap
  • Source/PrivateHeaders/Ably/ARTAnnotation+Private.h
  • Source/PrivateHeaders/Ably/ARTEncoder.h
  • Source/ARTRestChannel.m
  • Source/PrivateHeaders/Ably/ARTRestAnnotations+Private.h
  • Source/include/Ably/ARTRestAnnotations.h
  • Source/include/Ably/ARTRealtimeAnnotations.h
  • Source/ARTWrapperSDKProxyRealtimeAnnotations.m
  • Source/ARTRestAnnotations.m
  • Source/ARTRealtimeAnnotations.m
  • Ably.xcodeproj/project.pbxproj
📚 Learning: `artchanneloptions` is already marked with `ns_swift_sendable`....
Learnt from: maratal
PR: ably/ably-cocoa#1973
File: Source/include/Ably/ARTRealtimeChannelOptions.h:33-33
Timestamp: 2024-10-08T15:37:26.766Z
Learning: `ARTChannelOptions` is already marked with `NS_SWIFT_SENDABLE`.

Applied to files:

  • Source/PrivateHeaders/Ably/ARTConstants.h
  • Source/include/Ably/AblyPublic.h
  • Source/include/Ably/ARTAnnotation.h
  • Source/ARTConstants.m
  • Source/PrivateHeaders/Ably/ARTRealtimeAnnotations+Private.h
  • Source/PrivateHeaders/Ably/ARTRestChannel+Private.h
  • Source/include/Ably/ARTRestChannel.h
  • Source/Ably.modulemap
  • Source/include/module.modulemap
  • Source/PrivateHeaders/Ably/ARTJsonLikeEncoder.h
  • Source/PrivateHeaders/Ably/ARTAnnotation+Private.h
  • Source/PrivateHeaders/Ably/ARTEncoder.h
  • Source/ARTRestChannel.m
  • Source/PrivateHeaders/Ably/ARTRestAnnotations+Private.h
  • Source/include/Ably/ARTRestAnnotations.h
  • Source/ARTJsonLikeEncoder.m
  • Test/Tests/RealtimeAnnotationsTests.swift
  • Source/include/Ably/ARTRealtimeAnnotations.h
  • Test/Tests/RestAnnotationsTests.swift
  • Source/ARTRestAnnotations.m
  • Source/ARTRealtimeAnnotations.m
  • Ably.xcodeproj/project.pbxproj
📚 Learning: in 'source/artjsonlikeencoder.m', when handling messages from 'input', using `mutablecopy` on the ar...
Learnt from: maratal
PR: ably/ably-cocoa#1998
File: Source/ARTJsonLikeEncoder.m:766-774
Timestamp: 2024-11-26T17:50:25.730Z
Learning: In 'Source/ARTJsonLikeEncoder.m', when handling messages from 'input', using `mutableCopy` on the array ensures that modifications to `messages` do not affect the original `input`.

Applied to files:

  • Test/Codables/TestAppSetup.swift
  • Source/ARTAnnotation.m
  • Source/PrivateHeaders/Ably/ARTJsonLikeEncoder.h
  • Source/PrivateHeaders/Ably/ARTAnnotation+Private.h
  • Source/PrivateHeaders/Ably/ARTEncoder.h
  • Source/ARTRestChannel.m
  • Source/ARTJsonLikeEncoder.m
  • Source/ARTRestAnnotations.m
  • Source/ARTRealtimeAnnotations.m
📚 Learning: in the swift test files within the `test/tests/` directory of the ably cocoa project, it's acceptabl...
Learnt from: maratal
PR: ably/ably-cocoa#1995
File: Test/Tests/RealtimeClientChannelTests.swift:192-193
Timestamp: 2024-11-04T23:11:06.909Z
Learning: In the Swift test files within the `Test/Tests/` directory of the Ably Cocoa project, it's acceptable to access internal types and properties directly in tests.

Applied to files:

  • Test/Codables/TestAppSetup.swift
  • Source/include/Ably/AblyPublic.h
  • Source/PrivateHeaders/Ably/ARTRestChannel+Private.h
  • Source/Ably.modulemap
  • Source/include/module.modulemap
  • Source/PrivateHeaders/Ably/ARTRestAnnotations+Private.h
  • Test/Tests/RealtimeAnnotationsTests.swift
  • Source/include/Ably/ARTRealtimeAnnotations.h
  • Test/Tests/RestAnnotationsTests.swift
  • Ably.xcodeproj/project.pbxproj
📚 Learning: parsing the message's `id` is necessary for checking `msgserial` and `index` in the `member:isnewert...
Learnt from: maratal
PR: ably/ably-cocoa#1982
File: Source/ARTPresenceMessage.m:59-59
Timestamp: 2024-10-06T12:27:40.858Z
Learning: Parsing the message's `id` is necessary for checking `msgSerial` and `index` in the `member:isNewerThan:` method.

Applied to files:

  • Test/Codables/TestAppSetup.swift
  • Source/ARTRestChannel.m
📚 Learning: as a general rule, `ns_swift_sendable` annotations are not needed for private objective-c interfaces...
Learnt from: maratal
PR: ably/ably-cocoa#1973
File: Source/include/Ably/ARTBaseQuery.h:4-8
Timestamp: 2024-09-12T20:12:29.428Z
Learning: As a general rule, `NS_SWIFT_SENDABLE` annotations are not needed for private Objective-C interfaces in the ably-cocoa project.

Applied to files:

  • Source/include/Ably/AblyPublic.h
  • Source/include/Ably/ARTAnnotation.h
  • Source/PrivateHeaders/Ably/ARTRestChannel+Private.h
  • Source/include/Ably/ARTRestChannel.h
  • Source/Ably.modulemap
  • Source/include/module.modulemap
  • Source/PrivateHeaders/Ably/ARTJsonLikeEncoder.h
  • Source/PrivateHeaders/Ably/ARTAnnotation+Private.h
  • Source/PrivateHeaders/Ably/ARTEncoder.h
  • Source/ARTRestChannel.m
  • Source/PrivateHeaders/Ably/ARTRestAnnotations+Private.h
  • Source/include/Ably/ARTRestAnnotations.h
  • Source/include/Ably/ARTRealtimeAnnotations.h
  • Test/Tests/RestAnnotationsTests.swift
  • Source/ARTRealtimeAnnotations.m
  • Ably.xcodeproj/project.pbxproj
📚 Learning: in the ably cocoa sdk, when decoding annotation actions from integers, unknown/invalid action values...
Learnt from: maratal
PR: ably/ably-cocoa#2066
File: Source/ARTJsonLikeEncoder.m:341-351
Timestamp: 2025-06-16T15:39:16.005Z
Learning: In the Ably Cocoa SDK, when decoding annotation actions from integers, unknown/invalid action values should default to ARTAnnotationCreate rather than failing or using an unknown state. This is an intentional design decision for the annotation system.

Applied to files:

  • Source/include/Ably/AblyPublic.h
  • Source/include/Ably/ARTAnnotation.h
  • Source/PrivateHeaders/Ably/ARTRestChannel+Private.h
  • Source/include/Ably/ARTRestChannel.h
  • Source/ARTAnnotation.m
  • Source/PrivateHeaders/Ably/ARTJsonLikeEncoder.h
  • Source/PrivateHeaders/Ably/ARTAnnotation+Private.h
  • Source/PrivateHeaders/Ably/ARTEncoder.h
  • Source/ARTRestChannel.m
  • Source/PrivateHeaders/Ably/ARTRestAnnotations+Private.h
  • Source/include/Ably/ARTRestAnnotations.h
  • Source/ARTJsonLikeEncoder.m
  • Test/Tests/RealtimeAnnotationsTests.swift
  • Source/include/Ably/ARTRealtimeAnnotations.h
  • Test/Tests/RestAnnotationsTests.swift
  • Source/ARTWrapperSDKProxyRealtimeAnnotations.m
  • Source/ARTRestAnnotations.m
  • Source/ARTRealtimeAnnotations.m
📚 Learning: avoid suggesting refactoring the conditional compilation in `restclientchannelstests.swift` for `art...
Learnt from: maratal
PR: ably/ably-cocoa#1973
File: Test/Tests/RestClientChannelsTests.swift:5-18
Timestamp: 2024-09-17T13:25:42.060Z
Learning: Avoid suggesting refactoring the conditional compilation in `RestClientChannelsTests.swift` for `ARTRestChannels` extensions, as the user prefers to keep the current structure.

Applied to files:

  • Source/include/Ably/AblyPublic.h
  • Source/PrivateHeaders/Ably/ARTRestChannel+Private.h
  • Source/include/Ably/ARTRestChannel.h
  • Source/Ably.modulemap
  • Source/include/module.modulemap
  • Source/ARTRestChannel.m
  • Source/PrivateHeaders/Ably/ARTRestAnnotations+Private.h
  • Source/include/Ably/ARTRestAnnotations.h
  • Test/Tests/RealtimeAnnotationsTests.swift
  • Test/Tests/RestAnnotationsTests.swift
  • Source/ARTRestAnnotations.m
  • Ably.xcodeproj/project.pbxproj
📚 Learning: in `artmessage`, the `action` and `serial` fields are set by the realtime service and should not be ...
Learnt from: maratal
PR: ably/ably-cocoa#1998
File: Source/ARTJsonLikeEncoder.m:278-279
Timestamp: 2024-11-22T10:11:05.305Z
Learning: In `ARTMessage`, the `action` and `serial` fields are set by the realtime service and should not be serialized in the `messageToDictionary:` method in `Source/ARTJsonLikeEncoder.m`.

Applied to files:

  • Source/include/Ably/ARTAnnotation.h
  • Source/ARTConstants.m
  • Source/ARTAnnotation.m
  • Source/PrivateHeaders/Ably/ARTJsonLikeEncoder.h
  • Source/PrivateHeaders/Ably/ARTAnnotation+Private.h
  • Source/PrivateHeaders/Ably/ARTEncoder.h
  • Source/ARTRestChannel.m
  • Source/ARTJsonLikeEncoder.m
  • Source/include/Ably/ARTRealtimeAnnotations.h
  • Source/ARTWrapperSDKProxyRealtimeAnnotations.m
  • Source/ARTRestAnnotations.m
  • Source/ARTRealtimeAnnotations.m
📚 Learning: the unpublish method in artrealtimeannotations works by publishing an annotation with a delete actio...
Learnt from: maratal
PR: ably/ably-cocoa#2066
File: Source/include/Ably/ARTRealtimeAnnotations.h:25-28
Timestamp: 2025-06-16T14:18:14.569Z
Learning: The unpublish method in ARTRealtimeAnnotations works by publishing an annotation with a DELETE action, rather than directly deleting. This is why the documentation correctly refers to "The annotation to publish" even though it's an unpublish method.

Applied to files:

  • Test/Tests/RealtimeAnnotationsTests.swift
  • Source/include/Ably/ARTRealtimeAnnotations.h
  • Test/Tests/RestAnnotationsTests.swift
  • Source/ARTWrapperSDKProxyRealtimeAnnotations.m
  • Source/ARTRestAnnotations.m
  • Source/ARTRealtimeAnnotations.m
📚 Learning: in xcode project files, multiple pbxbuildfile entries for the same source or header file are valid i...
Learnt from: maratal
PR: ably/ably-cocoa#2066
File: Ably.xcodeproj/project.pbxproj:416-424
Timestamp: 2025-06-16T14:12:36.705Z
Learning: In Xcode project files, multiple PBXBuildFile entries for the same source or header file are valid if they are associated with different build targets.

Applied to files:

  • Ably.xcodeproj/project.pbxproj
🪛 SwiftLint (0.57.0)
Test/Tests/RealtimeAnnotationsTests.swift

[Warning] 101-101: Unused parameter in a closure should be replaced with _

(unused_closure_parameter)

Test/Tests/RestAnnotationsTests.swift

[Warning] 106-106: Unused parameter in a closure should be replaced with _

(unused_closure_parameter)


[Warning] 256-256: Unused parameter in a closure should be replaced with _

(unused_closure_parameter)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: check
  • GitHub Check: check
  • GitHub Check: build
  • GitHub Check: check (tvOS, test_tvOS18_4)
  • GitHub Check: check (macOS, test_macOS)
  • GitHub Check: check (iOS, test_iOS18_4)
🔇 Additional comments (41)
ably-common (1)

1-1: Submodule advance verified for annotation APIs

Commit 3fe9352 ("Add rest annotations. Add publish method for realtime annotations. Add test for annotations") contains the expected spec updates and accompanying tests, with no unintended breaking changes detected.

Test/Codables/TestAppSetup.swift (1)

46-46: LGTM! Property addition follows established patterns.

The new mutableMessages optional Boolean property is consistent with the existing optional Boolean properties in the Namespace struct and supports the test configuration needs for the annotations feature.

Source/ARTConstants.m (1)

5-5: LGTM! Good centralization of the constant.

The new ARTIdempotentLibraryGeneratedIdLength constant centralizes what was previously a local definition, improving maintainability. The descriptive name and comment clarifying the unit (bytes) make the purpose clear.

Source/PrivateHeaders/Ably/ARTConstants.h (1)

5-5: LGTM! Proper extern declaration for the new constant.

The extern declaration correctly exposes the ARTIdempotentLibraryGeneratedIdLength constant for internal SDK use, following the established pattern of the existing constant declarations.

Source/include/Ably/ARTAnnotation.h (1)

77-78: LGTM! Important utility method for size validation.

The new annotationSize method provides necessary size calculation functionality for annotation publishing workflows. The :nodoc: annotation correctly marks it as internal-only, and the method name clearly conveys its purpose.

Source/PrivateHeaders/Ably/ARTRealtimeAnnotations+Private.h (1)

9-9: LGTM! Proper weak reference with clear documentation.

The new realtime property provides necessary back-reference to the owning realtime instance. The weak reference correctly prevents retain cycles, and the comment clearly explains the ownership relationship. This enables internal coordination between the annotations and realtime components.

Source/include/module.modulemap (1)

49-50: Addition looks correct – keep both module maps in sync

ARTRestAnnotations+Private.h is now exposed to SPM consumers via Ably.Private.
The header is placed next to the other annotation headers and the alphabetical order is preserved, so no issues here.

Source/include/Ably/AblyPublic.h (1)

24-25: Public umbrella updated correctly

Adding #import <Ably/ARTRestAnnotations.h> surfaces the new REST-side annotations API to SDK users and keeps the umbrella header in step with the newly-exposed class. All good.

Source/Ably.modulemap (1)

49-50: Xcode module map kept in lock-step with the SPM map

ARTRestAnnotations+Private.h is now available when clients use import Ably.Private inside Xcode builds.
The update mirrors the change in the SPM module map, avoiding the mismatches we have hit in the past.

Source/include/Ably/ARTRestChannel.h (1)

7-7: Forward declaration added – OK

The new forward declaration for ARTRestAnnotations prevents an extra public import in this header. Looks good.

Source/PrivateHeaders/Ably/ARTRestChannel+Private.h (1)

15-22: Internal plumbing in place – matches the public API

ARTRestAnnotationsInternal is wired in exactly the same pattern as presence and push, so the internal/external surfaces stay consistent. No concerns.

Source/PrivateHeaders/Ably/ARTAnnotation+Private.h (1)

11-12: LGTM! Method renaming improves clarity.

The addition of "Data" to the method names (decodeDataWithEncoder:error: and encodeDataWithEncoder:error:) provides better clarity about what these methods encode/decode, distinguishing them from other encoding operations in the annotation system.

Source/PrivateHeaders/Ably/ARTJsonLikeEncoder.h (1)

36-37: LGTM! Consistent API design for annotation support.

The new annotation encoding/decoding methods follow the established patterns for messages and presence messages, maintaining consistency in the encoder interface. The method names are clear and the placement in the private interface is appropriate.

Also applies to: 45-46

Source/ARTRestChannel.m (4)

21-23: LGTM! Proper imports for annotation support.

The addition of imports for ARTRestAnnotations, ARTRestAnnotations+Private.h, and ARTConstants.h correctly supports the new annotation functionality being integrated.


42-44: LGTM! Consistent pattern for exposing internal functionality.

The annotations getter method follows the same pattern as the existing presence and push getters, maintaining consistency in the API design.


120-120: LGTM! Clean integration of annotations into the channel.

The addition of the _annotations property and its eager initialization in the constructor, along with the simple getter method, cleanly integrates annotation support into the REST channel. The eager initialization differs from the lazy initialization of presence, which is appropriate based on expected annotation usage patterns.

Also applies to: 133-133, 151-153


309-309: LGTM! Replaced magic number with named constant.

The replacement of the hardcoded value with ARTIdempotentLibraryGeneratedIdLength improves code maintainability and consistency across the codebase.

Also applies to: 333-333

Source/PrivateHeaders/Ably/ARTRestAnnotations+Private.h (1)

1-24: LGTM! Well-structured private header following project conventions.

The private header follows established patterns in the codebase:

  • Proper forward declarations for dependencies
  • Clean separation between internal implementation and wrapper classes
  • Consistent naming conventions
  • Appropriate use of NS_ASSUME_NONNULL_BEGIN/END blocks

The structure aligns with other private headers like ARTRestPresence+Private.h and maintains the project's architectural consistency.

Source/PrivateHeaders/Ably/ARTEncoder.h (1)

7-7: LGTM! Consistent protocol extension for annotation support.

The additions to the ARTEncoder protocol properly extend the encoding infrastructure:

  • Forward declaration for ARTAnnotation is correctly placed
  • New encoding/decoding methods follow the established pattern for messages and presence messages
  • Method signatures are consistent with existing conventions
  • Logical grouping maintains protocol organization

The protocol extension enables proper annotation serialization across the SDK.

Also applies to: 52-55, 60-63

Source/include/Ably/ARTRestAnnotations.h (1)

85-92: LGTM!

The class declaration is well-structured with appropriate Swift sendable annotation and clear documentation referencing the protocol for implementation details.

Source/ARTAnnotation.m (4)

76-86: LGTM!

The method rename from decodeWithEncoder to decodeDataWithEncoder improves clarity about the operation's purpose. The implementation logic and error handling remain correct.


88-98: LGTM!

The method rename to encodeDataWithEncoder is consistent with the decode method and improves API clarity. The implementation and error handling are correct.


133-135: LGTM!

The isIdEmpty method correctly handles both nil and empty string cases with a clear and simple implementation.


100-131: Improve null safety and error handling.

The size calculation logic is comprehensive, but there are several potential null pointer issues and silent failures:

  1. self.name and self.clientId could be nil, causing crashes when calling lengthOfBytesUsingEncoding:
  2. [self.extras toJSONString] could return nil
  3. JSON serialization errors are silently ignored, potentially underestimating size
- (NSInteger)annotationSize {
    // TO3l8*
    NSInteger finalResult = 0;
-   finalResult += [self.name lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
-   finalResult += [[self.extras toJSONString] lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
-   finalResult += [self.clientId lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
+   if (self.name) {
+       finalResult += [self.name lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
+   }
+   NSString *extrasJSON = [self.extras toJSONString];
+   if (extrasJSON) {
+       finalResult += [extrasJSON lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
+   }
+   if (self.clientId) {
+       finalResult += [self.clientId lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
+   }
    if (self.data) {
        if ([self.data isKindOfClass:[NSString class]]) {
            finalResult += [self.data lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
        }
        else if ([self.data isKindOfClass:[NSData class]]) {
            finalResult += [self.data length];
        }
        else {
            NSError *error = nil;
            NSJSONWritingOptions options;
            if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, *)) {
                options = NSJSONWritingWithoutEscapingSlashes;
            }
            else {
                options = 0; //no specific format
            }
            NSData *jsonData = [NSJSONSerialization dataWithJSONObject:self.data
                                                               options:options
                                                                 error:&error];
-           if (!error) {
+           if (!error && jsonData) {
                finalResult += [jsonData length];
+           } else {
+               // Log warning about failed JSON serialization
+               ARTLogWarn(@"Failed to serialize annotation data for size calculation: %@", error.localizedDescription);
            }
        }
    }
    return finalResult;
}
⛔ Skipped due to learnings
Learnt from: maratal
PR: ably/ably-cocoa#2066
File: Source/ARTJsonLikeEncoder.m:341-351
Timestamp: 2025-06-16T15:39:16.005Z
Learning: In the Ably Cocoa SDK, when decoding annotation actions from integers, unknown/invalid action values should default to ARTAnnotationCreate rather than failing or using an unknown state. This is an intentional design decision for the annotation system.
Ably.xcodeproj/project.pbxproj (3)

401-406: Verify test-file membership – 3 build-file entries per test source could be overkill

RealtimeAnnotationsTests.swift and RestAnnotationsTests.swift each appear three times in the PBXBuildFile section.
Duplicating a file across different test targets (iOS / tvOS / macOS) is fine, but duplicating within the same target will trigger duplicate symbol / “file listed twice” linker warnings.

Please double-check the target membership in the Build Phases ▸ Compile Sources pages for every test bundle to ensure each file is present once per target.
If any duplicates are in the same target, drop the extra reference(s).


437-446: Public/Private header flags look correct – confirm they land in the right header phase

ARTRestAnnotations.h is marked Public, while ARTRestAnnotations+Private.h is Private, which matches the existing header-visibility conventions.
Just make sure the Public header is added to the “Public Headers” phase of all framework targets (Ably iOS / macOS / tvOS) so that CocoaPods / SwiftPM consumers can see it.


3545-3545: Triple inclusion of ARTRestAnnotations.m – verify per-target duplication only

ARTRestAnnotations.m shows up in three separate Compile Sources lists. If those map to the iOS, macOS and tvOS framework targets respectively, all good. If they map to the same target, a duplicate-object build failure will occur.

Run an Xcode build for each platform to be safe.

Also applies to: 3808-3808, 3947-3947

Source/include/Ably/ARTRealtimeAnnotations.h (1)

15-68: Well-designed API for annotation operations.

The new methods provide a comprehensive interface for annotation operations with good separation between message-based and serial-based variants. The consistent pattern across publish, delete, and get operations makes the API intuitive to use.

Test/Tests/RealtimeAnnotationsTests.swift (2)

128-128: Temporary sleep could cause test flakiness.

The hardcoded sleep(1) could make tests unreliable. I see this is documented as a temporary measure pending CHA-887. Consider using a more robust synchronization mechanism or at least wrapping this in a retry logic to reduce flakiness.


10-152: Comprehensive test coverage for annotation lifecycle.

The test thoroughly validates the annotation publish, delete, and retrieval flow with proper verification of annotation properties and message summaries. Good use of split done pattern for managing multiple async operations.

Source/ARTWrapperSDKProxyRealtimeAnnotations.m (1)

35-58: Correct proxy implementation for annotation methods.

All six new methods correctly forward calls to the underlying ARTRealtimeAnnotations instance, maintaining consistency with the existing proxy pattern.

Source/ARTJsonLikeEncoder.m (1)

87-109: Comprehensive annotation encoding/decoding implementation.

The implementation correctly handles:

  • Single annotation and array encoding/decoding
  • Proper action value conversion with intentional default to ARTAnnotationCreate
  • All annotation properties in serialization
  • Integration with protocol message encoding/decoding

The code follows established patterns and properly handles edge cases.

Also applies to: 357-367, 422-457, 493-535, 564-573, 634-636, 893-893

Test/Tests/RestAnnotationsTests.swift (2)

173-303: Excellent test for idempotent annotation publishing.

This test thoroughly validates that annotations with implicit IDs are only published once when idempotent publishing is enabled. The verification that duplicate publish attempts succeed without creating duplicate annotations is particularly valuable.


305-343: Good edge case testing for message size limits.

The test properly validates that annotations exceeding maxMessageSize are rejected with the correct error code (40009). Using ARTDefault.maxMessageSize() ensures the test adapts to configuration changes.

Source/ARTRestAnnotations.m (3)

19-45: LGTM!

The ARTAnnotationsQuery implementation correctly handles limit validation and defaults.


47-84: LGTM!

The ARTRestAnnotations wrapper correctly delegates all operations to the internal implementation.


149-213: Potential crash due to weak reference in async block

The weak _channel reference could become nil during async execution, potentially causing crashes when accessing channel properties. Consider capturing a strong reference or checking for nil.

-dispatch_async(_queue, ^{
+    ARTRestChannelInternal *channel = _channel;
+    if (!channel) {
+        if (callback) {
+            callback([ARTErrorInfo createWithCode:ARTErrorConnectionFailed message:@"Channel has been deallocated"]);
+        }
+        return;
+    }
+    
+dispatch_async(_queue, ^{
+    // Re-check channel availability inside the block
+    if (!channel) {
+        if (callback) {
+            callback([ARTErrorInfo createWithCode:ARTErrorConnectionFailed message:@"Channel has been deallocated"]);
+        }
+        return;
+    }
+    
     // RSAN1c3: encode annotation data
     NSError *encodeError = nil;
-    ARTAnnotation *annotationToPublish = self->_dataEncoder ? [annotation encodeDataWithEncoder:self->_dataEncoder error:&encodeError] : annotation;
+    ARTAnnotation *annotationToPublish = self->_dataEncoder ? [annotation encodeDataWithEncoder:self->_dataEncoder error:&encodeError] : annotation;

Then use channel instead of self->_channel throughout the block.

⛔ Skipped due to learnings
Learnt from: maratal
PR: ably/ably-cocoa#1973
File: Source/include/Ably/ARTRealtimeChannelOptions.h:33-33
Timestamp: 2024-10-08T15:37:26.766Z
Learning: `ARTChannelOptions` is already marked with `NS_SWIFT_SENDABLE`.
Learnt from: maratal
PR: ably/ably-cocoa#1973
File: Test/Tests/RestClientChannelsTests.swift:5-18
Timestamp: 2024-09-17T13:25:42.060Z
Learning: Avoid suggesting refactoring the conditional compilation in `RestClientChannelsTests.swift` for `ARTRestChannels` extensions, as the user prefers to keep the current structure.
Learnt from: maratal
PR: ably/ably-cocoa#1973
File: Test/Tests/RestClientChannelsTests.swift:5-18
Timestamp: 2024-10-08T15:37:26.765Z
Learning: Avoid suggesting refactoring the conditional compilation in `RestClientChannelsTests.swift` for `ARTRestChannels` extensions, as the user prefers to keep the current structure.
Learnt from: maratal
PR: ably/ably-cocoa#1995
File: Source/ARTRealtimeChannel.m:227-227
Timestamp: 2024-11-04T23:10:23.027Z
Learning: In `ARTRealtimeChannelInternal`, the `internalPresence` property is declared in the private header `ARTRealtimeChannel+Private.h`, so it does not need to be redeclared in `ARTRealtimeChannel.m`.
Learnt from: maratal
PR: ably/ably-cocoa#1998
File: Source/ARTJsonLikeEncoder.m:278-279
Timestamp: 2024-11-22T10:11:05.305Z
Learning: In `ARTMessage`, the `action` and `serial` fields are set by the realtime service and should not be serialized in the `messageToDictionary:` method in `Source/ARTJsonLikeEncoder.m`.
Learnt from: maratal
PR: ably/ably-cocoa#2066
File: Source/ARTJsonLikeEncoder.m:341-351
Timestamp: 2025-06-16T15:39:16.005Z
Learning: In the Ably Cocoa SDK, when decoding annotation actions from integers, unknown/invalid action values should default to ARTAnnotationCreate rather than failing or using an unknown state. This is an intentional design decision for the annotation system.
Learnt from: maratal
PR: ably/ably-cocoa#1973
File: Source/include/Ably/ARTBaseQuery.h:4-8
Timestamp: 2024-09-12T20:12:29.428Z
Learning: As a general rule, `NS_SWIFT_SENDABLE` annotations are not needed for private Objective-C interfaces in the ably-cocoa project.
Learnt from: maratal
PR: ably/ably-cocoa#1973
File: Source/include/Ably/ARTBaseQuery.h:4-8
Timestamp: 2024-10-08T15:37:26.766Z
Learning: As a general rule, `NS_SWIFT_SENDABLE` annotations are not needed for private Objective-C interfaces in the ably-cocoa project.
Learnt from: maratal
PR: ably/ably-cocoa#1998
File: Source/ARTJsonLikeEncoder.m:766-774
Timestamp: 2024-11-26T17:50:25.730Z
Learning: In 'Source/ARTJsonLikeEncoder.m', when handling messages from 'input', using `mutableCopy` on the array ensures that modifications to `messages` do not affect the original `input`.
Learnt from: maratal
PR: ably/ably-cocoa#2066
File: Source/include/Ably/ARTRealtimeAnnotations.h:25-28
Timestamp: 2025-06-16T14:18:14.569Z
Learning: The unpublish method in ARTRealtimeAnnotations works by publishing an annotation with a DELETE action, rather than directly deleting. This is why the documentation correctly refers to "The annotation to publish" even though it's an unpublish method.
Source/ARTRealtimeAnnotations.m (4)

32-54: LGTM!

The new forwarding methods in ARTRealtimeAnnotations correctly delegate to the internal implementation, maintaining consistency with the existing pattern.


107-191: Well-structured implementation with proper state handling

The publishAnnotation method correctly handles connection state validation, annotation encoding, size limits, and channel state transitions. The use of dispatch_sync is appropriate for state checking.


192-221: LGTM! Clean refactoring

The _subscribe method has been refactored for better readability while maintaining the same functionality. The state checking and attachOnSubscribe handling remain correct.


287-287: LGTM!

The method call update to decodeDataWithEncoder:error: is consistent with the renamed API.

@github-actions github-actions bot temporarily deployed to staging/pull/2080/jazzydoc August 3, 2025 21:33 Inactive
@maratal maratal force-pushed the feature/2077-annotations branch from 3fe9352 to 966de7f Compare August 3, 2025 23:21
@maratal
Copy link
Collaborator Author

maratal commented Aug 3, 2025

@coderabbitai pause

@github-actions github-actions bot temporarily deployed to staging/pull/2080/features August 3, 2025 23:22 Inactive
@maratal maratal requested a review from Copilot August 3, 2025 23:24
@github-actions github-actions bot temporarily deployed to staging/pull/2080/jazzydoc August 3, 2025 23:24 Inactive
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR implements support for message annotations in both REST and Realtime APIs, enabling users to publish, delete, and retrieve annotations (such as reactions) on individual messages. The implementation adds new query options for fetching annotations with pagination and ensures proper error handling for annotations exceeding maximum message size.

  • Adds ARTRestAnnotations and ARTRealtimeAnnotations classes with comprehensive methods for annotation management
  • Implements annotation size validation against maxMessageSize with appropriate error codes
  • Adds comprehensive test coverage for both REST and realtime annotation operations including idempotency and error conditions

Reviewed Changes

Copilot reviewed 27 out of 27 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
Source/include/Ably/ARTRestAnnotations.h Public header defining REST annotations API with query parameters and protocol methods
Source/ARTRestAnnotations.m Implementation of REST annotations functionality with HTTP requests and validation
Source/ARTRealtimeAnnotations.m Implementation of realtime annotations with protocol message handling
Test/Tests/RestAnnotationsTests.swift Comprehensive tests for REST annotation operations
Test/Tests/RealtimeAnnotationsTests.swift Comprehensive tests for realtime annotation operations
Various header/project files Module map updates and public API exposure

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: 0

♻️ Duplicate comments (3)
Source/ARTRestAnnotations.m (3)

224-226: Add nil check for message parameter

The method should validate that the message parameter is not nil before accessing its serial property.

 - (void)getForMessage:(ARTMessage *)message query:(ARTAnnotationsQuery *)query callback:(ARTPaginatedAnnotationsCallback)callback {
+    if (!message || !message.serial) {
+        if (callback) {
+            dispatch_async(_userQueue, ^{
+                callback(nil, [ARTErrorInfo createWithCode:ARTErrorInvalidParams message:@"Message or message serial cannot be nil"]);
+            });
+        }
+        return;
+    }
     [self getForMessageSerial:message.serial query:query callback:callback];
 }

286-288: Add nil check for message parameter in publishForMessage

The method should validate that the message parameter is not nil before accessing its serial property.

 - (void)publishForMessage:(ARTMessage *)message annotation:(ARTAnnotation *)annotation callback:(ARTCallback)callback {
+    if (!message || !message.serial) {
+        if (callback) {
+            dispatch_async(_userQueue, ^{
+                callback([ARTErrorInfo createWithCode:ARTErrorInvalidParams message:@"Message or message serial cannot be nil"]);
+            });
+        }
+        return;
+    }
     [self publishAnnotation:annotation messageSerial:message.serial action:ARTAnnotationCreate callback:callback];
 }

294-296: Add nil check for message parameter in deleteForMessage

The method should validate that the message parameter is not nil before accessing its serial property.

 - (void)deleteForMessage:(ARTMessage *)message annotation:(ARTAnnotation *)annotation callback:(ARTCallback)callback {
+    if (!message || !message.serial) {
+        if (callback) {
+            dispatch_async(_userQueue, ^{
+                callback([ARTErrorInfo createWithCode:ARTErrorInvalidParams message:@"Message or message serial cannot be nil"]);
+            });
+        }
+        return;
+    }
     [self publishAnnotation:annotation messageSerial:message.serial action:ARTAnnotationDelete callback:callback];
 }
🧹 Nitpick comments (3)
Test/Tests/RealtimeAnnotationsTests.swift (1)

101-101: Replace unused closure parameter with underscore

The stateChange parameter is not used in the closure.

-realtimeChannel.once(.attached) { stateChange in
+realtimeChannel.once(.attached) { _ in
Test/Tests/RestAnnotationsTests.swift (2)

106-106: Replace unused closure parameter with underscore

The stateChange parameter is not used in the closure.

-realtimeChannel.once(.attached) { stateChange in
+realtimeChannel.once(.attached) { _ in

256-256: Replace unused closure parameter with underscore

The stateChange parameter is not used in the closure.

-realtimeChannel.once(.attached) { stateChange in
+realtimeChannel.once(.attached) { _ in
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3fe9352 and 966de7f.

📒 Files selected for processing (26)
  • Ably.xcodeproj/project.pbxproj (18 hunks)
  • Source/ARTAnnotation.m (3 hunks)
  • Source/ARTConstants.m (1 hunks)
  • Source/ARTJsonLikeEncoder.m (4 hunks)
  • Source/ARTRealtimeAnnotations.m (5 hunks)
  • Source/ARTRestAnnotations.m (1 hunks)
  • Source/ARTRestChannel.m (7 hunks)
  • Source/ARTWrapperSDKProxyRealtimeAnnotations.m (1 hunks)
  • Source/Ably.modulemap (1 hunks)
  • Source/PrivateHeaders/Ably/ARTAnnotation+Private.h (1 hunks)
  • Source/PrivateHeaders/Ably/ARTConstants.h (1 hunks)
  • Source/PrivateHeaders/Ably/ARTEncoder.h (2 hunks)
  • Source/PrivateHeaders/Ably/ARTJsonLikeEncoder.h (1 hunks)
  • Source/PrivateHeaders/Ably/ARTRealtimeAnnotations+Private.h (1 hunks)
  • Source/PrivateHeaders/Ably/ARTRestAnnotations+Private.h (1 hunks)
  • Source/PrivateHeaders/Ably/ARTRestChannel+Private.h (1 hunks)
  • Source/include/Ably/ARTAnnotation.h (1 hunks)
  • Source/include/Ably/ARTRealtimeAnnotations.h (2 hunks)
  • Source/include/Ably/ARTRestAnnotations.h (1 hunks)
  • Source/include/Ably/ARTRestChannel.h (2 hunks)
  • Source/include/Ably/ARTTypes.h (0 hunks)
  • Source/include/Ably/AblyPublic.h (1 hunks)
  • Source/include/module.modulemap (1 hunks)
  • Test/Codables/TestAppSetup.swift (1 hunks)
  • Test/Tests/RealtimeAnnotationsTests.swift (1 hunks)
  • Test/Tests/RestAnnotationsTests.swift (1 hunks)
💤 Files with no reviewable changes (1)
  • Source/include/Ably/ARTTypes.h
✅ Files skipped from review due to trivial changes (5)
  • Source/include/module.modulemap
  • Source/Ably.modulemap
  • Source/include/Ably/AblyPublic.h
  • Source/ARTWrapperSDKProxyRealtimeAnnotations.m
  • Ably.xcodeproj/project.pbxproj
🚧 Files skipped from review as they are similar to previous changes (17)
  • Test/Codables/TestAppSetup.swift
  • Source/PrivateHeaders/Ably/ARTConstants.h
  • Source/ARTConstants.m
  • Source/PrivateHeaders/Ably/ARTRealtimeAnnotations+Private.h
  • Source/include/Ably/ARTAnnotation.h
  • Source/PrivateHeaders/Ably/ARTRestChannel+Private.h
  • Source/include/Ably/ARTRestChannel.h
  • Source/ARTRestChannel.m
  • Source/PrivateHeaders/Ably/ARTRestAnnotations+Private.h
  • Source/PrivateHeaders/Ably/ARTAnnotation+Private.h
  • Source/PrivateHeaders/Ably/ARTJsonLikeEncoder.h
  • Source/PrivateHeaders/Ably/ARTEncoder.h
  • Source/include/Ably/ARTRestAnnotations.h
  • Source/ARTAnnotation.m
  • Source/include/Ably/ARTRealtimeAnnotations.h
  • Source/ARTJsonLikeEncoder.m
  • Source/ARTRealtimeAnnotations.m
🧰 Additional context used
🧠 Learnings (11)
📓 Common learnings
Learnt from: maratal
PR: ably/ably-cocoa#1973
File: Source/include/Ably/ARTBaseQuery.h:4-8
Timestamp: 2024-09-12T20:12:29.428Z
Learning: As a general rule, `NS_SWIFT_SENDABLE` annotations are not needed for private Objective-C interfaces in the ably-cocoa project.
Learnt from: maratal
PR: ably/ably-cocoa#1973
File: Source/include/Ably/ARTBaseQuery.h:4-8
Timestamp: 2024-10-08T15:37:26.766Z
Learning: As a general rule, `NS_SWIFT_SENDABLE` annotations are not needed for private Objective-C interfaces in the ably-cocoa project.
Learnt from: maratal
PR: ably/ably-cocoa#2066
File: Source/ARTJsonLikeEncoder.m:341-351
Timestamp: 2025-06-16T15:39:16.005Z
Learning: In the Ably Cocoa SDK, when decoding annotation actions from integers, unknown/invalid action values should default to ARTAnnotationCreate rather than failing or using an unknown state. This is an intentional design decision for the annotation system.
📚 Learning: in the swift test files within the `test/tests/` directory of the ably cocoa project, it's acceptabl...
Learnt from: maratal
PR: ably/ably-cocoa#1995
File: Test/Tests/RealtimeClientChannelTests.swift:192-193
Timestamp: 2024-11-04T23:11:06.909Z
Learning: In the Swift test files within the `Test/Tests/` directory of the Ably Cocoa project, it's acceptable to access internal types and properties directly in tests.

Applied to files:

  • Test/Tests/RealtimeAnnotationsTests.swift
  • Test/Tests/RestAnnotationsTests.swift
📚 Learning: avoid suggesting refactoring the conditional compilation in `restclientchannelstests.swift` for `art...
Learnt from: maratal
PR: ably/ably-cocoa#1973
File: Test/Tests/RestClientChannelsTests.swift:5-18
Timestamp: 2024-09-17T13:25:42.060Z
Learning: Avoid suggesting refactoring the conditional compilation in `RestClientChannelsTests.swift` for `ARTRestChannels` extensions, as the user prefers to keep the current structure.

Applied to files:

  • Test/Tests/RealtimeAnnotationsTests.swift
  • Source/ARTRestAnnotations.m
  • Test/Tests/RestAnnotationsTests.swift
📚 Learning: the unpublish method in artrealtimeannotations works by publishing an annotation with a delete actio...
Learnt from: maratal
PR: ably/ably-cocoa#2066
File: Source/include/Ably/ARTRealtimeAnnotations.h:25-28
Timestamp: 2025-06-16T14:18:14.569Z
Learning: The unpublish method in ARTRealtimeAnnotations works by publishing an annotation with a DELETE action, rather than directly deleting. This is why the documentation correctly refers to "The annotation to publish" even though it's an unpublish method.

Applied to files:

  • Test/Tests/RealtimeAnnotationsTests.swift
  • Source/ARTRestAnnotations.m
  • Test/Tests/RestAnnotationsTests.swift
📚 Learning: `artchanneloptions` is already marked with `ns_swift_sendable`....
Learnt from: maratal
PR: ably/ably-cocoa#1973
File: Source/include/Ably/ARTRealtimeChannelOptions.h:33-33
Timestamp: 2024-10-08T15:37:26.766Z
Learning: `ARTChannelOptions` is already marked with `NS_SWIFT_SENDABLE`.

Applied to files:

  • Test/Tests/RealtimeAnnotationsTests.swift
  • Source/ARTRestAnnotations.m
  • Test/Tests/RestAnnotationsTests.swift
📚 Learning: in the ably cocoa sdk, when decoding annotation actions from integers, unknown/invalid action values...
Learnt from: maratal
PR: ably/ably-cocoa#2066
File: Source/ARTJsonLikeEncoder.m:341-351
Timestamp: 2025-06-16T15:39:16.005Z
Learning: In the Ably Cocoa SDK, when decoding annotation actions from integers, unknown/invalid action values should default to ARTAnnotationCreate rather than failing or using an unknown state. This is an intentional design decision for the annotation system.

Applied to files:

  • Test/Tests/RealtimeAnnotationsTests.swift
  • Source/ARTRestAnnotations.m
  • Test/Tests/RestAnnotationsTests.swift
📚 Learning: in `artmessage`, the `action` and `serial` fields are set by the realtime service and should not be ...
Learnt from: maratal
PR: ably/ably-cocoa#1998
File: Source/ARTJsonLikeEncoder.m:278-279
Timestamp: 2024-11-22T10:11:05.305Z
Learning: In `ARTMessage`, the `action` and `serial` fields are set by the realtime service and should not be serialized in the `messageToDictionary:` method in `Source/ARTJsonLikeEncoder.m`.

Applied to files:

  • Source/ARTRestAnnotations.m
📚 Learning: in `artrealtimechannelinternal`, the `internalpresence` property is declared in the private header `...
Learnt from: maratal
PR: ably/ably-cocoa#1995
File: Source/ARTRealtimeChannel.m:227-227
Timestamp: 2024-11-04T23:10:23.027Z
Learning: In `ARTRealtimeChannelInternal`, the `internalPresence` property is declared in the private header `ARTRealtimeChannel+Private.h`, so it does not need to be redeclared in `ARTRealtimeChannel.m`.

Applied to files:

  • Source/ARTRestAnnotations.m
📚 Learning: in 'source/artjsonlikeencoder.m', when handling messages from 'input', using `mutablecopy` on the ar...
Learnt from: maratal
PR: ably/ably-cocoa#1998
File: Source/ARTJsonLikeEncoder.m:766-774
Timestamp: 2024-11-26T17:50:25.730Z
Learning: In 'Source/ARTJsonLikeEncoder.m', when handling messages from 'input', using `mutableCopy` on the array ensures that modifications to `messages` do not affect the original `input`.

Applied to files:

  • Source/ARTRestAnnotations.m
📚 Learning: parsing the message's `id` is necessary for checking `msgserial` and `index` in the `member:isnewert...
Learnt from: maratal
PR: ably/ably-cocoa#1982
File: Source/ARTPresenceMessage.m:59-59
Timestamp: 2024-10-06T12:27:40.858Z
Learning: Parsing the message's `id` is necessary for checking `msgSerial` and `index` in the `member:isNewerThan:` method.

Applied to files:

  • Source/ARTRestAnnotations.m
📚 Learning: as a general rule, `ns_swift_sendable` annotations are not needed for private objective-c interfaces...
Learnt from: maratal
PR: ably/ably-cocoa#1973
File: Source/include/Ably/ARTBaseQuery.h:4-8
Timestamp: 2024-09-12T20:12:29.428Z
Learning: As a general rule, `NS_SWIFT_SENDABLE` annotations are not needed for private Objective-C interfaces in the ably-cocoa project.

Applied to files:

  • Test/Tests/RestAnnotationsTests.swift
🪛 SwiftLint (0.57.0)
Test/Tests/RealtimeAnnotationsTests.swift

[Warning] 101-101: Unused parameter in a closure should be replaced with _

(unused_closure_parameter)

Test/Tests/RestAnnotationsTests.swift

[Warning] 106-106: Unused parameter in a closure should be replaced with _

(unused_closure_parameter)


[Warning] 256-256: Unused parameter in a closure should be replaced with _

(unused_closure_parameter)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: check (tvOS, test_tvOS18_4)
  • GitHub Check: check (macOS, test_macOS)
  • GitHub Check: check
  • GitHub Check: build

@coderabbitai
Copy link

coderabbitai bot commented Aug 3, 2025

✅ Actions performed

Reviews paused.

@maratal maratal force-pushed the feature/2077-annotations branch from 966de7f to 62f61a8 Compare August 4, 2025 08:52
@github-actions github-actions bot temporarily deployed to staging/pull/2080/features August 4, 2025 08:53 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/2080/jazzydoc August 4, 2025 08:56 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/2080/features August 9, 2025 23:11 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/2080/jazzydoc August 9, 2025 23:13 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/2080/features August 9, 2025 23:37 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/2080/jazzydoc August 9, 2025 23:42 Inactive
@maratal maratal force-pushed the feature/2077-annotations branch from a6b3107 to 3f0ce6a Compare August 9, 2025 23:44
@github-actions github-actions bot temporarily deployed to staging/pull/2080/features August 9, 2025 23:45 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/2080/jazzydoc August 9, 2025 23:48 Inactive
@maratal maratal force-pushed the feature/2077-annotations branch from 3f0ce6a to 953996e Compare August 10, 2025 00:20
@github-actions github-actions bot temporarily deployed to staging/pull/2080/features August 10, 2025 00:21 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/2080/jazzydoc August 10, 2025 00:24 Inactive
@maratal maratal force-pushed the feature/2077-annotations branch from 953996e to 1512bda Compare August 10, 2025 00:27
@github-actions github-actions bot temporarily deployed to staging/pull/2080/features August 10, 2025 00:28 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/2080/jazzydoc August 10, 2025 00:32 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/2080/features October 15, 2025 16:15 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/2080/jazzydoc October 15, 2025 16:20 Inactive
@maratal maratal force-pushed the feature/2077-annotations branch from ce2f665 to 2b25fa2 Compare October 15, 2025 22:49
@github-actions github-actions bot temporarily deployed to staging/pull/2080/features October 15, 2025 22:49 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/2080/jazzydoc October 15, 2025 22:52 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/2080/features October 15, 2025 23:08 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/2080/jazzydoc October 15, 2025 23:12 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/2080/features October 15, 2025 23:44 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/2080/jazzydoc October 15, 2025 23:47 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/2080/features October 16, 2025 01:46 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/2080/jazzydoc October 16, 2025 01:51 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/2080/features October 16, 2025 10:59 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/2080/jazzydoc October 16, 2025 11:03 Inactive
maratal and others added 4 commits October 16, 2025 10:13
…plaintext_and_HTML_format (changed html content).
- Rest annotations
- Publishing realtime annotations
- Tests

Co-authored-by: Lawrence Forooghian <lawrence@forooghian.com>
@lawrence-forooghian
Copy link
Collaborator

I've addressed my feedback here and sorted out the commits. I haven't looked at the tests in any detail, please take another look at them to convince yourself that they are correct and sufficient

@lawrence-forooghian lawrence-forooghian merged commit 8507248 into main Oct 16, 2025
11 of 12 checks passed
@lawrence-forooghian lawrence-forooghian deleted the feature/2077-annotations branch October 16, 2025 14:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

Complete annotations implementation

3 participants