Skip to content

Improve example app on macOS and tvOS#448

Merged
lawrence-forooghian merged 5 commits intomainfrom
fix-example-app-mac
Oct 22, 2025
Merged

Improve example app on macOS and tvOS#448
lawrence-forooghian merged 5 commits intomainfrom
fix-example-app-mac

Conversation

@lawrence-forooghian
Copy link
Copy Markdown
Collaborator

@lawrence-forooghian lawrence-forooghian commented Oct 22, 2025

  • fix the message reaction / edit buttons on macOS and tvOS
  • send message when user presses Enter
  • disable text input on tvOS

I've also added a README for the example app.

Summary by CodeRabbit

  • New Features

    • Added loading state for message history display
    • Introduced enhanced message input interface with typing detection
    • Added platform-specific UI adjustments for tvOS compatibility
  • Improvements

    • Improved message scroll anchoring behavior
    • Refined message ordering and history loading flow
  • Documentation

    • Added comprehensive README for the example application with setup instructions and feature overview

It seems that the `flip` modifier that we were using to keep the scroll
view anchored to the bottom was causing issues with message reaction
buttons and the edit/delete button on these platforms. Switch to using
defaultScrollAnchor (Claude's suggestion; my SwiftUI knowledge is not
good enough to properly evaluate if this is the best approach) instead.
Seems to work well enough, though.
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Oct 22, 2025

Walkthrough

Renamed Environment.live associated value from clientId to clientID, propagated it to ARTRealtimeOptions, refactored ContentView to add a loading state for history, replaced List with ScrollView/VStack, added a new input area with typing detection and send behavior (excluded on tvOS), and conditionally omitted the Edit button on tvOS. README added.

Changes

Cohort / File(s) Summary
ContentView & Environment signature
Example/AblyChatExample/ContentView.swift
Changed Environment.live associated value from clientId to clientID; updated pattern matches and assignments (realtimeOptions.clientId = clientID). Replaced List-based UI with isLoadingHistory gating, ScrollView+VStack rendering, history prepend/append handling, removed flip() usage, and added input area with typing detection, Enter-to-send, and platform-specific send styling (input excluded on tvOS).
Menu button tvOS conditional
Example/AblyChatExample/MessageViews/MenuButtonView.swift
Wrapped the Edit button/actions in #if !os(tvOS) conditional compilation so Edit is omitted on tvOS; Delete action unchanged.
Documentation
Example/README.md
Added README describing example app features (send/edit/delete messages, reactions, history loading), run instructions (AblyChat.xcworkspace / AblyChatExample), switching Environment.current between mock/live, supported OS versions, and tvOS input note.

Sequence Diagram

sequenceDiagram
    participant User
    participant UI as ContentView
    participant Input as Input Handler
    participant SDK as Chat SDK

    User->>UI: Launch app
    activate UI
    UI->>UI: Set isLoadingHistory = true
    UI-->>User: Show loading indicator
    deactivate UI

    UI->>SDK: Request message history
    activate SDK
    SDK-->>UI: Return previousMessages
    deactivate SDK

    activate UI
    UI->>UI: Prepend previousMessages to items
    UI->>UI: Set isLoadingHistory = false
    UI-->>User: Render ScrollView (anchored bottom)
    deactivate UI

    User->>Input: Type message
    activate Input
    Input->>SDK: Send typing notification (startTyping)
    Input->>Input: Update text state
    deactivate Input

    User->>Input: Press Enter or tap Send
    activate Input
    Input->>SDK: Send message
    SDK-->>UI: New message event
    deactivate Input

    activate UI
    UI->>UI: Append new message to items
    UI->>UI: Scroll to bottom anchor
    deactivate UI
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I burrowed through lists and gave them a nudge,

Swapped flips for a ScrollView and trimmed a small grudge.
clientID now hops where the old name once stood,
History loads in order — tidy and good.
Tap Enter, send joy — from this rabbit, with love.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The pull request title "Improve example app on macOS and tvOS" is directly related to the changeset, which includes multiple improvements to the example app with platform-specific considerations. The changes encompass UI refactoring (List to ScrollView, loading states), input handling improvements (Enter to send, typing detection), conditional compilation for tvOS (disabling text input, hiding Edit button), and documentation via a new README. While the title is somewhat generic and doesn't specify the exact nature of the improvements, it accurately reflects the main objective of enhancing the example app specifically for macOS and tvOS compatibility, making it clear and relevant to the changes.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix-example-app-mac

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/448/AblyChat October 22, 2025 11:29 Inactive
It's not a very good experience (at least on the simulator) and probably
doesn't reflect the kinds of UX that users will be building on tvOS.

The tvOS experience could still be improved (e.g. the message reaction
buttons are tiny) but it'll do for now.
@lawrence-forooghian lawrence-forooghian marked this pull request as ready for review October 22, 2025 11:36
@github-actions github-actions bot temporarily deployed to staging/pull/448/AblyChat October 22, 2025 11:37 Inactive
Restores the content that was deleted in 7a7c55b, and adds a bit more.
Copy link
Copy Markdown

@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: 1

🧹 Nitpick comments (4)
Example/AblyChatExample/MessageViews/MenuButtonView.swift (1)

18-20: Add an accessibility label to the icon-only menu button.

Improve VoiceOver by labeling the ellipsis icon.

         } label: {
-            Image(systemName: "ellipsis.circle")
+            Image(systemName: "ellipsis.circle")
+                .accessibilityLabel("More options")
         }
Example/README.md (2)

15-19: Tighten phrasing.

Prefer concise wording.

-In order to allow the app to use modern SwiftUI features, it supports the following OS versions:
+To use modern SwiftUI features, the app supports the following OS versions:

