Skip to Content
Course content

171: Interview Practice: System Design Basics for iOS Roles

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I'm used to coding challenges, but how do I even start a system design conversation?

The biggest mistake I see candidates make is jumping straight into the architecture. They'll start drawing boxes for "Network Layer" and "Database" before they even know what they're building. In a real interview, the prompt will be intentionally vague—something like "Design an app like Instagram." If you start coding or diagramming immediately, you've already failed the "communication" part of the test.

I always tell my mentees to spend the first five to ten minutes just asking questions. You need to define the scope. Does the app need to work offline? Are we supporting tablets? Is "real-time" a hard requirement, or is a 5-second delay acceptable? By narrowing the scope, you prevent yourself from over-engineering a solution for a problem the interviewer didn't actually care about.

When they ask how I'd structure the app, is MVVM enough, or are they looking for something more?

MVVM is a great starting point, but for a system design interview, it's too small. It describes how a single screen works, not how a whole system scales. To impress a senior engineer, you need to talk about the Data Layer. This is where the Repository pattern comes in.

I prefer the Repository pattern because it abstracts the data source from the rest of the app. Your ViewModel shouldn't care if the data is coming from a JSON API or a local SQLite database; it just asks the Repository for a model. This makes your system testable and flexible. Here is a simplified way to think about that interface:

protocol FeedRepository {
    func fetchPosts(page: Int) async throws -> [Post]
    func savePost(_ post: Post) async throws
}

class FeedRepositoryImpl: FeedRepository {
    let remoteDataSource: APIClient
    let localDataSource: LocalDatabase

    init(remote: APIClient, local: LocalDatabase) {
        self.remoteDataSource = remote
        self.localDataSource = local
    }

    func fetchPosts(page: Int) async throws -> [Post] {
        // I'd implement a "cache-then-network" strategy here
        if let cached = await localDataSource.getCachedPosts(page: page) {
            return cached
        }
        let posts = try await remoteDataSource.getPosts(page: page)
        await localDataSource.save(posts)
        return posts
    }
    
    func savePost(_ post: Post) async throws {
        try await remoteDataSource.upload(post)
        await localDataSource.save(post)
    }
}

See how the FeedRepositoryImpl acts as the brain? It decides where the data comes from. That's the kind of "system" thinking interviewers are looking for.

How do I handle questions about "scaling" when the app is just running on a phone?

When a backend engineer talks about scale, they mean "more servers." When an iOS engineer talks about scale, we mean "limited resources." You're fighting for memory, battery life, and cellular data. If you're designing a feed with thousands of items, you can't just load them all into an array.

You should discuss things like pagination (cursor-based is usually better than offset-based for feeds) and image caching. Mentioning an LRU (Least Recently Used) cache shows you understand that the phone's RAM is a precious resource. I've seen people get tripped up by not mentioning "prefetching"—the act of loading the next page of data before the user actually hits the bottom of the screen to create a seamless experience.

How do I answer the "What happens when the user is offline?" part of the design?

This is where you talk about the "Single Source of Truth" (SSOT). The UI should never observe the network directly; it should observe the local database. When the network returns data, you save it to the database, and the database triggers a UI update.

If the user performs an action while offline—like liking a post—you shouldn't just show an error. You should implement an "Outbox" pattern. Save the action to a local queue, update the UI optimistically (show the heart as filled immediately), and then use a background task to sync those pending actions once the connection returns. It's a bit more work to build, but it's the difference between a junior app and a professional one.




📋 Practical Task

Design a Paginated Image Gallery Repository

You are tasked with designing the data layer for a high-performance image gallery app. The app must handle thousands of images without crashing the app due to memory pressure.

Your Goal: Write a Swift implementation of an ImageGalleryRepository that handles the following requirements:

  • Define a GalleryItem model.
  • Create a GalleryRepository protocol with a method fetchImages(page: Int).
  • Implement the repository such that it first checks a LocalCache (which you can mock as a simple dictionary or array) and, if empty, fetches from a RemoteAPI.
  • Implement a basic "Prefetch" logic: when page n is requested and returned successfully, the repository should automatically trigger a fetch for page n + 1 in the background.

Focus on the architecture of the data flow rather than the actual network calls. Use async/await for your asynchronous operations.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.