MAINTAINING A DEPENDENCY GRAPH WITHMAINTAINING A DEPENDENCY GRAPH WITH
WEAVERWEAVER
THÉOPHANE RUPINTHÉOPHANE RUPIN
@thrupin
DI ContainerDI Container
DI ContainerDI Container
An object able to instantiate, retain, and resolve other
objects’ dependencies.
Dependency GraphDependency Graph
final class Foo {
let bar: Bar
init(bar: Bar) {
self.bar = bar
bar.foo = self
}
func barDidSomething() { ... }
}
final class Bar {
weak var foo: Foo?
func doSomething() { foo?.barDidSomething() }
}
Weaver is a code generation tool which makes it easy
to inject dependencies.
https://github.com/scribd/Weaver
WHY?WHY?
WHY?WHY?
I could not nd any satisfying compile time DI solution,
so I gave it a try.
ADVANTAGESADVANTAGES
ADVANTAGESADVANTAGES
1. Mimics manual DI technics. No black box.
ADVANTAGESADVANTAGES
1. Mimics manual DI technics. No black box.
2. Compile time errors. Fails fast.
ADVANTAGESADVANTAGES
1. Mimics manual DI technics. No black box.
2. Compile time errors. Fails fast.
3. Type safety. If it compiles, it works!
ADVANTAGESADVANTAGES
1. Mimics manual DI technics. No black box.
2. Compile time errors. Fails fast.
3. Type safety. If it compiles, it works!
4. Declarativeness.
ADVANTAGESADVANTAGES
1. Mimics manual DI technics. No black box.
2. Compile time errors. Fails fast.
3. Type safety. If it compiles, it works!
4. Declarativeness.
5. No optionality. If it's declared, it's there.
ADVANTAGESADVANTAGES
1. Mimics manual DI technics. No black box.
2. Compile time errors. Fails fast.
3. Type safety. If it compiles, it works!
4. Declarativeness.
5. No optionality. If it's declared, it's there.
6. Thread safety. Immutability. No lazy loading.
ADVANTAGESADVANTAGES
1. Mimics manual DI technics. No black box.
2. Compile time errors. Fails fast.
3. Type safety. If it compiles, it works!
4. Declarativeness.
5. No optionality. If it's declared, it's there.
6. Thread safety. Immutability. No lazy loading.
7. No additional framework to ship.
DRAWBACKSDRAWBACKS
DRAWBACKSDRAWBACKS
1. More code to compile. Can be optimized.
DRAWBACKSDRAWBACKS
1. More code to compile. Can be optimized.
2. Meta-programmation is hard.
DRAWBACKSDRAWBACKS
1. More code to compile. Can be optimized.
2. Meta-programmation is hard.
3. Use of pseudo annotations.
DRAWBACKSDRAWBACKS
1. More code to compile. Can be optimized.
2. Meta-programmation is hard.
3. Use of pseudo annotations.
4. Experimental.
LET'S SEE IN PRACTICELET'S SEE IN PRACTICE
A logical dependency graph
HOW COULD WE PROVIDEHOW COULD WE PROVIDE URLSessionURLSession??
Sharing an instance of URLSession
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
}
Sharing an instance of URLSession
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
static var shared: AppDelegate? {
return UIApplication.shared.delegate as? AppDelegate
}
}
Sharing an instance of URLSession
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
static var shared: AppDelegate? {
return UIApplication.shared.delegate as? AppDelegate
}
let urlSession: URLSession = {
let config = ...
return URLSession(configuration: config)
}()
}
Using the shared instance of URLSession
final class APIClient {
func get(_ url: URL, completion: @escaping (Data?) -> Void) ->
}
}
Using the shared instance of URLSession
final class APIClient {
func get(_ url: URL, completion: @escaping (Data?) -> Void) ->
AppDelegate.shared.urlSession.dataTask(with: url) {
...
}.resume()
}
}
Not so good...
Injecting a shared instance of URLSession in
APIClient
final class APIClient {
func get(_ url: URL, completion: @escaping (Data?) -> Void) ->
}
}
Injecting a shared instance of URLSession in
APIClient
final class APIClient {
private let urlSession: URLSession
func get(_ url: URL, completion: @escaping (Data?) -> Void) ->
}
}
Injecting a shared instance of URLSession in
APIClient
final class APIClient {
private let urlSession: URLSession
init(urlSession: URLSession = AppDelegate.shared.urlSession)
self.urlSession = urlSession
}
func get(_ url: URL, completion: @escaping (Data?) -> Void) ->
}
}
Injecting a shared instance of URLSession in
APIClient
final class APIClient {
private let urlSession: URLSession
init(urlSession: URLSession = AppDelegate.shared.urlSession)
self.urlSession = urlSession
}
func get(_ url: URL, completion: @escaping (Data?) -> Void) ->
urlSession.dataTask(with: url) { ... }.resume()
}
}
Using APIClient in MovieManager
final class MovieManager {
func getMovies(_ completion: @escaping ([Movie]?) -> Void) {
}
}
Using APIClient in MovieManager
final class MovieManager {
func getMovies(_ completion: @escaping ([Movie]?) -> Void) {
APIClient().get("http://my_movie_api/movies") { ... }
}
}
Using APIClient in MovieManager
final class MovieManager {
func getMovies(_ completion: @escaping ([Movie]?) -> Void) {
APIClient().get("http://my_movie_api/movies") { ... }
// ^ No URLSession to pass in.
}
}
Try again...
Passing down an instance of URLSession in
APIClient
final class APIClient {
private let urlSession: URLSession
init(_ urlSession: URLSession) {
self.urlSession = urlSession
}
func get(_ url: URL, completion: @escaping (Data?) -> Void) ->
urlSession.dataTask(with: url) { ... }.resume()
}
}
Passing down an instance of URLSession in
APIClient
final class APIClient {
private let urlSession: URLSession
init(_ urlSession: URLSession) { // <- No default anymore
self.urlSession = urlSession
}
func get(_ url: URL, completion: @escaping (Data?) -> Void) ->
urlSession.dataTask(with: url) { ... }.resume()
}
}
From MovieManager to APIClient
final class MovieManager {
func getMovies(_ completion: @escaping ([Movie]?) -> Void) {
}
}
From MovieManager to APIClient
final class MovieManager {
private let apiClient: APIClient
func getMovies(_ completion: @escaping ([Movie]?) -> Void) {
}
}
From MovieManager to APIClient
final class MovieManager {
private let apiClient: APIClient
init(urlSession: URLSession) {
apiClient = APIClient(urlSession)
}
func getMovies(_ completion: @escaping ([Movie]?) -> Void) {
}
}
From MovieManager to APIClient
final class MovieManager {
private let apiClient: APIClient
init(urlSession: URLSession) {
apiClient = APIClient(urlSession)
}
func getMovies(_ completion: @escaping ([Movie]?) -> Void) {
apiClient.get("http://my_movie_api/movies") { ... }
}
}
From HomeViewController to MovieManager
final class HomeViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
}
From HomeViewController to MovieManager
final class HomeViewController {
private let movieManager: MovieManager
override func viewDidLoad() {
super.viewDidLoad()
}
}
From HomeViewController to MovieManager
final class HomeViewController {
private let movieManager: MovieManager
init(_ urlSession: URLSession) {
movieManager = MovieManager(urlSession: urlSession)
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
From HomeViewController to MovieManager
final class HomeViewController {
private let movieManager: MovieManager
init(_ urlSession: URLSession) {
movieManager = MovieManager(urlSession: urlSession)
}
override func viewDidLoad() {
super.viewDidLoad()
movieManager.getMovies { ... }
}
}
From AppDelegate to HomeViewController
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func applicationDidFinishLaunching(_ application: UIApplicati
}
}
From AppDelegate to HomeViewController
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func applicationDidFinishLaunching(_ application: UIApplicati
let configuration = ...
let urlSession = URLSession(configuration: configuration)
}
}
From AppDelegate to HomeViewController
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func applicationDidFinishLaunching(_ application: UIApplicati
let configuration = ...
let urlSession = URLSession(configuration: configuration)
let controller = HomeViewController(urlSession)
...
}
}
Well done!
WHY IS THIS BETTER?WHY IS THIS BETTER?
WHY IS THIS BETTER?WHY IS THIS BETTER?
MODULARITYMODULARITY
What if ImageManager needs a bigger HTTP cache?
final class ImageManager {
}
What if ImageManager needs a bigger HTTP cache?
final class ImageManager {
init() {
}
}
What if ImageManager needs a bigger HTTP cache?
final class ImageManager {
init() {
let configuration = ...
}
}
What if ImageManager needs a bigger HTTP cache?
final class ImageManager {
init() {
let configuration = ...
config.urlCache?.diskCapacity = 1024 * 1024 * 50
config.urlCache?.memoryCapacity = 1024 * 1024 * 5
}
}
What if ImageManager needs a bigger HTTP cache?
final class ImageManager {
init() {
let configuration = ...
config.urlCache?.diskCapacity = 1024 * 1024 * 50
config.urlCache?.memoryCapacity = 1024 * 1024 * 5
let urlSession = URLSession(configuration: configuration)
}
}
What if ImageManager needs a bigger HTTP cache?
final class ImageManager {
private let apiClient: APIClient
init() {
let configuration = ...
config.urlCache?.diskCapacity = 1024 * 1024 * 50
config.urlCache?.memoryCapacity = 1024 * 1024 * 5
let urlSession = URLSession(configuration: configuration)
apiClient = APIClient(urlSession)
}
}
What if ImageManager needs a bigger HTTP cache?
final class ImageManager {
private let apiClient: APIClient
init() {
let configuration = ...
config.urlCache?.diskCapacity = 1024 * 1024 * 50
config.urlCache?.memoryCapacity = 1024 * 1024 * 5
let urlSession = URLSession(configuration: configuration)
apiClient = APIClient(urlSession)
}
func getImage(_ image: String, completion: @escaping (UIImage
apiClient.get("http://my_movie_api/images/(image)") { ..
}
}
That was easy!
Great! But this is too much code to write...
In a real project, we'd have to pass down dozens of
dependencies
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func applicationDidFinishLaunching(_ application: UIApplicati
let controller = HomeViewController(
)
}
}
In a real project, we'd have to pass down dozens of
dependencies
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func applicationDidFinishLaunching(_ application: UIApplicati
let controller = HomeViewController(
sessionManager: sessionManager,
)
}
}
In a real project, we'd have to pass down dozens of
dependencies
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func applicationDidFinishLaunching(_ application: UIApplicati
let controller = HomeViewController(
sessionManager: sessionManager,
analyticsManager: analyticsManager,
)
}
}
In a real project, we'd have to pass down dozens of
dependencies
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func applicationDidFinishLaunching(_ application: UIApplicati
let controller = HomeViewController(
sessionManager: sessionManager,
analyticsManager: analyticsManager,
reachabilityManager: reachabilityManager,
)
}
}
In a real project, we'd have to pass down dozens of
dependencies
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func applicationDidFinishLaunching(_ application: UIApplicati
let controller = HomeViewController(
sessionManager: sessionManager,
analyticsManager: analyticsManager,
reachabilityManager: reachabilityManager,
logger: logger,
)
}
}
In a real project, we'd have to pass down dozens of
dependencies
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func applicationDidFinishLaunching(_ application: UIApplicati
let controller = HomeViewController(
sessionManager: sessionManager,
analyticsManager: analyticsManager,
reachabilityManager: reachabilityManager,
logger: logger,
...
)
}
}
In a real project, we'd have to pass down dozens of
dependencies
Ouch!
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func applicationDidFinishLaunching(_ application: UIApplicati
let controller = HomeViewController(
sessionManager: sessionManager,
analyticsManager: analyticsManager,
reachabilityManager: reachabilityManager,
logger: logger,
...
)
}
}
THAT'S WHERE WEAVER COMES HANDY.THAT'S WHERE WEAVER COMES HANDY.
WHAT METHODOLOGY HAVE WE USED SO FAR?WHAT METHODOLOGY HAVE WE USED SO FAR?
WHAT METHODOLOGY HAVE WE USED SO FAR?WHAT METHODOLOGY HAVE WE USED SO FAR?
1. Looked at the code.
WHAT METHODOLOGY HAVE WE USED SO FAR?WHAT METHODOLOGY HAVE WE USED SO FAR?
1. Looked at the code.
2. Built a representation of the dependency graph.
WHAT METHODOLOGY HAVE WE USED SO FAR?WHAT METHODOLOGY HAVE WE USED SO FAR?
1. Looked at the code.
2. Built a representation of the dependency graph.
3. Made sure the dependency graph was ok.
WHAT METHODOLOGY HAVE WE USED SO FAR?WHAT METHODOLOGY HAVE WE USED SO FAR?
1. Looked at the code.
2. Built a representation of the dependency graph.
3. Made sure the dependency graph was ok.
4. Used a DI technique to implement the graph.
WEAVER DOES THE SAMEWEAVER DOES THE SAME
WEAVER DOES THE SAMEWEAVER DOES THE SAME BUT AUTOMATICALLYBUT AUTOMATICALLY
WEAVER DOES THE SAMEWEAVER DOES THE SAME BUT AUTOMATICALLYBUT AUTOMATICALLY
1. Scans the code, looking for annotations.
WEAVER DOES THE SAMEWEAVER DOES THE SAME BUT AUTOMATICALLYBUT AUTOMATICALLY
1. Scans the code, looking for annotations.
2. Builds a representation of the dependency graph.
WEAVER DOES THE SAMEWEAVER DOES THE SAME BUT AUTOMATICALLYBUT AUTOMATICALLY
1. Scans the code, looking for annotations.
2. Builds a representation of the dependency graph.
3. Validates the dependency graph.
WEAVER DOES THE SAMEWEAVER DOES THE SAME BUT AUTOMATICALLYBUT AUTOMATICALLY
1. Scans the code, looking for annotations.
2. Builds a representation of the dependency graph.
3. Validates the dependency graph.
4. Generates the code to implement the graph.
BACK TO OUR EXAMPLEBACK TO OUR EXAMPLE
Referencing to URLSession in APIClient
Referencing to URLSession in APIClient
final class APIClient {
func get(_ url: URL, completion: @escaping (Data?) -> Void) ->
}
}
Referencing to URLSession in APIClient
final class APIClient {
private let dependencies: APIClientDependencyResolver
init(injecting dependencies: APIClientDependencyResolver) {
self.dependencies = dependencies
}
func get(_ url: URL, completion: @escaping (Data?) -> Void) ->
}
}
Referencing to URLSession in APIClient
final class APIClient {
private let dependencies: APIClientDependencyResolver
// weaver: urlSession <- URLSession
init(injecting dependencies: APIClientDependencyResolver) {
self.dependencies = dependencies
}
func get(_ url: URL, completion: @escaping (Data?) -> Void) ->
}
}
Referencing to URLSession in APIClient
final class APIClient {
private let dependencies: APIClientDependencyResolver
// weaver: urlSession <- URLSession
init(injecting dependencies: APIClientDependencyResolver) {
self.dependencies = dependencies
}
func get(_ url: URL, completion: @escaping (Data?) -> Void) ->
dependencies.urlSession.dataTask(with: url) { ... }.resum
}
}
Referencing to URLSession in APIClient
GENERATED CODEGENERATED CODE
Referencing to URLSession in APIClient
GENERATED CODEGENERATED CODE
protocol APIClientInputDependencyResolver {
var urlSession: URLSession { get }
}
Referencing to URLSession in APIClient
GENERATED CODEGENERATED CODE
protocol APIClientInputDependencyResolver {
var urlSession: URLSession { get }
}
protocol APIClientDependencyResolver {
var urlSession: URLSession { get }
}
Referencing to URLSession in APIClient
GENERATED CODEGENERATED CODE
protocol APIClientInputDependencyResolver {
var urlSession: URLSession { get }
}
protocol APIClientDependencyResolver {
var urlSession: URLSession { get }
}
final class APIClientDependencyContainer: APIClientDependencyReso
let urlSession: URLSession
init(injecting dependencies: APIClientInputDependencyResolver
urlSession = dependencies.urlSession
}
}
Registering APIClient in MovieManager
final class MovieManager {
func getMovies(_ completion: @escaping ([Movie]?) -> Void) {
}
}
Registering APIClient in MovieManager
final class MovieManager {
private let dependencies: MovieManagerDependencyResolver
init(injecting dependencies: MovieManagerDependencyResolver)
self.dependencies = dependencies
}
func getMovies(_ completion: @escaping ([Movie]?) -> Void) {
}
}
Registering APIClient in MovieManager
final class MovieManager {
private let dependencies: MovieManagerDependencyResolver
// weaver: apiClient = APIClient
init(injecting dependencies: MovieManagerDependencyResolver)
self.dependencies = dependencies
}
func getMovies(_ completion: @escaping ([Movie]?) -> Void) {
}
}
Registering APIClient in MovieManager
final class MovieManager {
private let dependencies: MovieManagerDependencyResolver
// weaver: apiClient = APIClient
init(injecting dependencies: MovieManagerDependencyResolver)
self.dependencies = dependencies
}
func getMovies(_ completion: @escaping ([Movie]?) -> Void) {
dependencies.apiClient.get("http://my_movie_api/movies")
}
}
Registering APIClient in MovieManager
GENERATED CODEGENERATED CODE
Registering APIClient in MovieManager
GENERATED CODEGENERATED CODE
protocol MovieManagerInputDependencyResolver {
var urlSession: URLSession { get }
}
Registering APIClient in MovieManager
GENERATED CODEGENERATED CODE
protocol MovieManagerInputDependencyResolver {
var urlSession: URLSession { get }
}
protocol MovieManagerDependencyResolver {
var apiClient: APIClient { get }
}
Registering APIClient in MovieManager
GENERATED CODEGENERATED CODE
protocol MovieManagerInputDependencyResolver {
var urlSession: URLSession { get }
}
protocol MovieManagerDependencyResolver {
var apiClient: APIClient { get }
}
final class MovieManagerDependencyContainer: MovieManagerDependencyResolv
let urlSession: URLSession
var apiClient: APIClient {
let dependencies = APIClientDependencyContainer(injecting: self)
return APIClient(injecting: dependencies)
}
init(injecting dependencies: MovieManagerInputDependencyResolver) {
urlSession = dependencies.urlSession
}
}
Registering APIClient in MovieManager
GENERATED CODEGENERATED CODE
protocol MovieManagerInputDependencyResolver {
var urlSession: URLSession { get }
}
protocol MovieManagerDependencyResolver {
var apiClient: APIClient { get }
}
final class MovieManagerDependencyContainer: MovieManagerDependencyResolv
let urlSession: URLSession
var apiClient: APIClient {
let dependencies = APIClientDependencyContainer(injecting: self)
return APIClient(injecting: dependencies)
}
init(injecting dependencies: MovieManagerInputDependencyResolver) {
urlSession = dependencies.urlSession
}
}
extension MovieManagerDependencyContainer: APIClientInputDependencyResolv
Registering MovieManager in
HomeViewController
final class HomeViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
}
Registering MovieManager in
HomeViewController
final class HomeViewController {
private let dependencies: HomeViewControllerDependencyResolve
init(injecting dependencies: HomeViewControllerDependencyReso
self.dependencies = dependencies
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
Registering MovieManager in
HomeViewController
final class HomeViewController {
private let dependencies: HomeViewControllerDependencyResolve
// weaver: movieManager = MovieManager
init(injecting dependencies: HomeViewControllerDependencyReso
self.dependencies = dependencies
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
Registering MovieManager in
HomeViewController
final class HomeViewController {
private let dependencies: HomeViewControllerDependencyResolve
// weaver: movieManager = MovieManager
init(injecting dependencies: HomeViewControllerDependencyReso
self.dependencies = dependencies
}
override func viewDidLoad() {
super.viewDidLoad()
dependencies.movieManager.getMovies { ... }
}
}
Registering MovieManager in
HomeViewController
GENERATED CODEGENERATED CODE
Registering MovieManager in
HomeViewController
GENERATED CODEGENERATED CODE
protocol HomeViewControllerInputDependencyResolver {
var urlSession: URLSession { get }
}
Registering MovieManager in
HomeViewController
GENERATED CODEGENERATED CODE
protocol HomeViewControllerInputDependencyResolver {
var urlSession: URLSession { get }
}
protocol HomeViewControllerDependencyResolver {
var movieManager: MovieManager { get }
}
Registering MovieManager in
HomeViewController
GENERATED CODEGENERATED CODE
protocol HomeViewControllerInputDependencyResolver {
var urlSession: URLSession { get }
}
protocol HomeViewControllerDependencyResolver {
var movieManager: MovieManager { get }
}
final class HomeViewControllerDependencyContainer: HomeViewControllerDepe
let urlSession: URLSession
var movieManager: MovieManager {
let dependencies = MovieManagerDependencyContainer(injecting: sel
return MovieManager(injecting: dependencies)
}
init(injecting dependencies: HomeViewControllerInputDependencyResolve
urlSession = dependencies.urlSession
}
}
Registering MovieManager in
HomeViewController
GENERATED CODEGENERATED CODE
protocol HomeViewControllerInputDependencyResolver {
var urlSession: URLSession { get }
}
protocol HomeViewControllerDependencyResolver {
var movieManager: MovieManager { get }
}
final class HomeViewControllerDependencyContainer: HomeViewControllerDepe
let urlSession: URLSession
var movieManager: MovieManager {
let dependencies = MovieManagerDependencyContainer(injecting: sel
return MovieManager(injecting: dependencies)
}
init(injecting dependencies: HomeViewControllerInputDependencyResolve
urlSession = dependencies.urlSession
}
}
extension HomeViewControllerDependencyContainer: MovieManagerInputDepende
COMPILING...COMPILING...
COMPILING...COMPILING...
Oops!
URLSession cannot be resolved
Registering URLSession in AppDelegate
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
}
Registering URLSession in AppDelegate
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
private let dependencies = AppDelegateDependencyContainer()
}
Registering URLSession in AppDelegate
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
private let dependencies = AppDelegateDependencyContainer()
// weaver: urlSession = URLSession
}
Registering URLSession in AppDelegate
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
private let dependencies = AppDelegateDependencyContainer()
// weaver: urlSession = URLSession
// weaver: urlSession.scope = .container
}
Registering URLSession in AppDelegate
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
private let dependencies = AppDelegateDependencyContainer()
// weaver: urlSession = URLSession
// weaver: urlSession.scope = .container
// weaver: urlSession.builder = AppDelegate.makeURLSession
}
Registering URLSession in AppDelegate
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
private let dependencies = AppDelegateDependencyContainer()
// weaver: urlSession = URLSession
// weaver: urlSession.scope = .container
// weaver: urlSession.builder = AppDelegate.makeURLSession
static func makeURLSession(_: AppDelegateDependencyResolver)
let configuration = ...
return URLSession(configuration: configuration)
}
}
Registering URLSession in AppDelegate
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
private let dependencies = AppDelegateDependencyContainer()
// weaver: urlSession = URLSession
// weaver: urlSession.scope = .container
// weaver: urlSession.builder = AppDelegate.makeURLSession
static func makeURLSession(_: AppDelegateDependencyResolver)
let configuration = ...
return URLSession(configuration: configuration)
}
// weaver: homeViewController = HomeViewController
}
Registering URLSession in AppDelegate
GENERATED CODEGENERATED CODE
Registering URLSession in AppDelegate
GENERATED CODEGENERATED CODE
protocol AppDelegateDependencyResolver {
var homeViewController: HomeViewController { get }
}
Registering URLSession in AppDelegate
GENERATED CODEGENERATED CODE
protocol AppDelegateDependencyResolver {
var homeViewController: HomeViewController { get }
}
final class AppDelegateDependencyContainer: AppDelegateDependencyResolver
let urlSession: URLSession
var homeViewController: HomeViewController {
let dependencies = HomeViewControllerDependencyContainer(injectin
return HomeViewController(injecting: dependencies)
}
init() {
urlSession = URLSession.makeURLSession(self)
}
}
Registering URLSession in AppDelegate
GENERATED CODEGENERATED CODE
protocol AppDelegateDependencyResolver {
var homeViewController: HomeViewController { get }
}
final class AppDelegateDependencyContainer: AppDelegateDependencyResolver
let urlSession: URLSession
var homeViewController: HomeViewController {
let dependencies = HomeViewControllerDependencyContainer(injectin
return HomeViewController(injecting: dependencies)
}
init() {
urlSession = URLSession.makeURLSession(self)
}
}
extension AppDelegateDependencyContainer: HomeViewControllerInputDependen
It works!
STILL MODULAR?STILL MODULAR?
STILL MODULAR?STILL MODULAR?
YESYES
Registering URLSession with a bigger HTTP cache in
ImageManager
final class ImageManager {
}
Registering URLSession with a bigger HTTP cache in
ImageManager
final class ImageManager {
private let dependencies: ImageManagerDependencyResolver
init(injecting dependencies: ImageManagerDependencyResolver) { self.d
}
Registering URLSession with a bigger HTTP cache in
ImageManager
final class ImageManager {
private let dependencies: ImageManagerDependencyResolver
// weaver: urlSession = URLSession
// weaver: urlSession.scope = .container
// weaver: urlSession.builder = ImageManager.makeURLSession
init(injecting dependencies: ImageManagerDependencyResolver) { self.d
}
Registering URLSession with a bigger HTTP cache in
ImageManager
final class ImageManager {
private let dependencies: ImageManagerDependencyResolver
// weaver: urlSession = URLSession
// weaver: urlSession.scope = .container
// weaver: urlSession.builder = ImageManager.makeURLSession
init(injecting dependencies: ImageManagerDependencyResolver) { self.d
static func makeURLSession(_: ImageManagerDependencyResolver) -> URLS
let configuration = ...
configuration.urlCache?.diskCapacity = 1024 * 1024 * 50
configuration.urlCache?.memoryCapacity = 1024 * 1024 * 5
return URLSession(configuration: configuration)
}
}
Registering URLSession in ImageManager
GENERATED CODEGENERATED CODE
Registering URLSession in ImageManager
GENERATED CODEGENERATED CODE
protocol ImageManagerDependencyResolver {
var apiClient: APIClient { get }
}
Registering URLSession in ImageManager
GENERATED CODEGENERATED CODE
protocol ImageManagerDependencyResolver {
var apiClient: APIClient { get }
}
final class ImageManagerDependencyContainer: ImageManagerDependencyResolv
let urlSession: URLSession
var apiClient: APIClient {
let dependencies = APIClientDependencyContainer(injecting: self)
return APIClient(injecting: dependencies)
}
init() {
urlSession = URLSession.makeURLSession(self)
}
}
Registering URLSession in ImageManager
GENERATED CODEGENERATED CODE
protocol ImageManagerDependencyResolver {
var apiClient: APIClient { get }
}
final class ImageManagerDependencyContainer: ImageManagerDependencyResolv
let urlSession: URLSession
var apiClient: APIClient {
let dependencies = APIClientDependencyContainer(injecting: self)
return APIClient(injecting: dependencies)
}
init() {
urlSession = URLSession.makeURLSession(self)
}
}
extension ImageManagerDependencyContainer: APIClientInputDependencyResolv
Works out of the box
ROOM FOR IMPROVEMENTROOM FOR IMPROVEMENT
ROOM FOR IMPROVEMENTROOM FOR IMPROVEMENT
Adopt Weaver more widely at Scribd.
ROOM FOR IMPROVEMENTROOM FOR IMPROVEMENT
Adopt Weaver more widely at Scribd.
Dependency graph visualizer.
ROOM FOR IMPROVEMENTROOM FOR IMPROVEMENT
Adopt Weaver more widely at Scribd.
Dependency graph visualizer.
Dependency graph optimizations.
ROOM FOR IMPROVEMENTROOM FOR IMPROVEMENT
Adopt Weaver more widely at Scribd.
Dependency graph visualizer.
Dependency graph optimizations.
Detect retain cycles.
ROOM FOR IMPROVEMENTROOM FOR IMPROVEMENT
Adopt Weaver more widely at Scribd.
Dependency graph visualizer.
Dependency graph optimizations.
Detect retain cycles.
Detect suspicious graph shapes.
ROOM FOR IMPROVEMENTROOM FOR IMPROVEMENT
Adopt Weaver more widely at Scribd.
Dependency graph visualizer.
Dependency graph optimizations.
Detect retain cycles.
Detect suspicious graph shapes.
...
THE END.THE END.

