Skip to content

Instantly share code, notes, and snippets.

@vittoriom
Last active March 11, 2017 13:11
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vittoriom/3429bd2b342243416fc5 to your computer and use it in GitHub Desktop.
Save vittoriom/3429bd2b342243416fc5 to your computer and use it in GitHub Desktop.
A wrap-up of the Swifty Service Locator implementation as described in http://vittoriomonaco.it/blog/swifty-service-locator
// Since in this class we'll use other objects, we define a typealias at the beginning of the file that will have 2 functions:
// 1) It will provide an interface for service locators that want to locate dependencies for this object, type-safe, not more and not less
// 2) It will show in a readable way what dependencies this object needs
typealias ArticleInteractorServiceLocatorInterface = protocol<ArticleRepositoryLocator, ReachabilityLocator>
// Then we define a class implementing this protocol, that will get all the default implementations of the protocols it inherits from, for free
class ArticleInteractorServiceLocator: ArticleInteractorServiceLocatorInterface {
// We can just override the default implementation in this specific service locator!
func reachability() -> ReachabilityInterface {
return OfflineSimulator()
}
}
class ArticleInteractor {
// dependencies
let repository: ArticleRepositoryInterface // Always code against interfaces!
let reachability: ReachabilityInterface
init(serviceLocator: ArticleInteractorServiceLocatorInterface = ArticleInteractorServiceLocator()) {
self.repository = serviceLocator.articleRepository() // We don't know which implementation we'll get, we just care about the API
self.reachability = serviceLocator.reachability()
}
}
// ArticleInteractorTests.swift
// As in the app case, we define our own implementation of the ArticleInteractorServiceLocatorInterface
class ArticleInteractorServiceLocatorTestImpl: ArticleInteractorServiceLocatorInterface {
lazy var fakeReachability = FakeReachability()
lazy var fakeArticleRepository = FakeArticleRepository()
func articleRepository() -> ArticleRepositoryInterface {
return fakeArticleRepository
}
func reachability() -> ReachabilityInterface {
return fakeReachability
}
}
// We use Quick & Nimble, but it doesn't matter except for the syntax
class ArticleInteractorTests: QuickSpec {
override func spec() {
describe("An article interactor") {
var sut: ArticleInteractor!
var serviceLocator: ArticleInteractorServiceLocatorTestImpl!
beforeEach {
serviceLocator = ArticleInteractorServiceLocatorTestImpl()
// We now use our own service locator
sut = ArticleInteractor(serviceLocator: serviceLocator)
}
context("when offline") {
beforeEach {
// We can operate on our fakes
serviceLocator.fakeReachability.isOffline = true
}
it("should do something") {
//test something
//...
}
}
}
}
}
class ArticleRepository: ArticleRepositoryInterface {
func articlesList() -> [Article] {
return []
}
}
// In 95% of the cases, we'll always use this implementation when locating ArticlePersistenceInterfaces in the main app. For the tests, we'll provide a different implementation anyway.
// So, contextually with the definition of the main class conforming to the protocol, we also extend the locator to provide this implementation
extension ArticleRepositoryLocator {
func articleRepository() -> ArticleRepositoryInterface {
return ArticleRepository()
}
}
// This is a dependency we want to inject into one or more objects later
protocol ArticleRepositoryInterface {
func articlesList() -> [Article]
}
// Contextually with the definition of a dependency interface we also define a protocol that will locate objects conforming to this interface
protocol ArticleRepositoryLocator {
func articleRepository() -> ArticleRepositoryInterface
}
class OfflineSimulator: ReachabilityInterface {
func isConnected() -> Bool {
return false
}
}
// We can't provide a second default implementation for the ReachabilityInterfaceLocator, which is good because this would be a developer error that is now catched by the compiler. Also, we don't want this implementation to be the default ;)
class Reachability: ReachabilityInterface {
func isConnected() -> Bool {
return true
}
}
extension ReachabilityInterfaceLocator {
func reachability() -> ReachabilityInterface {
return Reachability()
}
}
protocol ReachabilityInterface {
func isConnected() -> Bool
}
protocol ReachabilityLocator {
func reachability() -> ReachabilityInterface
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment