Skip to content

Instantly share code, notes, and snippets.

@Edudjr
Created February 16, 2022 11:18
Show Gist options
  • Save Edudjr/5796f874233dc4812507db77327139a8 to your computer and use it in GitHub Desktop.
Save Edudjr/5796f874233dc4812507db77327139a8 to your computer and use it in GitHub Desktop.
Repository Pattern in Swift using Type Erasure
// Repository.swift
import Foundation
protocol Repository {
associatedtype Model
func get(id: String, completion: @escaping (Result<Model, Error>) -> Void)
func save(_ item: Model, completion: @escaping (Result<Model, Error>) -> Void)
func remove(_ item: Model, completion: @escaping (Result<Model, Error>) -> Void)
}
enum RepositoryError: Error {
case keyNotFound
case couldNotSave
case couldNotRemove
}
// Type Erasure
struct AnyRepository<Model>: Repository {
typealias Completion = (Result<Model, Error>) -> Void
private let box: RepositoryBoxBase<Model>
init<RepositoryType: Repository>(_ repository: RepositoryType) where Model == RepositoryType.Model {
// If this has already been boxed, avoid boxing again
if let erased = repository as? AnyRepository<Model> {
box = erased.box
} else {
box = RepositoryBox(base: repository)
}
}
func get(id: String, completion: @escaping Completion) {
box.get(id: id, completion: completion)
}
func save(_ item: Model, completion: @escaping Completion) {
box.save(item, completion: completion)
}
func remove(_ item: Model, completion: @escaping Completion) {
box.remove(item, completion: completion)
}
}
fileprivate final class RepositoryBox<RepositoryType: Repository>: RepositoryBoxBase<RepositoryType.Model> {
typealias Completion = (Result<Model, Error>) -> Void
fileprivate let base: RepositoryType
fileprivate init(base: RepositoryType) {
self.base = base
super.init()
}
override func get(id: String, completion: @escaping Completion) {
base.get(id: id, completion: completion)
}
override func save(_ item: Model, completion: @escaping Completion) {
base.save(item, completion: completion)
}
override func remove(_ item: Model, completion: @escaping Completion) {
base.remove(item, completion: completion)
}
}
// Abstract Base Class
private class RepositoryBoxBase<Model>: Repository {
typealias Completion = (Result<Model, Error>) -> Void
func get(id: String, completion: @escaping Completion) {}
func save(_ item: Model, completion: @escaping Completion) {}
func remove(_ item: Model, completion: @escaping Completion) {}
}
extension Repository {
public func eraseToAnyRepository() -> AnyRepository<Model> {
.init(self)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment