🥞 Creating and Using Protocols in Swift 🐼

Building a Newsletter App
Protocols
March 24, 2025
Sponsored

Need to capture HTTPS traffic?

Try Proxyman ❤️ the best-in-class macOS app, and capture HTTPS traffic from the iOS, Simulator with 1 click. Used by 250,000+ developers. Try it for free now!

This message is brought to you by a sponsor who helps keep this content free for everyone. If you have a moment, check them out — your support means a lot!

Welcome to issue #38 of the iOS Coffee Break Newsletter 📬 and to the 2nd issue of the "Building a Newsletter App" series!

Last week, we covered how to parse JSON using the Codable protocol and implemented a real world use case that fetches and decodes data from my newsletter API feed.

This week, I want to dive into protocols and the advantages of adopting them. As part of this, I will be refactoring my view model by replacing my APIClient by an IssuesLiveRepository class that conforms to a protocol we will define.

This will help improve flexibility, maintainability and testability in the code.

Let's explore how to implement this approach!

What is a Protocol?

In Swift, protocols act as blueprints, defining the expected behavior of a data type while ensuring consistency across code.

They have many real-world applications, such as:

  • Dependency Injection: Defining interfaces for dependencies makes testing and decoupling easier.
  • UI Component Design: Protocols help create reusable UI elements.
  • Networking: They establish interfaces for HTTP requests, simplifying mocking and testing.
  • Delegation: A key pattern in iOS, protocols define delegate responsibilities, enabling flexible communication between objects.

Defining a Protocol

Let's start by creating an IssuesRepository protocol that outlines a single method, responsible for returning an array of Issue objects. That is all we need for our example.

protocol IssuesRepository {
    func getIssues() async throws -> [Issue]
}

Conforming to the Protocol

Now, let's create an IssuesLiveRepository class that adheres to the IssuesRepository protocol. There is only one method to implement and it is the getIssues() function. Here, we call the getIssues() method from the APIClient that returns an array of issues.

class IssuesLiveRepository: IssuesRepository {
    private let apiClient: APIClient = APIClient()
 
    func getIssues() async throws -> [Issue] {
        return await apiClient.getIssues()
    }
}

Making use of the Protocol

In our view model, we define a property of type IssuesRepository. Using dependecy injection, we can pass in any instance that conforms to this protocol, making the code more modular, easier to test, and reducing tight coupling between components.

@Observable
class IssuesViewModel {
    private(set) var issues: [Issue] = []
    private let issuesRepository: IssuesRepository
 
    init(issuesRepository: IssuesRepository = IssuesLiveRepository()) {
        self.issuesRepository = issuesRepository
    }
 
    @MainActor
    func getIssues() async {
        do {
            self.issues = try await issuesRepository.getIssues()
        } catch {
            print("Error to be handled here: \(error)")
        }
    }
}

🤝 Wrapping Up

While protocols offer a powerful way to enhance functionality, it is essential to consider alternative approaches that might better suit specific use cases. As with many aspects of programming, there is no absolute right or wrong choice — each solution comes with its own tradeoffs.

Hopefully, this issue has provided insight into different ways to leverage protocols effectively and helped you weigh the benefits using them.

In future issues, I plan to introduce additional implementations of the IssuesRepository protocol, tailored for different use cases such as mocking and testing. Stay tuned!

References

CURATED FROM THE COMMUNITY

🐙 Dependency Inversion Principle (DIP) in iOS Swift

Have you come across the Dependency Inversion Principle (DIP)? If not, it is a fundamental concept in software development that helps create flexible and maintainable code.

In this article, Arifin dives into the DIP, covering its origins, the challenges of tight coupling, and various techniques for implementing it effectively in Swift.

✍️ Composing a Resume in Markdown

This week, I came across an amazing tool that I am excited to try out! Updating a resume can be a tedious task, but since it is essentially a text-based document, it can be easily automated using Markdown.

Scott wrote a insightful article explaining how to create a resume in Markdown, convert it to HTML, style it with CSS and then generate a PDF — making the entire process much more efficient!

🆗 Solving Swift Macro Trust Issues in Xcode Cloud Builds

If you have used a macro locally, you might have noticed that Xcode prompts you with a dialog asking for trust confirmation before executing the macro's package. This becomes an issue in automated setups like Xcode Cloud, where there is no way to manually approve the prompt.

In his latest article, Cihat explains how to set up a simple post-clone script to disable Xcode's macro fingerprint validation, allowing you to bypass the trust requirement.

☁️ Deploying a Swift Server App to Fly.io and Railway

For iOS developers, deploying a Server-Side Swift app can feel a bit overwhelming!

In this post, Natan from SwiftToolkit walks you through deploying a Vapor app on two platforms — Fly.io and Railway — both of which let you skip the hassle of installing Docker on your machine.

A few years back, I built a food-related 🍏🍕🍦 web API using Swift and Vapor, deploying it on Fly.io. At the time, I faced some challenges with the free tier as my sample project grew, but the deployment process was so effortless that it felt almost like cheating. Maybe I will give it another try!