System Design for Mobile Development (iOS)
📱 Mobile vs Backend System Design
Server scaling
Database sharding
Microservices
App architecture
State management
Network layer
Local storage
UI performance
Offline-first approach
┌─────────────────────────────────────────┐
│ PRESENTATION LAYER │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Views │ │ ViewModels │ │
│ │ (SwiftUI) │ │ (Observable) │ │
│ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ DOMAIN LAYER │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Use Cases │ │ Entities │ │
│ │ (Business │ │ (Models) │ │
│ │ Logic) │ │ │ │
│ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ DATA LAYER │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Repositories │ │ Services │ │
│ │ │ │ (Network, │ │
│ │ │ │ Auth, etc.) │ │
│ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────┘
// View
struct HomeView : View {
@ ObservedObject var viewModel : HomeViewModel
var body : some View {
List ( viewModel . items ) { item in
Text ( item . title )
}
}
}
// ViewModel
@ MainActor
class HomeViewModel : ObservableObject {
@ Published var items : [ Item ] = [ ]
func loadItems ( ) {
// Business logic
}
}
// Model
struct Item : Identifiable {
let id : String
let title : String
}
Abstract data source
Single source of truth
Easy to test
protocol ItemRepositoryProtocol {
func fetchItems ( ) async throws -> [ Item ]
}
class ItemRepository : ItemRepositoryProtocol {
private let networkService : NetworkServiceProtocol
private let cacheService : CacheServiceProtocol
func fetchItems ( ) async throws -> [ Item ] {
// 1. Check cache
if let cached = cacheService . getItems ( ) {
return cached
}
// 2. Fetch from network
let items = try await networkService . fetchItems ( )
// 3. Cache result
cacheService . saveItems ( items )
return items
}
}
Use Cases (Business Logic)
class FetchItemsUseCase {
private let repository : ItemRepositoryProtocol
init ( repository : ItemRepositoryProtocol ) {
self . repository = repository
}
func execute ( ) async throws -> [ Item ] {
return try await repository . fetchItems ( )
}
}
class ViewModel {
private let useCase : FetchItemsUseCase
init ( useCase : FetchItemsUseCase ) {
self . useCase = useCase
}
}
// Setup
let networkService = NetworkService ( )
let repository = ItemRepository ( networkService : networkService )
let useCase = FetchItemsUseCase ( repository : repository )
let viewModel = ViewModel ( useCase : useCase )
protocol NetworkServiceProtocol {
func request < T : Decodable > ( _ endpoint : Endpoint ) async throws -> T
}
protocol Endpoint {
var path : String { get }
var method : HTTPMethod { get }
var body : Encodable ? { get }
}
private let memoryCache = NSCache < NSString , UIImage > ( )
let cacheDir = FileManager . default . urls (
for : . cachesDirectory ,
in : . userDomainMask
) [ 0 ]
Offline-First Architecture
Always return cached data first
Fetch fresh data in background
Update cache when fresh data arrives
Queue writes for sync
Performance Optimizations
Lazy Loading : Load data as needed
Image Caching : Cache images in memory and disk
Pagination : Load data in chunks
Background Tasks : Use background queues
Debouncing : Prevent excessive API calls
Common Mobile System Design Questions
Architecture: MVVM
Components: FeedView, FeedViewModel, FeedRepository
Optimizations: Pagination, image caching, lazy loading
2. Design Offline-First App
Local storage first
Background sync
Conflict resolution
3. Design Image Caching System
Memory cache (NSCache)
Disk cache (FileManager)
Image resizing
Prefetching
Checklist for Mobile System Design
Clean Architecture by Robert C. Martin
iOS Architecture Patterns
Ray Wenderlich Architecture Tutorials