Developing Mentions System in Mobile App
@mentions more complex than hashtags: besides parsing and highlighting, need real user search on input, handle username changes (if user renames — mention points to same person), and push notification to addressee. Each adds complexity.
User Autocomplete
Scenario: user types @al — show matching names list. Cursor tracking logic similar to hashtags: find @ before cursor.
User Search
GET /users/search?q=al&limit=10 — search by username and display_name. On DB side — WHERE username ILIKE 'al%' OR display_name ILIKE 'al%' with GIN-index pg_trgm for fuzzy search. Debounce 150-200ms on client.
Prioritize results: first mutual followers, then everyone. JOIN with follows to sort by is_mutual.
UI Dropdown on iOS
func textViewDidChange(_ textView: UITextView) {
guard let mentionQuery = extractMentionQuery(textView) else {
hideMentionSuggestions()
return
}
searchUsers(query: mentionQuery)
.debounce(for: .milliseconds(200), scheduler: RunLoop.main)
.sink { [weak self] users in
self?.showMentionSuggestions(users)
}
.store(in: &cancellables)
}
func extractMentionQuery(_ textView: UITextView) -> String? {
let text = textView.text as NSString
let cursorPosition = textView.selectedRange.location
// Find @ before cursor
let searchRange = NSRange(location: 0, length: cursorPosition)
let pattern = "@([\\w\\.]{0,30})$"
guard let match = try? NSRegularExpression(pattern: pattern)
.firstMatch(in: textView.text, range: searchRange) else { return nil }
return (text.substring(with: match.range(at: 1)))
}
On user select from list — insert @username as single token (replace partially typed text). Token doesn't break on edit — deleting one character deletes entire @username. On iOS — via NSAttributedString with custom attribute and shouldChangeTextIn intercept.
On Compose — TextFieldValue with AnnotatedString, value change intercept via onValueChange.
Mentions Storage
Problem: storing @username as text is bad — if user renames, mention breaks. Correctly store mention as pair (user_id, display_username_at_time):
- In post body:
"Hi @[user:42|alex]! How are you?"— custom syntax withuser_id. - On display: fetch current username of user 42 from DB, show clickable.
- If user deleted account — show
@deleted_accountgray.
Display-side parsing adds load — cache display_name by user_id in memory during session.
Notifications
On post or comment publish with mention — parse user_id from tokens, create records in notifications (recipient_id, type='mention', source_post_id, actor_id), send push via FCM/APNs.
Batching: if one post mentions 5 people — 5 separate notifications (each gets their own). If user mentioned several times per minute by different people — one notification "3 mentions."
User can disable mention notifications in settings — notify_mentions flag in profile.
Profile on Mention Tap
Tap on @username → open user profile. If user blocked or blocked me — show stub "Profile unavailable." Implementation via UITextViewDelegate.textView(_:shouldInteractWith:in:interaction:) (iOS) or ClickableText with LocalUriHandler (Compose).
Timeline
Parse mentions and highlight in existing text — 1 day. Autocomplete on input with user search — 1-2 days. Notifications — another 1 day. Full system — 2-3 business days. Cost calculated individually.







