Design an Instagram-like Feed

Published:

๐ŸŽฏ Problem Statement

Design the architecture for an Instagram-like feed for iOS that can:

  • Display images and videos efficiently
  • Support infinite scrolling with pagination
  • Handle offline mode and caching
  • Optimize for battery life and data usage
  • Scale to millions of users

Constraints:

  • Feed has millions of posts
  • Images range from 500KB to 5MB
  • Videos up to 60 seconds, 1080p
  • Users scroll through 50+ posts per session
  • Must work on slow 3G networks

๐Ÿ“ STEP 1: High-Level Architecture Diagram

In the interview, start by drawing this on the whiteboard:

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    iOS CLIENT LAYER                         โ”‚
โ”‚                                                              โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”         โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                 โ”‚
โ”‚  โ”‚ FeedView     โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถโ”‚ FeedViewModelโ”‚                 โ”‚
โ”‚  โ”‚ Controller   โ”‚โ—€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚              โ”‚                 โ”‚
โ”‚  โ”‚              โ”‚         โ”‚  @Published  โ”‚                 โ”‚
โ”‚  โ”‚  - UITable/  โ”‚         โ”‚  properties  โ”‚                 โ”‚
โ”‚  โ”‚    Collectionโ”‚         โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                 โ”‚
โ”‚  โ”‚  - Cells     โ”‚                โ”‚                          โ”‚
โ”‚  โ”‚  - Prefetch  โ”‚                โ”‚                          โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                โ”‚                          โ”‚
โ”‚                                   โ”‚                          โ”‚
โ”‚                          โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”               โ”‚
โ”‚                          โ”‚ FeedRepository   โ”‚               โ”‚
โ”‚                          โ”‚ (Single Source   โ”‚               โ”‚
โ”‚                          โ”‚  of Truth)       โ”‚               โ”‚
โ”‚                          โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜               โ”‚
โ”‚                                    โ”‚                         โ”‚
โ”‚          โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”     โ”‚
โ”‚          โ”‚                         โ”‚                  โ”‚     โ”‚
โ”‚   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”         โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”โ”‚
โ”‚   โ”‚ Network     โ”‚         โ”‚ Cache          โ”‚  โ”‚ Database  โ”‚โ”‚
โ”‚   โ”‚ Service     โ”‚         โ”‚ Manager        โ”‚  โ”‚ Manager   โ”‚โ”‚
โ”‚   โ”‚             โ”‚         โ”‚                โ”‚  โ”‚           โ”‚โ”‚
โ”‚   โ”‚ - API calls โ”‚         โ”‚ - Memory cache โ”‚  โ”‚ - CoreDataโ”‚โ”‚
โ”‚   โ”‚ - URLSessionโ”‚         โ”‚ - Disk cache   โ”‚  โ”‚ - Offline โ”‚โ”‚
โ”‚   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜         โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜โ”‚
โ”‚                                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                              โ”‚
                              โ”‚ HTTP/HTTPS
                              โ–ผ
                    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                    โ”‚   BACKEND API    โ”‚
                    โ”‚                  โ”‚
                    โ”‚  GET /feed       โ”‚
                    โ”‚  GET /post/:id   โ”‚
                    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                              โ”‚
                              โ–ผ
                    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                    โ”‚   CDN (Images)   โ”‚
                    โ”‚   - CloudFront   โ”‚
                    โ”‚   - Imgix        โ”‚
                    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Key Dependencies:

  • ViewController depends on ViewModel (observer pattern)
  • ViewModel depends on Repository (clean architecture)
  • Repository coordinates Network, Cache, Database
  • All image/video URLs point to CDN

๐Ÿ’ฌ What to say while drawing:

โ€œIโ€™ll start with a layered architecture. At the top, we have the View layer handling UI. The ViewModel manages state and business logic. The Repository acts as a single source of truth, coordinating between network, cache, and database. For images, weโ€™ll use a CDN to reduce latency and server load.โ€


๐Ÿ‘ค STEP 2: User Flow Diagram

Draw this to show interaction flow:

USER OPENS APP
      โ”‚
      โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ 1. Load Feed    โ”‚
โ”‚    Screen       โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
         โ”‚
         โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”         โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ 2. Check        โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถโ”‚ YES: Show    โ”‚
โ”‚    Cache?       โ”‚         โ”‚    Cached    โ”‚
โ”‚                 โ”‚         โ”‚    Posts     โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜         โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
         โ”‚ NO
         โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ 3. Show         โ”‚
โ”‚    Loading      โ”‚
โ”‚    Indicator    โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
         โ”‚
         โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ 4. Fetch 20     โ”‚
โ”‚    Posts from   โ”‚
โ”‚    API          โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
         โ”‚
         โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”         โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ 5. Parse JSON   โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถโ”‚ 6. Store in  โ”‚
โ”‚    Response     โ”‚         โ”‚    Cache     โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜         โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
         โ”‚
         โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ 7. Display      โ”‚
โ”‚    Posts        โ”‚
โ”‚    (Text only)  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
         โ”‚
         โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”         โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ 8. Download     โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถโ”‚ 9. Show      โ”‚
โ”‚    Images       โ”‚         โ”‚    Images    โ”‚
โ”‚    Async        โ”‚         โ”‚    as loaded โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜         โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
         โ”‚
         โ”‚ USER SCROLLS DOWN
         โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ 10. Reached     โ”‚
โ”‚     80% of list โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
         โ”‚
         โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ 11. Prefetch    โ”‚
โ”‚     next 20     โ”‚
โ”‚     posts       โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
         โ”‚
         โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ 12. Repeat      โ”‚
โ”‚     steps 4-9   โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

OFFLINE MODE:
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ No network?     โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถโ”‚ Show cached โ”‚
โ”‚                 โ”‚         โ”‚ posts with  โ”‚
โ”‚                 โ”‚         โ”‚ banner      โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜         โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

๐Ÿ’ฌ What to say while drawing:

โ€œWhen the user opens the app, we first check the cache. If we have data, show it immediately for perceived performance. Then fetch fresh data in the background. As users scroll, we prefetch the next page at 80% scroll position. For offline mode, we gracefully degrade by showing cached content with a banner notification.โ€


๐Ÿ”„ STEP 3: Detailed Component Interactions

Sequence diagram for loading a post:

User          ViewController   ViewModel   Repository   NetworkService   CacheManager
 โ”‚                 โ”‚              โ”‚            โ”‚               โ”‚               โ”‚
 โ”‚ Scroll          โ”‚              โ”‚            โ”‚               โ”‚               โ”‚
 โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถโ”‚              โ”‚            โ”‚               โ”‚               โ”‚
 โ”‚                 โ”‚ loadMore()   โ”‚            โ”‚               โ”‚               โ”‚
 โ”‚                 โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถโ”‚            โ”‚               โ”‚               โ”‚
 โ”‚                 โ”‚              โ”‚fetchPosts()โ”‚               โ”‚               โ”‚
 โ”‚                 โ”‚              โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถโ”‚               โ”‚               โ”‚
 โ”‚                 โ”‚              โ”‚            โ”‚ check cache   โ”‚               โ”‚
 โ”‚                 โ”‚              โ”‚            โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถโ”‚
 โ”‚                 โ”‚              โ”‚            โ”‚โ—€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚
 โ”‚                 โ”‚              โ”‚            โ”‚ MISS          โ”‚               โ”‚
 โ”‚                 โ”‚              โ”‚            โ”‚               โ”‚               โ”‚
 โ”‚                 โ”‚              โ”‚            โ”‚ GET /feed     โ”‚               โ”‚
 โ”‚                 โ”‚              โ”‚            โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถโ”‚               โ”‚
 โ”‚                 โ”‚              โ”‚            โ”‚               โ”‚               โ”‚
 โ”‚                 โ”‚              โ”‚            โ”‚  JSON array   โ”‚               โ”‚
 โ”‚                 โ”‚              โ”‚            โ”‚โ—€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚               โ”‚
 โ”‚                 โ”‚              โ”‚            โ”‚               โ”‚               โ”‚
 โ”‚                 โ”‚              โ”‚            โ”‚ store cache   โ”‚               โ”‚
 โ”‚                 โ”‚              โ”‚            โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถโ”‚
 โ”‚                 โ”‚              โ”‚            โ”‚               โ”‚               โ”‚
 โ”‚                 โ”‚              โ”‚  [Post]    โ”‚               โ”‚               โ”‚
 โ”‚                 โ”‚              โ”‚โ—€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚               โ”‚               โ”‚
 โ”‚                 โ”‚  posts array โ”‚            โ”‚               โ”‚               โ”‚
 โ”‚                 โ”‚โ—€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚            โ”‚               โ”‚               โ”‚
 โ”‚  Display posts  โ”‚              โ”‚            โ”‚               โ”‚               โ”‚
 โ”‚โ—€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚              โ”‚            โ”‚               โ”‚               โ”‚

๐Ÿ”‘ Key Considerations

