Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Roll your own iOS dependency injection
protocol Injectable: class {
var router: Routable { get }
}
class Injector: Injectable {
lazy var router: Routable { Router() }()
}
protocol Routable {
func route(url: String)
}
class Router: Routable {
func route(url: String) {
/* ... */
}
}
class Controller {
let router: Routable
init(injector: Injectable = Injector()) {
self.router = injector
}
func clicked(url: String) {
router.route(url: url)
}
}
class Application {
func start() {
let controller = Controller()
/* ... */
}
}
class FakeInjector: Injectable {
lazy var router: Routable { FakeRouter() }()
}
class FakeRoutable: Routable {
private(set) var lastRoutedUrl: String?
func route(url: String) {
lastRoutedUrl = url
}
}
class ControllerSpec: QuickSpec {
override func spec() {
var subject: Controller!
var injector: FakeInjectable!
var router: FakeRoutable!
beforeEach() {
injector = FakeInjectable()
injector.router = FakeRouteable!
subject = Controller(injector: injector)
}
describe("click a URL") {
it("routes the URL via the router") {
let url = "https://masilotti.com"
subject.click(url: url)
expect(router.lastRoutedUrl).to(equal(url))
}
}
}
}
@joemasilotti
Copy link
Author

joemasilotti commented Feb 28, 2020

I'm toying around with this approach to dependency injection on iOS.

The goal is that you never actually pass a (static) dependency around in your application code. injector is always a default argument so you get that for free. Then, when under test you inject your fake version with everything stubbed out.

The first obvious problem is that Injectable grows linearly with your application. But since the code ends up being fairly simple, perhaps that's not too terrible?

What do y'all think? Am I missing something obvious that will bite me?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment