Developing Reels (Short Videos) in a Mobile App
Reels — vertical video feed with autoplay on scroll. Technically one of the most complex UI patterns in mobile development: smooth paging scroll, video preloading, player with synchronized sound, infinite feed with pagination. Each component can be implemented to work without delays — or destroy UX.
Vertical Paging Scroll
Foundation of Reels — UICollectionView with UICollectionViewCompositionalLayout + UICollectionLayoutSectionOrthogonalScrollingBehavior.paging (iOS) or RecyclerView with PagerSnapHelper (Android). Each element fills entire screen (bounds.size).
On iOS pagination scroll via UICollectionView with isPagingEnabled = true — works, but doesn't control deceleration. Better: custom UICollectionViewFlowLayout with overridden targetContentOffset(forProposedContentOffset:withScrollingVelocity:) that snaps to nearest video with proper animation.
Velocity-based switching: fast swipe switches to next Reel regardless of scroll distance. sqrt(velocity.x² + velocity.y²) > threshold → snap to next element.
Player and Video Switching
Critical decision: one shared AVPlayer or player per cell?
One shared AVPlayer with AVPlayerItem reassignment during scroll — standard for Reels. On scrollViewDidEndDecelerating take player from previous cell and assign to current. AVPlayerLayer.player = avPlayer — instant. Player not recreated — switch without pauses.
On Android — one ExoPlayer for entire activity/fragment. PlayerView.player = exoPlayer reassigns. Use MediaItem with preload: exoPlayer.addMediaItem(nextMediaItem) for next video — ExoPlayer starts buffering before actual switch.
Preloading: keep in memory AVPlayerItem for current, next and previous video. Creating AVPlayerItem from URL takes time — do this when starting scroll to neighbor element, not after completion.
func collectionView(_ collectionView: UICollectionView,
willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
// Preload AVPlayerItem for this index
let item = AVPlayerItem(url: videoURLs[indexPath.row])
playerItemCache[indexPath.row] = item
}
Short Video Recording
Camera for recording Reels — custom AVCaptureSession. AVCaptureVideoPreviewLayer displays live preview fullscreen. Recording via AVCaptureMovieFileOutput with limit: maxRecordedDuration = CMTime(seconds: 60, preferredTimescale: 600).
Multi-segment recording (several short clips in one Reel) — create separate AVAsset for each recording, combine at end via AVMutableComposition. AVMutableCompositionTrack.insertTimeRange() inserts segments sequentially.
Real-time filters — Core Image with CIFilter apply to CMSampleBuffer in delegate method captureOutput(_:didOutput:from:). Render via CIContext with Metal (CIContext(mtlDevice: MTLCreateSystemDefaultDevice()!)). OpenGL for this task in 2025 — outdated with worse performance.
Audio, Text, Effects
Audio track — own music from media library via MPMediaPickerController (iOS) or ACTION_PICK with MediaStore.Audio (Android), or app catalog tracks. Overlay audio on video via AVMutableComposition with two tracks: video + audio.
Text over video — not UILabel over UIImageView, but bake into video via AVVideoCompositionCoreAnimationTool + CATextLayer. Gives correct text rendering on export and publishing.
Feed Performance
UICollectionView.prefetchDataSource on iOS — request metadata for next 3 videos on scroll. RecyclerView.setRecycledViewPool() on Android — shared view pool for reuse, reduces inflate operations.
Memory: store decoded AVPlayerItem maximum for 5 neighboring videos. On further scroll — release AVPlayerItem.asset explicitly. Without this viewing 20+ Reels memory grows to 300–500 MB and iOS sends memory warning.
| Component | Complexity | Approx Time |
|---|---|---|
| Vertical paging + shared player | High | 2–3 days |
| Preloading + cache | Medium | 1–2 days |
| Recording + multi-segments | High | 3–4 days |
| Real-time filters (Core Image) | High | 2–3 days |
| Text + audio in export | Medium | 2 days |
Timeline
Feed viewing with autoplay, preloading and pagination — 1–2 weeks. Full cycle (recording, editing, filters, publishing, feed) — 3–6 weeks depending on feature set. Cost calculated after requirements analysis.