1. Memory Management

Problem: Loading hundreds of high-res images will cause OOM crashes.

Solutions:

  • Use NSCache for memory cache (auto-evicts when low memory)
  • Downscale images to display size before caching
  • Implement cell reuse properly
  • Release off-screen images aggressively

2. Prefetching Strategy

Question: When and how much should you prefetch?

Approach:

  • Use UITableViewDataSourcePrefetching / UICollectionViewDataSourcePrefetching
  • Prefetch 5-10 items ahead of visible area
  • Cancel prefetch requests when scrolling direction changes
  • Prioritize visible items over prefetch items

3. Caching Strategy

Two-tier caching:

Cache TypeSizeEviction PolicyUse Case
Memory100-200 MBLRU (Least Recently Used)Fast access
Disk500 MB - 1 GBTTL + LRUOffline support

4. Video Playback

Challenges:

  • Battery drain from autoplay
  • Network bandwidth
  • Memory from multiple players

Solutions:

  • AVPlayer pool (reuse 3-5 instances)
  • Pause off-screen videos immediately
  • Preload only visible +1 video
  • Use HLS for adaptive streaming

5. Network Optimization

  • Batch API requests (fetch 20-30 posts at once)
  • Use CDN URLs for media
  • Implement retry logic with exponential backoff
  • Support resume for failed downloads

โœ… Proposed Architecture

High-Level Components

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚         ViewController Layer            โ”‚
โ”‚   (FeedViewController + Cells)          โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                 โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚          ViewModel Layer                โ”‚
โ”‚   (FeedViewModel + State Management)    โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                 โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚       Repository Pattern                โ”‚
โ”‚  (FeedRepository - Single Source)       โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
      โ”‚                        โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”        โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Network   โ”‚        โ”‚  Local Storage   โ”‚
โ”‚  Service   โ”‚        โ”‚  (Core Data)     โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜        โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
      โ”‚                        โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚          Cache Manager                  โ”‚
โ”‚   (Memory + Disk Image Cache)           โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Detailed Architecture

1. ViewModel (MVVM Pattern)

class FeedViewModel {
    // Observable state
    @Published var posts: [FeedPost] = []
    @Published var isLoading = false
    @Published var error: Error?
    
    private let repository: FeedRepository
    private var cancellables = Set<AnyCancellable>()
    private var currentPage = 0
    private var hasMorePages = true
    
    func loadInitialFeed() {
        currentPage = 0
        repository.fetchFeed(page: 0)
            .sink(
                receiveCompletion: { [weak self] completion in
                    self?.isLoading = false
                    if case .failure(let error) = completion {
                        self?.error = error
                    }
                },
                receiveValue: { [weak self] posts in
                    self?.posts = posts
                    self?.hasMorePages = !posts.isEmpty
                }
            )
            .store(in: &cancellables)
    }
    
    func loadMore() {
        guard !isLoading && hasMorePages else { return }
        
        currentPage += 1
        repository.fetchFeed(page: currentPage)
            .sink { [weak self] posts in
                self?.posts.append(contentsOf: posts)
                self?.hasMorePages = !posts.isEmpty
            }
            .store(in: &cancellables)
    }
}

2. Repository Pattern

class FeedRepository {
    private let networkService: NetworkService
    private let localStorage: LocalStorage
    private let imageCache: ImageCacheManager
    
    func fetchFeed(page: Int) -> AnyPublisher<[FeedPost], Error> {
        // Try local first if offline
        if !Reachability.isConnected {
            return localStorage.fetchCachedFeed()
        }
        
        // Fetch from network
        return networkService.getFeed(page: page)
            .handleEvents(receiveOutput: { [weak self] posts in
                // Cache to local storage
                self?.localStorage.save(posts)
            })
            .eraseToAnyPublisher()
    }
}

3. Image Cache Manager

class ImageCacheManager {
    private let memoryCache = NSCache<NSString, UIImage>()
    private let diskCache: DiskCache
    private let downloadQueue: OperationQueue
    
    init() {
        // Configure memory cache
        memoryCache.countLimit = 100
        memoryCache.totalCostLimit = 200 * 1024 * 1024 // 200 MB
        
        // Configure download queue
        downloadQueue.maxConcurrentOperationCount = 4
    }
    