13-14: Call out secret handling.

Add a brief warning to avoid committing API keys and suggest using an .xcconfig.

 By default, the example app uses a mock implementation of the Chat SDK. To switch to using the real SDK, change the `Environment.current` variable in `ContentView.swift` to `.live` and supply your Ably API key and a `clientID`.
+
+> Important: Never commit real API keys. Prefer storing them in an untracked `.xcconfig` and referencing them via build settings.
Example/AblyChatExample/ContentView.swift (1)

173-184: Throttle typing events to reduce network chatter.

Current .onChange can spam keystroke calls on fast typing. Consider a small debounce.

- .onChange(of: newMessage) {
+ .onChange(of: newMessage) {
     // this ensures that typing events are sent only when the message is actually changed whilst editing
     if let index = listItems.firstIndex(where: { $0.id == editingItemID }) {
       ...
     } else {
-      startTyping()
+      debounceTyping()
     }
   }

Add:

@State private var typingTask: Task<Void, Never>?
func debounceTyping(delay: Duration = .milliseconds(400)) {
  typingTask?.cancel()
  typingTask = Task { @MainActor in
    try? await Task.sleep(for: delay)
    startTyping()
  }
}
📜 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.

📥 Commits

Reviewing files that changed from the base of the PR and between d7cc8ee and 062f167.

📒 Files selected for processing (3)
  • Example/AblyChatExample/ContentView.swift (7 hunks)
  • Example/AblyChatExample/MessageViews/MenuButtonView.swift (1 hunks)
  • Example/README.md (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.swift

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.swift: Use protocol-based design; expose SDK functionality via protocols and prefer associated types with opaque return types (some Protocol) instead of existentials (any Protocol)
Isolate all mutable state to the main actor; mark stateful objects with @mainactor
Public API must use typed throws with ErrorInfo; use InternalError internally and convert at the public API boundary
For public structs emitted by the API, provide an explicit public memberwise initializer
When using AsyncSequence operators in @mainactor contexts, mark operator closures as @sendable
Task, CheckedContinuation, and AsyncThrowingStream do not support typed errors; use Result and call .get() to surface typed errors
Do not use Dictionary.mapValues for typed throws; use ablyChat_mapValuesWithTypedThrow instead
When the compiler struggles with typed throws, explicitly declare the error type on do blocks (e.g., do throws(InternalError))
Specify error types in closures using the throws(ErrorType) syntax (e.g., try items.map { jsonValue throws(InternalError) in ... })
Mark any test-only APIs with testsOnly_ prefix and wrap them in #if DEBUG

Files:

  • Example/AblyChatExample/MessageViews/MenuButtonView.swift
  • Example/AblyChatExample/ContentView.swift
Example/**

📄 CodeRabbit inference engine (CLAUDE.md)

Maintain the example SwiftUI app under the Example/ directory alongside the workspace AblyChat.xcworkspace

Files:

  • Example/AblyChatExample/MessageViews/MenuButtonView.swift
  • Example/AblyChatExample/ContentView.swift
  • Example/README.md
🧬 Code graph analysis (1)
Example/AblyChatExample/ContentView.swift (3)
Sources/AblyChat/Subscription.swift (1)
  • historyBeforeSubscribe (94-104)
Example/AblyChatExample/Mocks/MockSubscriptionStorage.swift (1)
  • historyBeforeSubscribe (211-213)
Sources/AblyChat/Messages.swift (1)
  • historyBeforeSubscribe (447-449)
🪛 LanguageTool
Example/README.md

[style] ~14-~14: Consider a more concise word here.
Context: ...ly your Ably API key and a clientID. In order to allow the app to use modern SwiftUI fea...

(IN_ORDER_TO_PREMIUM)

⏰ 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: Xcode, iOS (Xcode 26.0)
  • GitHub Check: Example app, iOS (Xcode 26.0)
  • GitHub Check: Example app, macOS (Xcode 26.0)
  • GitHub Check: Xcode, macOS (Xcode 26.0)
  • GitHub Check: Xcode, tvOS (Xcode 26.0)
  • GitHub Check: Xcode, release configuration, iOS (Xcode 26.0)
🔇 Additional comments (5)
Example/AblyChatExample/MessageViews/MenuButtonView.swift (1)

9-13: Good platform guard for tvOS.

Omitting Edit on tvOS matches the PR goal and avoids non-functional UI there.

Example/AblyChatExample/ContentView.swift (4)

13-13: Rename to clientID is consistent; options wiring looks correct.

Associated value rename and assignment to realtimeOptions.clientId are aligned.

Also applies to: 22-26


118-169: History loading gate + bottom anchoring look good.

Nice UX: placeholder until history arrives; avoids defaultScrollAnchor glitches from bulk inserts.


170-213: tvOS input disabled as intended.

Conditional compilation cleanly removes input/edit UI on tvOS.


279-286: No retention issue—subscription is already captured and used.

The subscription at line 280 is not discarded unused; it's explicitly retained and used at line 318 with subscription.historyBeforeSubscribe(). Additionally, the Ably Chat Swift SDK automatically manages listener lifecycle, so you don't need to handle removing listeners. This is distinct from JavaScript and Kotlin SDKs. Other subscribe methods in the same file (e.g., subscribeToReactions at line 326) demonstrate that listeners work without explicit storage, confirming the SDK's auto-management. The code is correct as-is.

Comment thread Example/AblyChatExample/ContentView.swift
@lawrence-forooghian lawrence-forooghian merged commit a131892 into main Oct 22, 2025
31 of 33 checks passed
@lawrence-forooghian lawrence-forooghian deleted the fix-example-app-mac branch October 22, 2025 12:01
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.

2 participants