Networking Layer Design
API Client Architecture
Design a flexible networking layer with protocol-based abstraction. Define an Endpoint struct that encapsulates path, method, headers, body, and query items. Create an APIClient protocol with a generic request method that returns Decodable objects.
protocol APIClient {
func request<T: Decodable>(_ endpoint: Endpoint) async throws -> T
}
Request and Response Handling
The NetworkService implements APIClient. Build URLRequest from Endpoint, execute with URLSession, and decode responses. Handle HTTP status codes with specific errors for unauthorized, not found, and server errors. Use JSONDecoder with date strategies for consistent parsing.
Retry and Error Recovery
Implement a RetryPolicy that determines whether to retry based on error type and attempt count. Retry on timeout and 5xx server errors. Use exponential backoff for retry delays. Add circuit breaker pattern to stop retrying after repeated failures.
Authentication
Handle token-based authentication with automatic refresh. Store tokens in Keychain. Intercept requests to add authorization headers. Refresh expired tokens transparently. Support multiple authentication methods through strategy pattern.
Caching Architecture
Multi-Layer Caching
Implement a caching hierarchy with memory cache (NSCache) for fast access, disk cache for persistence, and network as the final source. Each layer checks before fetching from the next layer.
class CacheManager {
let memoryCache = NSCache<NSString, AnyObject>()
let diskCache: DiskCache
func get<T: Codable>(_ key: String, type: T.Type) async throws -> T {
if let cached = memoryCache.object(forKey: key as NSString) as? T {
return cached
}
if let diskData = try await diskCache.get(key) {
let decoded = try JSONDecoder().decode(T.self, from: diskData)
memoryCache.setObject(decoded as AnyObject, forKey: key as NSString)
return decoded
}
throw CacheError.notFound
}
}
Cache Invalidation
Use time-based expiration (TTL), event-based invalidation (user action), and version-based invalidation (API version changes). Tag cached entries for group invalidation when related data changes.
Image Caching
Use a dedicated image cache with downsampling. Cache images at display size, not original size. Implement memory pressure handling to evict images under pressure. Use URLCache for HTTP-level caching of image requests.
Offline-First Design
Data Synchronization
Store data locally with CoreData or SwiftData. Queue mutations when offline. Sync when connectivity returns. Handle conflicts with last-write-wins or merge strategies.
Sync Strategies
- Optimistic sync: Apply changes locally immediately, sync in background
- Pessimistic sync: Wait for server confirmation before applying locally
- Conflict resolution: Use timestamps, version vectors, or operational transforms
Connectivity Monitoring
Use NWPathMonitor to track network state. Queue operations when offline. Retry failed syncs when connectivity returns. Show sync status to the user.
Modular Architecture
Split the app into feature modules with clear boundaries. Each module owns its data, networking, and UI. Use protocols for inter-module communication. Share common infrastructure through a core module.
Quiz
1. What is the benefit of a protocol-based networking layer?
2. What is cache invalidation?
3. What is optimistic sync?
4. Why use modular architecture in large iOS apps?
Flashcards
Question
What are the layers of a caching architecture?
Click to reveal answer
Answer
Memory cache (NSCache for fast access), disk cache (persistence), and network (source of truth). Check each layer before fetching from the next.
Question
What is the difference between optimistic and pessimistic sync?
Click to reveal answer
Answer
Optimistic applies changes locally immediately and syncs in background. Pessimistic waits for server confirmation before applying locally.
Question
What is the purpose of a circuit breaker in networking?
Click to reveal answer
Answer
To stop retrying after repeated failures, preventing resource waste and allowing the server to recover.
Revision Notes
Key Takeaways
- 1. Protocol-based networking enables testability and flexibility
- 2. Multi-layer caching optimizes performance at every level
- 3. Cache invalidation ensures data freshness
- 4. Optimistic sync provides responsive user experience
- 5. Modular architecture scales with team size
Interview Tips
- • Draw the architecture diagram before explaining details
- • Discuss trade-offs between different caching strategies
- • Explain how you handle offline scenarios and data conflicts
- • Walk through designing a networking layer for a social media app
Cheat Sheet
System Design Quick Reference
- Protocol-based networking for testability
- Multi-layer caching: memory, disk, network
- Cache invalidation: TTL, event-based, version-based
- Optimistic sync for responsive UI
- Modular architecture for team independence
- Circuit breaker for fault tolerance