Developing Algorithmic Recommendation Feed in a Mobile App
Algorithmic feed — not a date-sorted post list in reverse order. This is a ranking system that decides in real-time: what content to show specific user at specific moment. TikTok, Instagram Reels, YouTube Shorts follow one principle: maximize engagement time via predicted engagement score for each content unit.
Architecture: Candidate Generation → Ranking → Serving
Algorithmic feed works in two stages, mobile app critically depends on both.
Candidate generation — from millions of content units select hundreds of candidates for specific user. Usually lightweight model (Approximate Nearest Neighbor by user embedding) or rule set: followed users, trending in geo, topic affinity. This stage must fit 50–100ms.
Ranking — candidates ranked by heavy model predicting interaction probability (like, share, comment, completion rate). Gradient boosted trees (XGBoost, LightGBM), two-tower neural networks or DLRM. Result — ordered list with scores.
Serving — mobile app requests N next feed elements, gets them with pre-calculated order. Prefetch next page before user reaches current page end.
Ranking Signals: What to Collect in App
Algorithm quality depends on signal quality. Mobile app — main source:
Engagement signals:
-
like,share,comment,save— explicit signals with high weight -
video_completion_rate— did user watch video to end, where did they exit -
dwell_time— time on card/post, but not full time (other app may be open) -
swipe_away_velocity— fast swipe down without stop is negative signal
Implicit negative signals:
- User scrolled past in < 0.5 seconds — probable skip
- Report / Hide content — strong negative signal
- App backgrounded right after content show
Video completion tracking on iOS with AVPlayer:
class VideoProgressTracker {
private var timeObserver: Any?
private let player: AVPlayer
private let itemId: String
private var maxProgress: Float = 0
init(player: AVPlayer, itemId: String) {
self.player = player
self.itemId = itemId
setupObserver()
}
private func setupObserver() {
let interval = CMTime(seconds: 0.5, preferredTimescale: CMTimeScale(NSEC_PER_SEC))
timeObserver = player.addPeriodicTimeObserver(forInterval: interval, queue: .main) { [weak self] time in
guard let self,
let duration = self.player.currentItem?.duration.seconds,
duration > 0 else { return }
let progress = Float(time.seconds / duration)
if progress > self.maxProgress {
self.maxProgress = progress
}
}
}
func reportCompletion() {
Analytics.track(.videoProgress(itemId: itemId,
completionRate: maxProgress,
source: .algorithmicFeed))
}
}
On Android — ExoPlayer with AnalyticsListener.onPlaybackStateChanged() and Player.Listener.onPositionDiscontinuity().
Prefetch and Infinite Feed
User shouldn't see loader on scroll. Standard: load next page when user reaches second-to-last element.
// Android, RecyclerView + ViewModel
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
val layoutManager = recyclerView.layoutManager as LinearLayoutManager
val lastVisible = layoutManager.findLastVisibleItemPosition()
val total = layoutManager.itemCount
if (total - lastVisible <= PREFETCH_THRESHOLD) {
viewModel.loadNextPage()
}
}
})
PREFETCH_THRESHOLD — usually 3–5 elements. For video feed increase to 7–10 because video load takes longer.
Deduplication: server may return same item in two paginated responses. Client stores Set<String> of shown IDs and filters duplicates before adding to list.
Contextual Signals from Device
Time of day, day of week, network type (WiFi vs cellular), battery level — all context improving ranking. On iOS — CTTelephonyNetworkInfo for network type, UIDevice.current.batteryLevel for charge. Don't send these with every request — enough on session open.
Explainability: Why This Content
Users want understanding why feed shows what shows. Minimum — tag "Because you liked X" or "Popular in your region." This is both UX and trust. Server returns explanation_key with content, mobile app renders matching label.
A/B Testing Algorithm
New ranking model version not rolled to all immediately. Typical scheme: 5% traffic → 20% → 50% → 100%, monitoring session metrics (D1/D7 retention, avg time in app, engagement rate) at each step.
Feature flags managed via Firebase Remote Config or custom system. Client sends experiment_variant in each feed API request — allows server to choose right ranker.
Workflow
Audit current tracking: what already collected, how accurate view data.
Design event schema for feed: completion rate, dwell time, explicit signals.
Develop client side: infinite scroll, prefetch, deduplication, video player with tracking.
Integrate with feed API: paginated load, error handling, offline fallback (cached feed).
Implement explanation labels and UI control elements (hide content, not interested).
Set up monitoring and A/B infrastructure.
Timeline Estimates
Client side with proper tracking for existing feed API — 2–3 weeks. Full system with server ranker, training pipeline and mobile integration — 2–3 months. Cost depends on content volume, required latency and existing analytics infrastructure.







