👨‍🎨 Implementing Views Using Mock Data 🚧

Building a Newsletter App
Protocols
Mocks
March 31, 2025
Sponsored

The #1 Padel Score Tracker Companion

With seamless point counting on your Apple Watch, Padel Time ensures precision and eliminates disputes, leaving you to focus on the game. You just need to raise your arm and click on the winning team on each point, nothing else.

Welcome to issue #39 of the iOS Coffee Break Newsletter 📬 and to the 3rd issue of the "Building a Newsletter App" series!

Last week, we covered how to create and use Protocols in Swift and introduced an IssuesLiveRepository that performs the work of a real external dependency within our app.

This week, I am introducing additional implementations of the IssuesRepository protocol, tailored for different use cases such as mocking and testing.

The Plan

As part of this, I will implement 3 additional versions of the IssuesRepository protocol, each designed to serve as a mock for different scenarios within our IssuesView.

  • IssuesSuccessMockRepository to handle scenarios where our mocked dependency returns data.
  • IssuesEmptyRepository to handle scenarios where our mocked dependency doesn't return data.
  • IssuesFailureMockRepository to handle scenarios where our mocked dependency returns an error.

Creating Additional Instances of the IssuesRepository

Let's first create an IssuesSuccessMockRepository class that returns an array of sample issues.

class IssuesSuccessMockRepository: IssuesRepository {
    func getIssues() async throws -> [Issue] {
        return Issue.sample
    }
}

Next, I am creating IssuesEmptyMockRepository for the scenario that displays no issues data. Here I want to return an empty array.

class IssuesEmptyMockRepository: IssuesRepository {
    func getIssues() async throws -> [Issue] {
        return []
    }
}

Finally, I am creating IssuesFailureMockRepository and here, I am throwing an exception.

class IssuesFailureMockRepository: IssuesRepository {
    func getIssues() async throws -> [Issue] {
        throw APIError.unknownError
    }
}

Making Use of our Mocks

Using our IssuesView from previous discussions as an example, I can use the #preview macro to see how the view appears with the 3 newly implemented versions:

struct IssuesView: View {
    @State var vm: IssuesViewModel
 
    init(vm: IssuesViewModel = IssuesViewModel()) {
        self.vm = vm
    }
 
    var body: some View {
        NavigationStack {
            List(vm.issues) { issue in
                IssueRowView(issue: issue)
            }
            .navigationTitle("Issues")
            .overlay {
                if vm.issues.isEmpty {
                    ContentUnavailableView(
                        "No Issues",
                        systemImage: "books.vertical.fill",
                        description: Text("No issues have been found.")
                    )
                }
            }
            .alert(isPresented: $vm.hasError, error: vm.error) {
                Button("Retry") {
                    Task {
                        await vm.getIssues()
                    }
                }
            }
            .task {
                await self.vm.getIssues()
            }
        }
    }
}
 
#Preview("Happy Path") {
    IssuesView(
        vm: IssuesViewModel(
            issuesRepository: IssuesSuccessMockRepository()
        )
    )
}
 
#Preview("Empty Path") {
    IssuesView(
        vm: IssuesViewModel(
            issuesRepository: IssuesEmptyMockRepository()
        )
    )
}
 
#Preview("Unhappy Path") {
    IssuesView(
        vm: IssuesViewModel(
            issuesRepository: IssuesFailureMockRepository()
        )
    )
}

Here is the output of the IssuesView with the 3 previews:

🤝 Wrapping Up

With that, you can see the advantages of using mocks to design views for different scenarios. Keep this approach in mind when writing your code to ensure it remains flexible and scalable.

Mock data can also be incredibly useful in testing, enabling us to write tests a lot easier, and run them faster in a more predictable way as I plan to show you on future issues! Stay tuned!

CURATED FROM THE COMMUNITY

🐼 Freeing up space on your Mac

If you are an iOS developer, chances are you have run into storage issues on your machine at some point — thank you Xcode!

This week, Manu put together a great guide on manually freeing up space on your Mac. While there are external tools that can handle this for you, I promise it is an interesting read, even if you are just curious about how Xcode eats up your storage.

🧘‍♂️ How to automate perfect screenshots for the Mac App Store

Jesse recently explored ways to automate macOS screenshots, finding far fewer options compared to iOS.

He put his findings into an article, detailing his workflow for generating Mac app screenshots with minimal manual effort. If you have faced the same challenge, be sure to check it out!

🔎 Profiling apps using Instruments

Not quite sure how to use Apple's Instruments tool yet?

Apple just released an in-depth, 1.5-hour tutorial on profiling apps with Instruments — an invaluable resource for anyone looking to get started! If you are interested but unsure where to begin, this is the perfect guide. It is definitely on my watch list!