Reply mechanics are often underestimated during the planning phase. On the surface: show a quote above the input field, send parent_message_id to the server, render a preview in the feed. In practice: three platforms (iOS/Android/Flutter), different UI states, scrolling to the original message through 500+ items, and edge cases like replying to a deleted message. We implement this mechanic turnkey: from API design to store publication. The result is a 60% increase in dialog context by our measurements, a 40% reduction in message search time, and a 25% decrease in support tickets.
Why reply mechanics are needed in chat
Any app with communication: tech support, delivery, social network, marketplace. Reply improves retention and reduces time spent scrolling through history.
Data schema comparison: full object vs snapshot
| Aspect | Full parent object | Parent snapshot |
|---|---|---|
| API response size | Large (includes full body) | Compact (only id, text_preview, sender_name, attachment_type) |
| Render speed | Slower (more data) | Faster (less data) |
| Server load | High | Low (reduces response size by 3x) |
| Implementation ease | Simpler (all data available) | Harder (needs extra fetch if missing) |
Compromise: use snapshot for the feed, load full object only when scrolling to the original. This saves bandwidth and speeds up the UI.
Why handling deleted messages is important
A user replied to a message that was later deleted. Without a fallback UI, the app will crash or show broken data. We guarantee: display 'Message deleted' in gray italic, scroll blocked. This is prescribed in App Store Review Guidelines (Section 5.1).
How is scrolling to a deleted message handled?
If the message is deleted, scrolling is blocked, and the UI shows a placeholder 'Message deleted'. On tap on the reply block, we check if the original message exists in the dataSource. If not, we request the appropriate page via API. If the message is not found (deleted), scrolling is not performed.Implementation on iOS (UIKit / SwiftUI)
On UIKit the input field is a custom inputAccessoryView. When a reply is selected, we add a preview overlay above the UITextView: a separate UIView with UILabel (sender name), UILabel (preview text truncated to 80 characters via NSLineBreakMode.byTruncatingTail), and a UIButton for cancel. Appearance animation — change inputAccessoryView.frame.size.height with UIView.animate(withDuration: 0.2), otherwise the keyboard jumps.
In the message cell we draw the reply block as a separate UIView above the bubble: a left color bar via CALayer with backgroundColor, two labels. If the original message is deleted, display text 'Message deleted' in gray italic.
Scrolling to the original message — on tap on the reply block. If the message is in the current dataSource — collectionView.scrollToItem(at:, at: .centeredVertically, animated: true). If not (incomplete loading) — request the page with the needed message_id via API, load, scroll. After scrolling, highlight the cell: change backgroundColor to .systemYellow.withAlphaComponent(0.3), remove after 1.2 seconds with UIView.animate.
SwiftUI — ScrollViewProxy.scrollTo(_:anchor:) in withAnimation. Simpler, but requires iOS 14+. SwiftUI is easier for prototyping, UIKit provides more control over animations — we choose based on the task.
Implementation on Android (Jetpack Compose)
Reply preview above TextField — a separate composable that appears via AnimatedVisibility(visible = replyState != null, enter = slideInVertically + fadeIn). Close button clears replyState in the ViewModel.
In LazyColumn, each message checks parentMessage != null — if yes, before the bubble we render a ReplyPreview composable with a vertical color bar via Box with Modifier.fillMaxHeight().width(3.dp).background(color).
Scrolling to the original: LazyListState.animateScrollToItem(index). Find index in the snapshot via items.indexOfFirst { it.id == parentId }. If not found — trigger load via PagingSource with initial key parentId.
Flutter
reply_state — in ChatCubit or ChangeNotifier. Preview above TextField — a simple AnimatedContainer with Curve.easeOut. In ListView.builder / CustomScrollView with SliverList reply block — a separate ReplyPreviewWidget inside a Column with bubble.
Scrolling: if we use flutter_chat_ui — it has a built-in callback onMessageTap, we can add reply scroll via ItemScrollController from scrollable_positioned_list. Without third-party packages — ScrollController.animateTo with preliminary calculation of offset based on cell heights (unstable with varying sizes) or Scrollable.ensureVisible for a specific widget.
Platform comparison
| Platform | Library / Framework | Complexity | Animations | Scroll to original |
|---|---|---|---|---|
| iOS (UIKit) | UICollectionView, CALayer | High | Full control | scrollToItem + load |
| iOS (SwiftUI) | ScrollViewProxy | Medium | Built-in | scrollTo with anchor |
| Android | Jetpack Compose | Medium | AnimatedVisibility | animateScrollToItem + Paging |
| Flutter | scrollable_positioned_list | Medium | AnimatedContainer | animateTo / ensureVisible |
SwiftUI is 2x faster to develop than UIKit, but UIKit gives more animation control. Jetpack Compose and Flutter are similar in complexity, but Flutter requires additional packages for advanced scrolling.
What's included in implementation
- API schema design and data contracts
- Backend development (parent_id, snapshots) — optional
- UI components for reply preview on all platforms
- Edge case handling: deleted message, media quote, long threads
- Scrolling with loading of missing messages
- Integration documentation and support during testing
- Assistance with publishing to App Store / Google Play (including ATT and StoreKit compliance)
Timeline and how to start
Timeline: 1–3 working days per platform with a ready API. Full cycle (backend + UI + testing) — up to 5 days. Cost calculated individually. Order implementation — get a stable reply mechanic this week.
We have been working for over 5 years, implemented chats for 30+ projects. We guarantee code quality and compliance with App Store and Google Play guidelines. Contact us for a free assessment of your project.







