🥞 Creating and Using Protocols in Swift 🐼
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!