    func loadImage(url: URL, size: CGSize, completion: @escaping (UIImage?) -> Void) {
        let cacheKey = url.absoluteString as NSString
        
        // Check memory cache
        if let cachedImage = memoryCache.object(forKey: cacheKey) {
            completion(cachedImage)
            return
        }
        
        // Check disk cache
        diskCache.image(forKey: cacheKey as String) { [weak self] diskImage in
            if let diskImage = diskImage {
                self?.memoryCache.setObject(diskImage, forKey: cacheKey)
                completion(diskImage)
                return
            }
            
            // Download
            self?.downloadImage(url: url, size: size, cacheKey: cacheKey, completion: completion)
        }
    }
    
    private func downloadImage(url: URL, size: CGSize, cacheKey: NSString, completion: @escaping (UIImage?) -> Void) {
        downloadQueue.addOperation {
            guard let data = try? Data(contentsOf: url),
                  var image = UIImage(data: data) else {
                completion(nil)
                return
            }
            
            // Downscale to display size
            image = image.downscaled(to: size)
            
            // Cache both memory and disk
            self.memoryCache.setObject(image, forKey: cacheKey)
            self.diskCache.store(image, forKey: cacheKey as String)
            
            DispatchQueue.main.async {
                completion(image)
            }
        }
    }
}

4. Prefetching

extension FeedViewController: UITableViewDataSourcePrefetching {
    func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
        for indexPath in indexPaths {
            let post = viewModel.posts[indexPath.row]
            
            // Prefetch image
            imageCache.loadImage(url: post.imageURL, size: cellSize) { _ in }
            
            // Trigger load more if near end
            if indexPath.row >= viewModel.posts.count - 5 {
                viewModel.loadMore()
            }
        }
    }
    
    func tableView(_ tableView: UITableView, cancelPrefetchingForRowsAt indexPaths: [IndexPath]) {
        // Cancel prefetch operations for off-screen rows
        for indexPath in indexPaths {
            let post = viewModel.posts[indexPath.row]
            imageCache.cancelLoad(url: post.imageURL)
        }
    }
}

๐Ÿ’ก What Interviewers Want to Hear

Trade-offs Discussion:

Memory vs Network:

  • Large memory cache = faster but more crashes
  • Small memory cache = slower but safer
  • Balance: 100-200 MB memory, rest on disk

Prefetching vs Bandwidth:

  • Aggressive prefetching = smooth UX, wastes data
  • No prefetching = janky scrolling
  • Balance: Prefetch 5-10 ahead, cancel on direction change

UITableView vs UICollectionView:

  • TableView: Simpler, better for vertical lists
  • CollectionView: More flexible, better for grids
  • Instagram uses: UICollectionView (for flexibility)

Performance Optimizations:

  1. Image Decoding on Background Thread:
    DispatchQueue.global(qos: .userInitiated).async {
     let decodedImage = image.decodedImage()
     DispatchQueue.main.async {
         cell.imageView.image = decodedImage
     }
    }
    
  2. Cell Height Caching: ```swift var heightCache: [IndexPath: CGFloat] = [:]

func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat { return heightCache[indexPath] ?? 300 }


3. **Lazy Loading:**
```swift
// Only load images when cell becomes visible
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    guard let feedCell = cell as? FeedCell else { return }
    feedCell.loadImage()
}

๐Ÿš€ Advanced Topics

Offline Support

class OfflineFeedManager {
    func syncWhenOnline() {
        NotificationCenter.default.publisher(for: .reachabilityChanged)
            .filter { $0.userInfo?["isReachable"] as? Bool == true }
            .sink { [weak self] _ in
                self?.syncPendingActions()
                self?.refreshFeed()
            }
            .store(in: &cancellables)
    }
}

Video Optimization

class VideoPlayerPool {
    private var players: [AVPlayer] = []
    private let maxPlayers = 3
    
    func getPlayer() -> AVPlayer {
        if players.count < maxPlayers {
            let player = AVPlayer()
            players.append(player)
            return player
        }
        
        // Reuse least recently used player
        return players.removeFirst()
    }
    
    func pauseAllPlayers() {
        players.forEach { $0.pause() }
    }
}

๐ŸŽฏ Summary Checklist

When answering this question, make sure to cover:

  • Architecture pattern (MVVM, Repository)
  • Memory management (NSCache, downscaling)
  • Caching strategy (2-tier: memory + disk)
  • Prefetching (UITableViewDataSourcePrefetching)
  • Pagination (cursor-based or offset)
  • Offline support (Core Data persistence)
  • Video handling (AVPlayer pool, autoplay strategy)
  • Performance (background decoding, cell reuse)
  • Network (batching, retry logic, CDN)
  • Trade-offs (discuss pros/cons of each choice)

๐Ÿ’ก Pro Tip: Start with high-level architecture, then drill down into specific components based on interviewerโ€™s focus. Always discuss trade-offs and explain your reasoning!