Created
March 25, 2022 14:04
-
-
Save bocato/6c3d62935d8ea3cddd1aff109ffa9d68 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Combine | |
public struct SomeRepository { | |
// Well defined errors, when possible. | |
public enum Error: Swift.Error { | |
case invalidData | |
} | |
// Public API as closures. | |
public let getList: () -> AnyPublisher<[String], Error> | |
} | |
extension SomeRepository { | |
/// The implementation of the logic, with Initializer Based Dependency Injection. | |
/// This should be *internal*, unless there is a real need to make it public. | |
static func instantiate( | |
session: URLSession, | |
jsonDecoder: JSONDecoder | |
) -> Self { | |
.init( | |
getList: { | |
session | |
.dataTaskPublisher(for: .init(string: "www.myapi.com/list")!) | |
.map(\.data) | |
.decode(type: [String].self, decoder: jsonDecoder) | |
.mapError { _ in Error.invalidData } | |
.eraseToAnyPublisher() | |
} | |
) | |
} | |
/// The real dependency, with the default parameters. | |
/// This can be *public*. | |
public static let live: Self = .instantiate( | |
session: .shared, | |
jsonDecoder: .init() | |
) | |
} | |
/* --- Usage --- */ | |
final class MyViewModel { | |
// MARK: - Dependencies | |
private let someRepository: SomeRepository | |
// MARK: - Properties | |
private var subscriptions: Set<AnyCancellable> = .init() | |
// MARK: - Initialization | |
init(someRepository: SomeRepository = .live) { | |
self.someRepository = someRepository | |
} | |
func doSomethingUsingTheRepository() { | |
someRepository | |
.getList() | |
.sink( | |
receiveCompletion: { completion in /* do your magic 🧙♀️ */ }, | |
receiveValue: { response in /* do your magic 🧙♀️ */ } | |
) | |
.store(in: &subscriptions) | |
} | |
} | |
/* --- Testing --- */ | |
#if DEBUG | |
extension SomeRepository { | |
static func stub( | |
getListResult: Result<[String], Error> | |
) -> Self { | |
.init( | |
getList: { getListResult.publisher.eraseToAnyPublisher() } | |
) | |
} | |
static let dummy: Self = .init( | |
getList: { Empty().eraseToAnyPublisher() } | |
) | |
static let someSpecificScenarioThatIUseAlot: Self = .init( | |
getList: { | |
Just([]) | |
.setFailureType(to: Error.self) | |
.eraseToAnyPublisher() | |
} | |
) | |
} | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment