Developing Reels (Short Videos) in a Mobile App
Client complains: the vertical video feed lags on iPhone 8 with old iOS — switching between clips takes a second, audio is out of sync, and fast swipes crash the app due to memory pressure. Sound familiar? We solve it: we implement smooth paging scroll using a shared player, preload videos three positions ahead, and cache AVPlayerItem for five neighboring clips — all delivering stable 60 fps even on devices with 2 GB RAM.
Vertical video feeds are technically one of the most challenging UI patterns: they require audio-video synchronization, minimized scroll latency, and proper memory management. Our team has built over 50 video feed projects using Swift 5.9+, Kotlin 1.9+, Combine, and Coroutines. Our engineers are Apple and Google certified, with deep experience in App Store Review Guidelines (Section 4.2, 5.1) and Google Play Console. The key solution: using a single instance of AVPlayer (iOS) or ExoPlayer (Android) reassigned across cells. This eliminates player initialization delays and reduces GPU load. Preloading AVPlayerItem three videos ahead cuts switch time to 80 ms. Contact us for a free estimate.
Device technical requirements
- iOS 14+, Android 7.0+
- Minimum 2 GB RAM
- Support for VideoToolbox (iOS) / MediaCodec (Android)
Vertical Paging Scroll
The foundation of Reels is UICollectionView with UICollectionViewCompositionalLayout + UICollectionLayoutSectionOrthogonalScrollingBehavior.paging (iOS) or RecyclerView with PagerSnapHelper (Android). Each cell fills the entire screen (bounds.size).
On iOS, paging scroll via UICollectionView with isPagingEnabled = true works but lacks control over deceleration. Better: a custom UICollectionViewFlowLayout overriding targetContentOffset(forProposedContentOffset:withScrollingVelocity:) that snaps to the nearest video with desired animation.
Velocity-based switching: a fast swipe switches to the next Reel regardless of scroll distance. sqrt(velocity.x² + velocity.y²) > threshold → snap to the next cell.
How to Switch Videos Without Delay?
The critical decision: one shared AVPlayer with AVPlayerItem reassignment during scroll. In scrollViewDidEndDecelerating, we take the player from the previous cell and assign it to the current one. AVPlayerLayer.player = avPlayer — instant. The player is never recreated — switching occurs without pauses.
On Android — one ExoPlayer for the entire activity/fragment. PlayerView.player = exoPlayer reassigns. We use MediaItem with preload: exoPlayer.addMediaItem(nextMediaItem) for the next video — ExoPlayer starts buffering before actual switch. ExoPlayer with preloading is 4x faster than standard MediaPlayer.
Preloading: keep in memory AVPlayerItem for current, next, and previous videos. Creating AVPlayerItem from URL takes time — we do it at scroll start toward a neighbor cell, not on 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 } According to Apple AVFoundation Guide, this approach minimizes delays.
How to Implement Multi-Segment Recording?
The camera for recording Reels is a custom AVCaptureSession. AVCaptureVideoPreviewLayer shows live preview fullscreen. Recording via AVCaptureMovieFileOutput with limit: maxRecordedDuration = CMTime(seconds: 60, preferredTimescale: 600).
Multi-segment recording (several short clips into one Reel) — create a separate AVAsset for each take, then merge via AVMutableComposition. AVMutableCompositionTrack.insertTimeRange() inserts segments sequentially.
Real-time filters — Core Image with CIFilter applied to CMSampleBuffer in delegate method captureOutput(_:didOutput:from:). Render via CIContext with Metal (CIContext(mtlDevice: MTLCreateSystemDefaultDevice()!)). OpenGL for this task is outdated with worse performance.
Sound, Text, Effects
Audio track — user's music from media library via MPMediaPickerController (iOS) or ACTION_PICK with MediaStore.Audio (Android), or in-app catalog tracks. Lay audio over video using AVMutableComposition with two tracks: video + audio.
Text overlay — not UILabel on top of UIImageView, but baked into video via AVVideoCompositionCoreAnimationTool with CATextLayer. This ensures correct text rendering during export and publication.
Feed Performance
UICollectionView.prefetchDataSource on iOS — request metadata of next 3 videos during scroll. RecyclerView.setRecycledViewPool() on Android — common view pool for reuse, reducing inflate operations.
Memory: store decoded AVPlayerItem for at most 5 neighboring videos. On scroll further, release AVPlayerItem.asset explicitly. Without this, watching 20+ Reels causes memory growth to 300–500 MB and iOS fires memory warning. Our engineers are Apple and Google certified, guaranteeing smooth operation even on devices with 2 GB RAM.
| Component | Complexity | Estimated Time |
|---|---|---|
| Vertical paging + shared player | High | 2–3 days |
| Preloading + caching | Medium | 1–2 days |
| Recording + multi-segment | High | 3–4 days |
| Real-time filters (Core Image) | High | 2–3 days |
| Text + audio in export | Medium | 2 days |
| Metric | iOS | Android |
|---|---|---|
| Scroll smoothness (fps) | 60 | 60 |
| Switch latency | <100ms | <120ms |
| Memory usage (5 videos) | ~150 MB | ~180 MB |
What's Included
- Architecture and API documentation.
- Source code with comments.
- CI/CD setup and store access (App Store Connect, Google Play Console).
- Client team training (2–3 sessions).
- 30 days of post-release support.
Timeline and Cost
Viewing feed with auto-play, preloading, and paging — 1–2 weeks. Full cycle (recording, editing, filters, publishing, feed) — 3–6 weeks. Cost is calculated individually based on integration complexity and required features. Contact us for a project estimate.
Step-by-Step Guide: How to Implement a Shared Player
- Create a single
AVPlayer/ExoPlayerin your Activity/ViewController. - In each cell, use
playerLayer.player = player/PlayerView.player = player. - In
scrollViewDidEndDecelerating, assign the player to the now-visible cell. - For preloading, implement prefetchDataSource (iOS) or addMediaItem (Android).
- Test on devices with 2 GB RAM.







