Improve example app on macOS and tvOS#448
Conversation
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.
WalkthroughRenamed Environment.live associated value from Changes
Sequence DiagramsequenceDiagram
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 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 |
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.
ced706b to
23ee66d
Compare
Restores the content that was deleted in 7a7c55b, and adds a bit more.
23ee66d to
062f167
Compare
There was a problem hiding this comment.
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
.onChangecan 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.
📒 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.swiftExample/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.swiftExample/AblyChatExample/ContentView.swiftExample/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,
releaseconfiguration, 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 toclientIDis consistent; options wiring looks correct.Associated value rename and assignment to
realtimeOptions.clientIdare 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
subscriptionat line 280 is not discarded unused; it's explicitly retained and used at line 318 withsubscription.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.,subscribeToReactionsat line 326) demonstrate that listeners work without explicit storage, confirming the SDK's auto-management. The code is correct as-is.
I've also added a README for the example app.
Summary by CodeRabbit
New Features
Improvements
Documentation