Skip to content
DeveloperMemos

Using the Repository Pattern in Swift

Swift, Repository Pattern, Design Patterns1 min read

The repository pattern is a software design pattern that provides an abstraction of data access. It acts as an intermediary between an application's data access layer and business logic layer, effectively decoupling the two. In Swift, using the repository pattern can help achieve loose coupling and keep domain objects persistence ignorant.

What Problem Does It Solve?

Imagine you need to query your model objects from different locations in your code repeatedly. A repository can be incredibly helpful in such cases. It provides a single entry point to work with your models and removes duplicate query code. Let's dive into how to use the repository pattern in Swift.

Sketching the Scene

Suppose you have code that fetches data from an API and maps it to model objects. Here's an example using RxSwift and Moya (a networking abstraction layer):

1// Fetch articles from the server
2APIManager.fetchArticles { articles in
3 // Handle the articles
4}

Why Do We Need a Repository?

At this point, you might think you don't need a repository. If you only call the API once in your entire codebase, adding a repository might be overkill. However, as your codebase grows, you'll find yourself writing the same code to fetch articles repeatedly. Copy-pasting the code everywhere is not a good solution.

Enter the repository. It's an object that encapsulates all the code to query your models in one place. With a repository, you have a single point of entry to get all the articles. Let's create a repository object that provides a public API to get the articles:

1protocol ArticleRepository {
2 func getArticles(completion: @escaping ([Article]) -> Void)
3}
4
5class RemoteArticleRepository: ArticleRepository {
6 func getArticles(completion: @escaping ([Article]) -> Void) {
7 // Fetch articles from the API
8 APIManager.fetchArticles { articles in
9 completion(articles)
10 }
11 }
12}
13
14// Usage
15let articleRepo: ArticleRepository = RemoteArticleRepository()
16articleRepo.getArticles { articles in
17 // Handle the articles
18}

Handling All Article Interactions

The repository isn't just for fetching data; it can also handle CRUD (create, read, update, delete) operations on your model. By adding the logic for these operations in the repository, you create a nice API to use throughout your code without repeating the same code over and over again.

1protocol ArticleRepository {
2 func getArticles(completion: @escaping ([Article]) -> Void)
3 func addArticle(_ article: Article)
4 func updateArticle(_ article: Article)
5 func deleteArticle(_ article: Article)
6}
7
8class RemoteArticleRepository: ArticleRepository {
9 // Implementation for CRUD operations
10 // ...
11}
12
13// Usage
14let articleRepo: ArticleRepository = RemoteArticleRepository()
15articleRepo.addArticle(newArticle)
16articleRepo.updateArticle(existingArticle)
17articleRepo.deleteArticle(articleToDelete)

Power-Up: Protocols

What if you need to load data from a local JSON file instead of an online source? Create a protocol that lists the method names, and then create an implementation for the online API and another for loading data offline:

1protocol ArticleRepository {
2 func getArticles(completion: @escaping ([Article]) -> Void)
3 // Other methods...
4}
5
6class RemoteArticleRepository: ArticleRepository {
7 // Implementation for online API
8 // ...
9}
10
11class LocalArticleRepository: ArticleRepository {
12 // Implementation for loading data offline
13 // ...
14}
15
16// Usage
17let articleRepo: ArticleRepository = LocalArticleRepository()
18articleRepo.getArticles { articles in
19 // Handle the articles (from local storage)
20}

In summary, the repository pattern provides a clean way to query your models, promotes code reusability, and keeps your codebase organized. Whether you're fetching data from an API or loading it from a local file, the repository pattern can simplify your Swift code and improve maintainability.