Maintaining a dependency graph with weaver

  • 1.
    MAINTAINING A DEPENDENCYGRAPH WITHMAINTAINING A DEPENDENCY GRAPH WITH WEAVERWEAVER
  • 2.
  • 3.
  • 4.
    DI ContainerDI Container Anobject able to instantiate, retain, and resolve other objects’ dependencies.
  • 5.
  • 6.
    final class Foo{ let bar: Bar init(bar: Bar) { self.bar = bar bar.foo = self } func barDidSomething() { ... } } final class Bar { weak var foo: Foo? func doSomething() { foo?.barDidSomething() } }
  • 7.
    Weaver is acode generation tool which makes it easy to inject dependencies. https://github.com/scribd/Weaver
  • 8.
  • 9.
    WHY?WHY? I could notnd any satisfying compile time DI solution, so I gave it a try.
  • 10.
  • 11.
    ADVANTAGESADVANTAGES 1. Mimics manualDI technics. No black box.
  • 12.
    ADVANTAGESADVANTAGES 1. Mimics manualDI technics. No black box. 2. Compile time errors. Fails fast.
  • 13.
    ADVANTAGESADVANTAGES 1. Mimics manualDI technics. No black box. 2. Compile time errors. Fails fast. 3. Type safety. If it compiles, it works!
  • 14.
    ADVANTAGESADVANTAGES 1. Mimics manualDI technics. No black box. 2. Compile time errors. Fails fast. 3. Type safety. If it compiles, it works! 4. Declarativeness.
  • 15.
    ADVANTAGESADVANTAGES 1. Mimics manualDI technics. No black box. 2. Compile time errors. Fails fast. 3. Type safety. If it compiles, it works! 4. Declarativeness. 5. No optionality. If it's declared, it's there.
  • 16.
    ADVANTAGESADVANTAGES 1. Mimics manualDI technics. No black box. 2. Compile time errors. Fails fast. 3. Type safety. If it compiles, it works! 4. Declarativeness. 5. No optionality. If it's declared, it's there. 6. Thread safety. Immutability. No lazy loading.
  • 17.
    ADVANTAGESADVANTAGES 1. Mimics manualDI technics. No black box. 2. Compile time errors. Fails fast. 3. Type safety. If it compiles, it works! 4. Declarativeness. 5. No optionality. If it's declared, it's there. 6. Thread safety. Immutability. No lazy loading. 7. No additional framework to ship.
  • 18.
  • 19.
    DRAWBACKSDRAWBACKS 1. More codeto compile. Can be optimized.
  • 20.
    DRAWBACKSDRAWBACKS 1. More codeto compile. Can be optimized. 2. Meta-programmation is hard.
  • 21.
    DRAWBACKSDRAWBACKS 1. More codeto compile. Can be optimized. 2. Meta-programmation is hard. 3. Use of pseudo annotations.
  • 22.
    DRAWBACKSDRAWBACKS 1. More codeto compile. Can be optimized. 2. Meta-programmation is hard. 3. Use of pseudo annotations. 4. Experimental.
  • 23.
    LET'S SEE INPRACTICELET'S SEE IN PRACTICE
  • 25.
  • 26.
    HOW COULD WEPROVIDEHOW COULD WE PROVIDE URLSessionURLSession??
  • 27.
    Sharing an instanceof URLSession @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { }
  • 28.
    Sharing an instanceof URLSession @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { static var shared: AppDelegate? { return UIApplication.shared.delegate as? AppDelegate } }
  • 29.
    Sharing an instanceof URLSession @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { static var shared: AppDelegate? { return UIApplication.shared.delegate as? AppDelegate } let urlSession: URLSession = { let config = ... return URLSession(configuration: config) }() }
  • 30.
    Using the sharedinstance of URLSession final class APIClient { func get(_ url: URL, completion: @escaping (Data?) -> Void) -> } }
  • 31.
    Using the sharedinstance of URLSession final class APIClient { func get(_ url: URL, completion: @escaping (Data?) -> Void) -> AppDelegate.shared.urlSession.dataTask(with: url) { ... }.resume() } }
  • 33.
  • 34.
    Injecting a sharedinstance of URLSession in APIClient final class APIClient { func get(_ url: URL, completion: @escaping (Data?) -> Void) -> } }
  • 35.
    Injecting a sharedinstance of URLSession in APIClient final class APIClient { private let urlSession: URLSession func get(_ url: URL, completion: @escaping (Data?) -> Void) -> } }
  • 36.
    Injecting a sharedinstance of URLSession in APIClient final class APIClient { private let urlSession: URLSession init(urlSession: URLSession = AppDelegate.shared.urlSession) self.urlSession = urlSession } func get(_ url: URL, completion: @escaping (Data?) -> Void) -> } }
  • 37.
    Injecting a sharedinstance of URLSession in APIClient final class APIClient { private let urlSession: URLSession init(urlSession: URLSession = AppDelegate.shared.urlSession) self.urlSession = urlSession } func get(_ url: URL, completion: @escaping (Data?) -> Void) -> urlSession.dataTask(with: url) { ... }.resume() } }
  • 38.
    Using APIClient inMovieManager final class MovieManager { func getMovies(_ completion: @escaping ([Movie]?) -> Void) { } }
  • 39.
    Using APIClient inMovieManager final class MovieManager { func getMovies(_ completion: @escaping ([Movie]?) -> Void) { APIClient().get("http://my_movie_api/movies") { ... } } }
  • 40.
    Using APIClient inMovieManager final class MovieManager { func getMovies(_ completion: @escaping ([Movie]?) -> Void) { APIClient().get("http://my_movie_api/movies") { ... } // ^ No URLSession to pass in. } }
  • 42.
  • 43.
    Passing down aninstance of URLSession in APIClient final class APIClient { private let urlSession: URLSession init(_ urlSession: URLSession) { self.urlSession = urlSession } func get(_ url: URL, completion: @escaping (Data?) -> Void) -> urlSession.dataTask(with: url) { ... }.resume() } }
  • 44.
    Passing down aninstance of URLSession in APIClient final class APIClient { private let urlSession: URLSession init(_ urlSession: URLSession) { // <- No default anymore self.urlSession = urlSession } func get(_ url: URL, completion: @escaping (Data?) -> Void) -> urlSession.dataTask(with: url) { ... }.resume() } }
  • 45.
    From MovieManager toAPIClient final class MovieManager { func getMovies(_ completion: @escaping ([Movie]?) -> Void) { } }
  • 46.
    From MovieManager toAPIClient final class MovieManager { private let apiClient: APIClient func getMovies(_ completion: @escaping ([Movie]?) -> Void) { } }
  • 47.
    From MovieManager toAPIClient final class MovieManager { private let apiClient: APIClient init(urlSession: URLSession) { apiClient = APIClient(urlSession) } func getMovies(_ completion: @escaping ([Movie]?) -> Void) { } }
  • 48.
    From MovieManager toAPIClient final class MovieManager { private let apiClient: APIClient init(urlSession: URLSession) { apiClient = APIClient(urlSession) } func getMovies(_ completion: @escaping ([Movie]?) -> Void) { apiClient.get("http://my_movie_api/movies") { ... } } }
  • 49.
    From HomeViewController toMovieManager final class HomeViewController { override func viewDidLoad() { super.viewDidLoad() } }
  • 50.
    From HomeViewController toMovieManager final class HomeViewController { private let movieManager: MovieManager override func viewDidLoad() { super.viewDidLoad() } }
  • 51.
    From HomeViewController toMovieManager final class HomeViewController { private let movieManager: MovieManager init(_ urlSession: URLSession) { movieManager = MovieManager(urlSession: urlSession) } override func viewDidLoad() { super.viewDidLoad() } }
  • 52.
    From HomeViewController toMovieManager final class HomeViewController { private let movieManager: MovieManager init(_ urlSession: URLSession) { movieManager = MovieManager(urlSession: urlSession) } override func viewDidLoad() { super.viewDidLoad() movieManager.getMovies { ... } } }
  • 53.
    From AppDelegate toHomeViewController @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { func applicationDidFinishLaunching(_ application: UIApplicati } }
  • 54.
    From AppDelegate toHomeViewController @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { func applicationDidFinishLaunching(_ application: UIApplicati let configuration = ... let urlSession = URLSession(configuration: configuration) } }
  • 55.
    From AppDelegate toHomeViewController @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { func applicationDidFinishLaunching(_ application: UIApplicati let configuration = ... let urlSession = URLSession(configuration: configuration) let controller = HomeViewController(urlSession) ... } }
  • 57.
  • 58.
    WHY IS THISBETTER?WHY IS THIS BETTER?
  • 59.
    WHY IS THISBETTER?WHY IS THIS BETTER? MODULARITYMODULARITY
  • 60.
    What if ImageManagerneeds a bigger HTTP cache? final class ImageManager { }
  • 61.
    What if ImageManagerneeds a bigger HTTP cache? final class ImageManager { init() { } }
  • 62.
    What if ImageManagerneeds a bigger HTTP cache? final class ImageManager { init() { let configuration = ... } }
  • 63.
    What if ImageManagerneeds a bigger HTTP cache? final class ImageManager { init() { let configuration = ... config.urlCache?.diskCapacity = 1024 * 1024 * 50 config.urlCache?.memoryCapacity = 1024 * 1024 * 5 } }
  • 64.
    What if ImageManagerneeds a bigger HTTP cache? final class ImageManager { init() { let configuration = ... config.urlCache?.diskCapacity = 1024 * 1024 * 50 config.urlCache?.memoryCapacity = 1024 * 1024 * 5 let urlSession = URLSession(configuration: configuration) } }
  • 65.
    What if ImageManagerneeds a bigger HTTP cache? final class ImageManager { private let apiClient: APIClient init() { let configuration = ... config.urlCache?.diskCapacity = 1024 * 1024 * 50 config.urlCache?.memoryCapacity = 1024 * 1024 * 5 let urlSession = URLSession(configuration: configuration) apiClient = APIClient(urlSession) } }
  • 66.
    What if ImageManagerneeds a bigger HTTP cache? final class ImageManager { private let apiClient: APIClient init() { let configuration = ... config.urlCache?.diskCapacity = 1024 * 1024 * 50 config.urlCache?.memoryCapacity = 1024 * 1024 * 5 let urlSession = URLSession(configuration: configuration) apiClient = APIClient(urlSession) } func getImage(_ image: String, completion: @escaping (UIImage apiClient.get("http://my_movie_api/images/(image)") { .. } }
  • 68.
  • 69.
    Great! But thisis too much code to write...
  • 70.
    In a realproject, we'd have to pass down dozens of dependencies @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { func applicationDidFinishLaunching(_ application: UIApplicati let controller = HomeViewController( ) } }
  • 71.
    In a realproject, we'd have to pass down dozens of dependencies @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { func applicationDidFinishLaunching(_ application: UIApplicati let controller = HomeViewController( sessionManager: sessionManager, ) } }
  • 72.
    In a realproject, we'd have to pass down dozens of dependencies @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { func applicationDidFinishLaunching(_ application: UIApplicati let controller = HomeViewController( sessionManager: sessionManager, analyticsManager: analyticsManager, ) } }
  • 73.
    In a realproject, we'd have to pass down dozens of dependencies @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { func applicationDidFinishLaunching(_ application: UIApplicati let controller = HomeViewController( sessionManager: sessionManager, analyticsManager: analyticsManager, reachabilityManager: reachabilityManager, ) } }
  • 74.
    In a realproject, we'd have to pass down dozens of dependencies @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { func applicationDidFinishLaunching(_ application: UIApplicati let controller = HomeViewController( sessionManager: sessionManager, analyticsManager: analyticsManager, reachabilityManager: reachabilityManager, logger: logger, ) } }
  • 75.
    In a realproject, we'd have to pass down dozens of dependencies @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { func applicationDidFinishLaunching(_ application: UIApplicati let controller = HomeViewController( sessionManager: sessionManager, analyticsManager: analyticsManager, reachabilityManager: reachabilityManager, logger: logger, ... ) } }
  • 76.
    In a realproject, we'd have to pass down dozens of dependencies Ouch! @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { func applicationDidFinishLaunching(_ application: UIApplicati let controller = HomeViewController( sessionManager: sessionManager, analyticsManager: analyticsManager, reachabilityManager: reachabilityManager, logger: logger, ... ) } }
  • 77.
    THAT'S WHERE WEAVERCOMES HANDY.THAT'S WHERE WEAVER COMES HANDY.
  • 78.
    WHAT METHODOLOGY HAVEWE USED SO FAR?WHAT METHODOLOGY HAVE WE USED SO FAR?
  • 79.
    WHAT METHODOLOGY HAVEWE USED SO FAR?WHAT METHODOLOGY HAVE WE USED SO FAR? 1. Looked at the code.
  • 80.
    WHAT METHODOLOGY HAVEWE USED SO FAR?WHAT METHODOLOGY HAVE WE USED SO FAR? 1. Looked at the code. 2. Built a representation of the dependency graph.
  • 81.
    WHAT METHODOLOGY HAVEWE USED SO FAR?WHAT METHODOLOGY HAVE WE USED SO FAR? 1. Looked at the code. 2. Built a representation of the dependency graph. 3. Made sure the dependency graph was ok.
  • 82.
    WHAT METHODOLOGY HAVEWE USED SO FAR?WHAT METHODOLOGY HAVE WE USED SO FAR? 1. Looked at the code. 2. Built a representation of the dependency graph. 3. Made sure the dependency graph was ok. 4. Used a DI technique to implement the graph.
  • 83.
    WEAVER DOES THESAMEWEAVER DOES THE SAME
  • 84.
    WEAVER DOES THESAMEWEAVER DOES THE SAME BUT AUTOMATICALLYBUT AUTOMATICALLY
  • 85.
    WEAVER DOES THESAMEWEAVER DOES THE SAME BUT AUTOMATICALLYBUT AUTOMATICALLY 1. Scans the code, looking for annotations.
  • 86.
    WEAVER DOES THESAMEWEAVER DOES THE SAME BUT AUTOMATICALLYBUT AUTOMATICALLY 1. Scans the code, looking for annotations. 2. Builds a representation of the dependency graph.
  • 87.
    WEAVER DOES THESAMEWEAVER DOES THE SAME BUT AUTOMATICALLYBUT AUTOMATICALLY 1. Scans the code, looking for annotations. 2. Builds a representation of the dependency graph. 3. Validates the dependency graph.
  • 88.
    WEAVER DOES THESAMEWEAVER DOES THE SAME BUT AUTOMATICALLYBUT AUTOMATICALLY 1. Scans the code, looking for annotations. 2. Builds a representation of the dependency graph. 3. Validates the dependency graph. 4. Generates the code to implement the graph.
  • 89.
    BACK TO OUREXAMPLEBACK TO OUR EXAMPLE
  • 90.
  • 91.
    Referencing to URLSessionin APIClient final class APIClient { func get(_ url: URL, completion: @escaping (Data?) -> Void) -> } }
  • 92.
    Referencing to URLSessionin APIClient final class APIClient { private let dependencies: APIClientDependencyResolver init(injecting dependencies: APIClientDependencyResolver) { self.dependencies = dependencies } func get(_ url: URL, completion: @escaping (Data?) -> Void) -> } }
  • 93.
    Referencing to URLSessionin APIClient final class APIClient { private let dependencies: APIClientDependencyResolver // weaver: urlSession <- URLSession init(injecting dependencies: APIClientDependencyResolver) { self.dependencies = dependencies } func get(_ url: URL, completion: @escaping (Data?) -> Void) -> } }
  • 94.
    Referencing to URLSessionin APIClient final class APIClient { private let dependencies: APIClientDependencyResolver // weaver: urlSession <- URLSession init(injecting dependencies: APIClientDependencyResolver) { self.dependencies = dependencies } func get(_ url: URL, completion: @escaping (Data?) -> Void) -> dependencies.urlSession.dataTask(with: url) { ... }.resum } }
  • 95.
    Referencing to URLSessionin APIClient GENERATED CODEGENERATED CODE
  • 96.
    Referencing to URLSessionin APIClient GENERATED CODEGENERATED CODE protocol APIClientInputDependencyResolver { var urlSession: URLSession { get } }
  • 97.
    Referencing to URLSessionin APIClient GENERATED CODEGENERATED CODE protocol APIClientInputDependencyResolver { var urlSession: URLSession { get } } protocol APIClientDependencyResolver { var urlSession: URLSession { get } }
  • 98.
    Referencing to URLSessionin APIClient GENERATED CODEGENERATED CODE protocol APIClientInputDependencyResolver { var urlSession: URLSession { get } } protocol APIClientDependencyResolver { var urlSession: URLSession { get } } final class APIClientDependencyContainer: APIClientDependencyReso let urlSession: URLSession init(injecting dependencies: APIClientInputDependencyResolver urlSession = dependencies.urlSession } }
  • 99.
    Registering APIClient inMovieManager final class MovieManager { func getMovies(_ completion: @escaping ([Movie]?) -> Void) { } }
  • 100.
    Registering APIClient inMovieManager final class MovieManager { private let dependencies: MovieManagerDependencyResolver init(injecting dependencies: MovieManagerDependencyResolver) self.dependencies = dependencies } func getMovies(_ completion: @escaping ([Movie]?) -> Void) { } }
  • 101.
    Registering APIClient inMovieManager final class MovieManager { private let dependencies: MovieManagerDependencyResolver // weaver: apiClient = APIClient init(injecting dependencies: MovieManagerDependencyResolver) self.dependencies = dependencies } func getMovies(_ completion: @escaping ([Movie]?) -> Void) { } }
  • 102.
    Registering APIClient inMovieManager final class MovieManager { private let dependencies: MovieManagerDependencyResolver // weaver: apiClient = APIClient init(injecting dependencies: MovieManagerDependencyResolver) self.dependencies = dependencies } func getMovies(_ completion: @escaping ([Movie]?) -> Void) { dependencies.apiClient.get("http://my_movie_api/movies") } }
  • 103.
    Registering APIClient inMovieManager GENERATED CODEGENERATED CODE
  • 104.
    Registering APIClient inMovieManager GENERATED CODEGENERATED CODE protocol MovieManagerInputDependencyResolver { var urlSession: URLSession { get } }
  • 105.
    Registering APIClient inMovieManager GENERATED CODEGENERATED CODE protocol MovieManagerInputDependencyResolver { var urlSession: URLSession { get } } protocol MovieManagerDependencyResolver { var apiClient: APIClient { get } }
  • 106.
    Registering APIClient inMovieManager GENERATED CODEGENERATED CODE protocol MovieManagerInputDependencyResolver { var urlSession: URLSession { get } } protocol MovieManagerDependencyResolver { var apiClient: APIClient { get } } final class MovieManagerDependencyContainer: MovieManagerDependencyResolv let urlSession: URLSession var apiClient: APIClient { let dependencies = APIClientDependencyContainer(injecting: self) return APIClient(injecting: dependencies) } init(injecting dependencies: MovieManagerInputDependencyResolver) { urlSession = dependencies.urlSession } }
  • 107.
    Registering APIClient inMovieManager GENERATED CODEGENERATED CODE protocol MovieManagerInputDependencyResolver { var urlSession: URLSession { get } } protocol MovieManagerDependencyResolver { var apiClient: APIClient { get } } final class MovieManagerDependencyContainer: MovieManagerDependencyResolv let urlSession: URLSession var apiClient: APIClient { let dependencies = APIClientDependencyContainer(injecting: self) return APIClient(injecting: dependencies) } init(injecting dependencies: MovieManagerInputDependencyResolver) { urlSession = dependencies.urlSession } } extension MovieManagerDependencyContainer: APIClientInputDependencyResolv
  • 108.
    Registering MovieManager in HomeViewController finalclass HomeViewController { override func viewDidLoad() { super.viewDidLoad() } }
  • 109.
    Registering MovieManager in HomeViewController finalclass HomeViewController { private let dependencies: HomeViewControllerDependencyResolve init(injecting dependencies: HomeViewControllerDependencyReso self.dependencies = dependencies } override func viewDidLoad() { super.viewDidLoad() } }
  • 110.
    Registering MovieManager in HomeViewController finalclass HomeViewController { private let dependencies: HomeViewControllerDependencyResolve // weaver: movieManager = MovieManager init(injecting dependencies: HomeViewControllerDependencyReso self.dependencies = dependencies } override func viewDidLoad() { super.viewDidLoad() } }
  • 111.
    Registering MovieManager in HomeViewController finalclass HomeViewController { private let dependencies: HomeViewControllerDependencyResolve // weaver: movieManager = MovieManager init(injecting dependencies: HomeViewControllerDependencyReso self.dependencies = dependencies } override func viewDidLoad() { super.viewDidLoad() dependencies.movieManager.getMovies { ... } } }
  • 112.
  • 113.
    Registering MovieManager in HomeViewController GENERATEDCODEGENERATED CODE protocol HomeViewControllerInputDependencyResolver { var urlSession: URLSession { get } }
  • 114.
    Registering MovieManager in HomeViewController GENERATEDCODEGENERATED CODE protocol HomeViewControllerInputDependencyResolver { var urlSession: URLSession { get } } protocol HomeViewControllerDependencyResolver { var movieManager: MovieManager { get } }
  • 115.
    Registering MovieManager in HomeViewController GENERATEDCODEGENERATED CODE protocol HomeViewControllerInputDependencyResolver { var urlSession: URLSession { get } } protocol HomeViewControllerDependencyResolver { var movieManager: MovieManager { get } } final class HomeViewControllerDependencyContainer: HomeViewControllerDepe let urlSession: URLSession var movieManager: MovieManager { let dependencies = MovieManagerDependencyContainer(injecting: sel return MovieManager(injecting: dependencies) } init(injecting dependencies: HomeViewControllerInputDependencyResolve urlSession = dependencies.urlSession } }
  • 116.
    Registering MovieManager in HomeViewController GENERATEDCODEGENERATED CODE protocol HomeViewControllerInputDependencyResolver { var urlSession: URLSession { get } } protocol HomeViewControllerDependencyResolver { var movieManager: MovieManager { get } } final class HomeViewControllerDependencyContainer: HomeViewControllerDepe let urlSession: URLSession var movieManager: MovieManager { let dependencies = MovieManagerDependencyContainer(injecting: sel return MovieManager(injecting: dependencies) } init(injecting dependencies: HomeViewControllerInputDependencyResolve urlSession = dependencies.urlSession } } extension HomeViewControllerDependencyContainer: MovieManagerInputDepende
  • 117.
  • 118.
  • 120.
    Registering URLSession inAppDelegate @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { }
  • 121.
    Registering URLSession inAppDelegate @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { private let dependencies = AppDelegateDependencyContainer() }
  • 122.
    Registering URLSession inAppDelegate @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { private let dependencies = AppDelegateDependencyContainer() // weaver: urlSession = URLSession }
  • 123.
    Registering URLSession inAppDelegate @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { private let dependencies = AppDelegateDependencyContainer() // weaver: urlSession = URLSession // weaver: urlSession.scope = .container }
  • 124.
    Registering URLSession inAppDelegate @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { private let dependencies = AppDelegateDependencyContainer() // weaver: urlSession = URLSession // weaver: urlSession.scope = .container // weaver: urlSession.builder = AppDelegate.makeURLSession }
  • 125.
    Registering URLSession inAppDelegate @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { private let dependencies = AppDelegateDependencyContainer() // weaver: urlSession = URLSession // weaver: urlSession.scope = .container // weaver: urlSession.builder = AppDelegate.makeURLSession static func makeURLSession(_: AppDelegateDependencyResolver) let configuration = ... return URLSession(configuration: configuration) } }
  • 126.
    Registering URLSession inAppDelegate @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { private let dependencies = AppDelegateDependencyContainer() // weaver: urlSession = URLSession // weaver: urlSession.scope = .container // weaver: urlSession.builder = AppDelegate.makeURLSession static func makeURLSession(_: AppDelegateDependencyResolver) let configuration = ... return URLSession(configuration: configuration) } // weaver: homeViewController = HomeViewController }
  • 127.
    Registering URLSession inAppDelegate GENERATED CODEGENERATED CODE
  • 128.
    Registering URLSession inAppDelegate GENERATED CODEGENERATED CODE protocol AppDelegateDependencyResolver { var homeViewController: HomeViewController { get } }
  • 129.
    Registering URLSession inAppDelegate GENERATED CODEGENERATED CODE protocol AppDelegateDependencyResolver { var homeViewController: HomeViewController { get } } final class AppDelegateDependencyContainer: AppDelegateDependencyResolver let urlSession: URLSession var homeViewController: HomeViewController { let dependencies = HomeViewControllerDependencyContainer(injectin return HomeViewController(injecting: dependencies) } init() { urlSession = URLSession.makeURLSession(self) } }
  • 130.
    Registering URLSession inAppDelegate GENERATED CODEGENERATED CODE protocol AppDelegateDependencyResolver { var homeViewController: HomeViewController { get } } final class AppDelegateDependencyContainer: AppDelegateDependencyResolver let urlSession: URLSession var homeViewController: HomeViewController { let dependencies = HomeViewControllerDependencyContainer(injectin return HomeViewController(injecting: dependencies) } init() { urlSession = URLSession.makeURLSession(self) } } extension AppDelegateDependencyContainer: HomeViewControllerInputDependen
  • 132.
  • 133.
  • 134.
  • 135.
    Registering URLSession witha bigger HTTP cache in ImageManager final class ImageManager { }
  • 136.
    Registering URLSession witha bigger HTTP cache in ImageManager final class ImageManager { private let dependencies: ImageManagerDependencyResolver init(injecting dependencies: ImageManagerDependencyResolver) { self.d }
  • 137.
    Registering URLSession witha bigger HTTP cache in ImageManager final class ImageManager { private let dependencies: ImageManagerDependencyResolver // weaver: urlSession = URLSession // weaver: urlSession.scope = .container // weaver: urlSession.builder = ImageManager.makeURLSession init(injecting dependencies: ImageManagerDependencyResolver) { self.d }
  • 138.
    Registering URLSession witha bigger HTTP cache in ImageManager final class ImageManager { private let dependencies: ImageManagerDependencyResolver // weaver: urlSession = URLSession // weaver: urlSession.scope = .container // weaver: urlSession.builder = ImageManager.makeURLSession init(injecting dependencies: ImageManagerDependencyResolver) { self.d static func makeURLSession(_: ImageManagerDependencyResolver) -> URLS let configuration = ... configuration.urlCache?.diskCapacity = 1024 * 1024 * 50 configuration.urlCache?.memoryCapacity = 1024 * 1024 * 5 return URLSession(configuration: configuration) } }
  • 139.
    Registering URLSession inImageManager GENERATED CODEGENERATED CODE
  • 140.
    Registering URLSession inImageManager GENERATED CODEGENERATED CODE protocol ImageManagerDependencyResolver { var apiClient: APIClient { get } }
  • 141.
    Registering URLSession inImageManager GENERATED CODEGENERATED CODE protocol ImageManagerDependencyResolver { var apiClient: APIClient { get } } final class ImageManagerDependencyContainer: ImageManagerDependencyResolv let urlSession: URLSession var apiClient: APIClient { let dependencies = APIClientDependencyContainer(injecting: self) return APIClient(injecting: dependencies) } init() { urlSession = URLSession.makeURLSession(self) } }
  • 142.
    Registering URLSession inImageManager GENERATED CODEGENERATED CODE protocol ImageManagerDependencyResolver { var apiClient: APIClient { get } } final class ImageManagerDependencyContainer: ImageManagerDependencyResolv let urlSession: URLSession var apiClient: APIClient { let dependencies = APIClientDependencyContainer(injecting: self) return APIClient(injecting: dependencies) } init() { urlSession = URLSession.makeURLSession(self) } } extension ImageManagerDependencyContainer: APIClientInputDependencyResolv
  • 144.
    Works out ofthe box
  • 145.
    ROOM FOR IMPROVEMENTROOMFOR IMPROVEMENT
  • 146.
    ROOM FOR IMPROVEMENTROOMFOR IMPROVEMENT Adopt Weaver more widely at Scribd.
  • 147.
    ROOM FOR IMPROVEMENTROOMFOR IMPROVEMENT Adopt Weaver more widely at Scribd. Dependency graph visualizer.
  • 148.
    ROOM FOR IMPROVEMENTROOMFOR IMPROVEMENT Adopt Weaver more widely at Scribd. Dependency graph visualizer. Dependency graph optimizations.
  • 149.
    ROOM FOR IMPROVEMENTROOMFOR IMPROVEMENT Adopt Weaver more widely at Scribd. Dependency graph visualizer. Dependency graph optimizations. Detect retain cycles.
  • 150.
    ROOM FOR IMPROVEMENTROOMFOR IMPROVEMENT Adopt Weaver more widely at Scribd. Dependency graph visualizer. Dependency graph optimizations. Detect retain cycles. Detect suspicious graph shapes.
  • 151.
    ROOM FOR IMPROVEMENTROOMFOR IMPROVEMENT Adopt Weaver more widely at Scribd. Dependency graph visualizer. Dependency graph optimizations. Detect retain cycles. Detect suspicious graph shapes. ...
  • 152.