A social photo feed built around one goal: scrolling that never stutters, no matter how many images fly by.
![]() |
![]() |
![]() |
|---|
I built this to go deep on image-pipeline engineering in UIKit — the part of a photo app that actually decides whether it feels fast. Every photo is generated on-device by a seeded pattern renderer (no network, fully deterministic), which makes the interesting part — caching, de-duplication, prefetching, cancellation — easy to exercise and reason about.
- Performant feed —
UICollectionViewwith compositional layout and a diffable data source keyed by post ID; like/comment updates usereconfigureItemsinstead of reloads - Custom image pipeline — an
ImageLoaderactor combining anNSCache, in-flight request de-duplication (concurrent requests for the same image share one render task), and off-actor rendering so CoreGraphics work never blocks loads - Prefetching & cancellation —
UICollectionViewDataSourcePrefetchingwarms the cache ahead of the scroll; direction changes cancel unclaimed prefetches, while tasks claimed by a visible cell are protected. Cells cancel their load task on reuse so recycled cells never flash a stale image - Infinite scroll — paged mock feed service with simulated latency; the next page kicks off a few rows before the bottom
- Social interactions — double-tap to like with an Instagram-style heart burst, springy like-button animation, and a comments sheet (medium/large detents) that writes straight back to the feed cell
- Profile grid — three-column compositional grid of the user's posts with a stats header, loading thumbnails at grid size so it never pays for feed-size renders
UIKit + MVVM, fully programmatic UI. View models (FeedViewModel, ProfileViewModel) own pagination and interaction state and notify controllers through lightweight closures; controllers translate that into diffable snapshots. The image pipeline is isolated behind the ImageLoader actor, with PatternImageRenderer standing in for network decode — same call shape as a real remote loader, so swapping in URLs would touch exactly one file.
PhotoFeed/
├── App/ AppDelegate, SceneDelegate
├── Models/ PhotoUser, Post, Comment, ImageToken, FeedPage
├── Services/ ImageLoader (actor), PatternImageRenderer,
│ FeedService, MockDataService
├── ViewModels/ FeedViewModel, ProfileViewModel
├── Controllers/ MainTabBar, Feed, Comments, Profile
└── Views/ PostCell, PhotoGridCell, AvatarView
Open PhotoFeed.xcodeproj in Xcode 15+ and run. No dependencies, no configuration — all data is mocked locally.
Built by Zaid Mubbasher · Portfolio · LinkedIn


