SlideShare a Scribd company logo
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
Maintaining a dependency graph with weaver
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()
}
}
Maintaining a dependency graph with weaver
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.
}
}
Maintaining a dependency graph with weaver
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)
...
}
}
Maintaining a dependency graph with weaver
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)") { ..
}
}
Maintaining a dependency graph with weaver
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
Maintaining a dependency graph with weaver
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
Maintaining a dependency graph with weaver
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
Maintaining a dependency graph with weaver
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.

More Related Content

PDF
Workshop 26: React Native - The Native Side
PPTX
Java Libraries You Can't Afford to Miss
PDF
Asynchronous Programming at Netflix
PDF
JAX-RS and CDI Bike the (Reactive) Bridge
PDF
Java Libraries You Can't Afford To Miss
PDF
Keeping the frontend under control with Symfony and Webpack
PDF
Finally, Professional Frontend Dev with ReactJS, WebPack & Symfony (Symfony C...
PDF
Java Libraries You Can’t Afford to Miss
Workshop 26: React Native - The Native Side
Java Libraries You Can't Afford to Miss
Asynchronous Programming at Netflix
JAX-RS and CDI Bike the (Reactive) Bridge
Java Libraries You Can't Afford To Miss
Keeping the frontend under control with Symfony and Webpack
Finally, Professional Frontend Dev with ReactJS, WebPack & Symfony (Symfony C...
Java Libraries You Can’t Afford to Miss

What's hot (20)

PDF
Mobile Open Day: React Native: Crossplatform fast dive
PDF
distage: Purely Functional Staged Dependency Injection; bonus: Faking Kind Po...
KEY
Working Effectively With Legacy Code
PDF
Completable future
PPTX
Java Libraries You Can’t Afford to Miss
PDF
Java EE 6 CDI Integrates with Spring & JSF
PPTX
Making React Native UI Components with Swift
PDF
50 common web developer interview questions [2020 updated] [www.full stack....
PDF
Building maintainable app
PDF
React Native One Day
PDF
Create Your Own Framework by Fabien Potencier
PDF
Flutter hooks tutorial (part 1) flutter animation using hooks (use effect and...
PDF
Seven Versions of One Web Application
PDF
Building maintainable app #droidconzg
PPTX
Ultimate Node.js countdown: the coolest Application Express examples
PDF
Паразитируем на React-экосистеме (Angular 4+) / Алексей Охрименко (IPONWEB)
KEY
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
ODP
Dependency Injection, Zend Framework and Symfony Container
PDF
Containers & Dependency in Ember.js
PDF
Testing Java Code Effectively
Mobile Open Day: React Native: Crossplatform fast dive
distage: Purely Functional Staged Dependency Injection; bonus: Faking Kind Po...
Working Effectively With Legacy Code
Completable future
Java Libraries You Can’t Afford to Miss
Java EE 6 CDI Integrates with Spring & JSF
Making React Native UI Components with Swift
50 common web developer interview questions [2020 updated] [www.full stack....
Building maintainable app
React Native One Day
Create Your Own Framework by Fabien Potencier
Flutter hooks tutorial (part 1) flutter animation using hooks (use effect and...
Seven Versions of One Web Application
Building maintainable app #droidconzg
Ultimate Node.js countdown: the coolest Application Express examples
Паразитируем на React-экосистеме (Angular 4+) / Алексей Охрименко (IPONWEB)
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
Dependency Injection, Zend Framework and Symfony Container
Containers & Dependency in Ember.js
Testing Java Code Effectively
Ad

Similar to Maintaining a dependency graph with weaver (20)

PDF
Elements for an iOS Backend
PDF
iOS 2 - The practical Stuff
PDF
iOS Swift application architecture
PDF
Deep Dive Into Swift
PPTX
iOS Development at Scale @Chegg
PDF
Distributing information on iOS
PDF
MFF UK - Introduction to iOS
PPTX
Best practices iOS meetup - pmd
PPTX
Swift meetup22june2015
PDF
Orthogonality: A Strategy for Reusable Code
PDF
Medium TechTalk — iOS
PDF
FI MUNI 2012 - iOS Basics
PDF
iOS development best practices
PDF
Prescribing RX Responsibly
PDF
Beyond the code. Complexity - 2025.05 - SwiftCraft
PPTX
Fundamental Design Patterns.pptx
ZIP
iPhone and Rails integration
PDF
[CocoaHeads Tricity] Do not reinvent the wheel
PPTX
iOS Development using Swift: Enums, ARC, Delegation, Closures, Table View and...
PDF
MFF UK - Advanced iOS Topics
Elements for an iOS Backend
iOS 2 - The practical Stuff
iOS Swift application architecture
Deep Dive Into Swift
iOS Development at Scale @Chegg
Distributing information on iOS
MFF UK - Introduction to iOS
Best practices iOS meetup - pmd
Swift meetup22june2015
Orthogonality: A Strategy for Reusable Code
Medium TechTalk — iOS
FI MUNI 2012 - iOS Basics
iOS development best practices
Prescribing RX Responsibly
Beyond the code. Complexity - 2025.05 - SwiftCraft
Fundamental Design Patterns.pptx
iPhone and Rails integration
[CocoaHeads Tricity] Do not reinvent the wheel
iOS Development using Swift: Enums, ARC, Delegation, Closures, Table View and...
MFF UK - Advanced iOS Topics
Ad

Recently uploaded (20)

PDF
Claude Code: Everyone is a 10x Developer - A Comprehensive AI-Powered CLI Tool
PDF
How to Choose the Right IT Partner for Your Business in Malaysia
PDF
Internet Downloader Manager (IDM) Crack 6.42 Build 42 Updates Latest 2025
PDF
Internet Downloader Manager (IDM) Crack 6.42 Build 41
PPTX
ai tools demonstartion for schools and inter college
PDF
Softaken Excel to vCard Converter Software.pdf
PDF
Addressing The Cult of Project Management Tools-Why Disconnected Work is Hold...
PDF
Digital Strategies for Manufacturing Companies
PPTX
Oracle E-Business Suite: A Comprehensive Guide for Modern Enterprises
PDF
System and Network Administraation Chapter 3
PDF
Design an Analysis of Algorithms II-SECS-1021-03
PDF
Why TechBuilder is the Future of Pickup and Delivery App Development (1).pdf
PPTX
Embracing Complexity in Serverless! GOTO Serverless Bengaluru
PPT
Introduction Database Management System for Course Database
PDF
wealthsignaloriginal-com-DS-text-... (1).pdf
PPTX
Operating system designcfffgfgggggggvggggggggg
PDF
Understanding Forklifts - TECH EHS Solution
PDF
System and Network Administration Chapter 2
PPTX
VVF-Customer-Presentation2025-Ver1.9.pptx
PDF
SAP S4 Hana Brochure 3 (PTS SYSTEMS AND SOLUTIONS)
Claude Code: Everyone is a 10x Developer - A Comprehensive AI-Powered CLI Tool
How to Choose the Right IT Partner for Your Business in Malaysia
Internet Downloader Manager (IDM) Crack 6.42 Build 42 Updates Latest 2025
Internet Downloader Manager (IDM) Crack 6.42 Build 41
ai tools demonstartion for schools and inter college
Softaken Excel to vCard Converter Software.pdf
Addressing The Cult of Project Management Tools-Why Disconnected Work is Hold...
Digital Strategies for Manufacturing Companies
Oracle E-Business Suite: A Comprehensive Guide for Modern Enterprises
System and Network Administraation Chapter 3
Design an Analysis of Algorithms II-SECS-1021-03
Why TechBuilder is the Future of Pickup and Delivery App Development (1).pdf
Embracing Complexity in Serverless! GOTO Serverless Bengaluru
Introduction Database Management System for Course Database
wealthsignaloriginal-com-DS-text-... (1).pdf
Operating system designcfffgfgggggggvggggggggg
Understanding Forklifts - TECH EHS Solution
System and Network Administration Chapter 2
VVF-Customer-Presentation2025-Ver1.9.pptx
SAP S4 Hana Brochure 3 (PTS SYSTEMS AND SOLUTIONS)

Maintaining a dependency graph with weaver

  • 1. MAINTAINING A DEPENDENCY GRAPH WITHMAINTAINING A DEPENDENCY GRAPH WITH WEAVERWEAVER
  • 4. DI ContainerDI Container An object able to instantiate, retain, and resolve other objects’ dependencies.
  • 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 a code generation tool which makes it easy to inject dependencies. https://github.com/scribd/Weaver
  • 9. WHY?WHY? I could not nd any satisfying compile time DI solution, so I gave it a try.
  • 11. ADVANTAGESADVANTAGES 1. Mimics manual DI technics. No black box.
  • 12. ADVANTAGESADVANTAGES 1. Mimics manual DI technics. No black box. 2. Compile time errors. Fails fast.
  • 13. ADVANTAGESADVANTAGES 1. Mimics manual DI technics. No black box. 2. Compile time errors. Fails fast. 3. Type safety. If it compiles, it works!
  • 14. 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.
  • 15. 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.
  • 16. 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.
  • 17. 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.
  • 19. DRAWBACKSDRAWBACKS 1. More code to compile. Can be optimized.
  • 20. DRAWBACKSDRAWBACKS 1. More code to compile. Can be optimized. 2. Meta-programmation is hard.
  • 21. DRAWBACKSDRAWBACKS 1. More code to compile. Can be optimized. 2. Meta-programmation is hard. 3. Use of pseudo annotations.
  • 22. DRAWBACKSDRAWBACKS 1. More code to compile. Can be optimized. 2. Meta-programmation is hard. 3. Use of pseudo annotations. 4. Experimental.
  • 23. LET'S SEE IN PRACTICELET'S SEE IN PRACTICE
  • 26. HOW COULD WE PROVIDEHOW COULD WE PROVIDE URLSessionURLSession??
  • 27. Sharing an instance of URLSession @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { }
  • 28. Sharing an instance of URLSession @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { static var shared: AppDelegate? { return UIApplication.shared.delegate as? AppDelegate } }
  • 29. 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) }() }
  • 30. Using the shared instance of URLSession final class APIClient { func get(_ url: URL, completion: @escaping (Data?) -> Void) -> } }
  • 31. Using the shared instance of URLSession final class APIClient { func get(_ url: URL, completion: @escaping (Data?) -> Void) -> AppDelegate.shared.urlSession.dataTask(with: url) { ... }.resume() } }
  • 34. Injecting a shared instance of URLSession in APIClient final class APIClient { func get(_ url: URL, completion: @escaping (Data?) -> Void) -> } }
  • 35. Injecting a shared instance of URLSession in APIClient final class APIClient { private let urlSession: URLSession func get(_ url: URL, completion: @escaping (Data?) -> Void) -> } }
  • 36. 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) -> } }
  • 37. 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() } }
  • 38. Using APIClient in MovieManager final class MovieManager { func getMovies(_ completion: @escaping ([Movie]?) -> Void) { } }
  • 39. Using APIClient in MovieManager final class MovieManager { func getMovies(_ completion: @escaping ([Movie]?) -> Void) { APIClient().get("http://my_movie_api/movies") { ... } } }
  • 40. 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. } }
  • 43. 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() } }
  • 44. 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() } }
  • 45. From MovieManager to APIClient final class MovieManager { func getMovies(_ completion: @escaping ([Movie]?) -> Void) { } }
  • 46. From MovieManager to APIClient final class MovieManager { private let apiClient: APIClient func getMovies(_ completion: @escaping ([Movie]?) -> Void) { } }
  • 47. From MovieManager to APIClient final class MovieManager { private let apiClient: APIClient init(urlSession: URLSession) { apiClient = APIClient(urlSession) } func getMovies(_ completion: @escaping ([Movie]?) -> Void) { } }
  • 48. 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") { ... } } }
  • 49. From HomeViewController to MovieManager final class HomeViewController { override func viewDidLoad() { super.viewDidLoad() } }
  • 50. From HomeViewController to MovieManager final class HomeViewController { private let movieManager: MovieManager override func viewDidLoad() { super.viewDidLoad() } }
  • 51. From HomeViewController to MovieManager final class HomeViewController { private let movieManager: MovieManager init(_ urlSession: URLSession) { movieManager = MovieManager(urlSession: urlSession) } override func viewDidLoad() { super.viewDidLoad() } }
  • 52. 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 { ... } } }
  • 53. From AppDelegate to HomeViewController @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { func applicationDidFinishLaunching(_ application: UIApplicati } }
  • 54. From AppDelegate to HomeViewController @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { func applicationDidFinishLaunching(_ application: UIApplicati let configuration = ... let urlSession = URLSession(configuration: configuration) } }
  • 55. From AppDelegate to HomeViewController @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { func applicationDidFinishLaunching(_ application: UIApplicati let configuration = ... let urlSession = URLSession(configuration: configuration) let controller = HomeViewController(urlSession) ... } }
  • 58. WHY IS THIS BETTER?WHY IS THIS BETTER?
  • 59. WHY IS THIS BETTER?WHY IS THIS BETTER? MODULARITYMODULARITY
  • 60. What if ImageManager needs a bigger HTTP cache? final class ImageManager { }
  • 61. What if ImageManager needs a bigger HTTP cache? final class ImageManager { init() { } }
  • 62. What if ImageManager needs a bigger HTTP cache? final class ImageManager { init() { let configuration = ... } }
  • 63. 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 } }
  • 64. 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) } }
  • 65. 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) } }
  • 66. 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)") { .. } }
  • 69. Great! But this is too much code to write...
  • 70. 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( ) } }
  • 71. 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, ) } }
  • 72. 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, ) } }
  • 73. 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, ) } }
  • 74. 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, ) } }
  • 75. 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, ... ) } }
  • 76. 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, ... ) } }
  • 77. THAT'S WHERE WEAVER COMES HANDY.THAT'S WHERE WEAVER COMES HANDY.
  • 78. WHAT METHODOLOGY HAVE WE USED SO FAR?WHAT METHODOLOGY HAVE WE USED SO FAR?
  • 79. WHAT METHODOLOGY HAVE WE USED SO FAR?WHAT METHODOLOGY HAVE WE USED SO FAR? 1. Looked at the code.
  • 80. 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.
  • 81. 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.
  • 82. 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.
  • 83. WEAVER DOES THE SAMEWEAVER DOES THE SAME
  • 84. WEAVER DOES THE SAMEWEAVER DOES THE SAME BUT AUTOMATICALLYBUT AUTOMATICALLY
  • 85. WEAVER DOES THE SAMEWEAVER DOES THE SAME BUT AUTOMATICALLYBUT AUTOMATICALLY 1. Scans the code, looking for annotations.
  • 86. 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.
  • 87. 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.
  • 88. 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.
  • 89. BACK TO OUR EXAMPLEBACK TO OUR EXAMPLE
  • 91. Referencing to URLSession in APIClient final class APIClient { func get(_ url: URL, completion: @escaping (Data?) -> Void) -> } }
  • 92. 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) -> } }
  • 93. 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) -> } }
  • 94. 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 } }
  • 95. Referencing to URLSession in APIClient GENERATED CODEGENERATED CODE
  • 96. Referencing to URLSession in APIClient GENERATED CODEGENERATED CODE protocol APIClientInputDependencyResolver { var urlSession: URLSession { get } }
  • 97. Referencing to URLSession in APIClient GENERATED CODEGENERATED CODE protocol APIClientInputDependencyResolver { var urlSession: URLSession { get } } protocol APIClientDependencyResolver { var urlSession: URLSession { get } }
  • 98. 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 } }
  • 99. Registering APIClient in MovieManager final class MovieManager { func getMovies(_ completion: @escaping ([Movie]?) -> Void) { } }
  • 100. Registering APIClient in MovieManager final class MovieManager { private let dependencies: MovieManagerDependencyResolver init(injecting dependencies: MovieManagerDependencyResolver) self.dependencies = dependencies } func getMovies(_ completion: @escaping ([Movie]?) -> Void) { } }
  • 101. 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) { } }
  • 102. 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") } }
  • 103. Registering APIClient in MovieManager GENERATED CODEGENERATED CODE
  • 104. Registering APIClient in MovieManager GENERATED CODEGENERATED CODE protocol MovieManagerInputDependencyResolver { var urlSession: URLSession { get } }
  • 105. Registering APIClient in MovieManager GENERATED CODEGENERATED CODE protocol MovieManagerInputDependencyResolver { var urlSession: URLSession { get } } protocol MovieManagerDependencyResolver { var apiClient: APIClient { get } }
  • 106. 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 } }
  • 107. 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
  • 108. Registering MovieManager in HomeViewController final class HomeViewController { override func viewDidLoad() { super.viewDidLoad() } }
  • 109. Registering MovieManager in HomeViewController final class HomeViewController { private let dependencies: HomeViewControllerDependencyResolve init(injecting dependencies: HomeViewControllerDependencyReso self.dependencies = dependencies } override func viewDidLoad() { super.viewDidLoad() } }
  • 110. 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() } }
  • 111. 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 { ... } } }
  • 113. Registering MovieManager in HomeViewController GENERATED CODEGENERATED CODE protocol HomeViewControllerInputDependencyResolver { var urlSession: URLSession { get } }
  • 114. Registering MovieManager in HomeViewController GENERATED CODEGENERATED CODE protocol HomeViewControllerInputDependencyResolver { var urlSession: URLSession { get } } protocol HomeViewControllerDependencyResolver { var movieManager: MovieManager { get } }
  • 115. 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 } }
  • 116. 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
  • 120. Registering URLSession in AppDelegate @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { }
  • 121. Registering URLSession in AppDelegate @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { private let dependencies = AppDelegateDependencyContainer() }
  • 122. Registering URLSession in AppDelegate @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { private let dependencies = AppDelegateDependencyContainer() // weaver: urlSession = URLSession }
  • 123. Registering URLSession in AppDelegate @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { private let dependencies = AppDelegateDependencyContainer() // weaver: urlSession = URLSession // weaver: urlSession.scope = .container }
  • 124. 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 }
  • 125. 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) } }
  • 126. 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 }
  • 127. Registering URLSession in AppDelegate GENERATED CODEGENERATED CODE
  • 128. Registering URLSession in AppDelegate GENERATED CODEGENERATED CODE protocol AppDelegateDependencyResolver { var homeViewController: HomeViewController { get } }
  • 129. 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) } }
  • 130. 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
  • 135. Registering URLSession with a bigger HTTP cache in ImageManager final class ImageManager { }
  • 136. Registering URLSession with a bigger HTTP cache in ImageManager final class ImageManager { private let dependencies: ImageManagerDependencyResolver init(injecting dependencies: ImageManagerDependencyResolver) { self.d }
  • 137. 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 }
  • 138. 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) } }
  • 139. Registering URLSession in ImageManager GENERATED CODEGENERATED CODE
  • 140. Registering URLSession in ImageManager GENERATED CODEGENERATED CODE protocol ImageManagerDependencyResolver { var apiClient: APIClient { get } }
  • 141. 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) } }
  • 142. 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
  • 144. Works out of the box
  • 145. ROOM FOR IMPROVEMENTROOM FOR IMPROVEMENT
  • 146. ROOM FOR IMPROVEMENTROOM FOR IMPROVEMENT Adopt Weaver more widely at Scribd.
  • 147. ROOM FOR IMPROVEMENTROOM FOR IMPROVEMENT Adopt Weaver more widely at Scribd. Dependency graph visualizer.
  • 148. ROOM FOR IMPROVEMENTROOM FOR IMPROVEMENT Adopt Weaver more widely at Scribd. Dependency graph visualizer. Dependency graph optimizations.
  • 149. ROOM FOR IMPROVEMENTROOM FOR IMPROVEMENT Adopt Weaver more widely at Scribd. Dependency graph visualizer. Dependency graph optimizations. Detect retain cycles.
  • 150. ROOM FOR IMPROVEMENTROOM FOR IMPROVEMENT Adopt Weaver more widely at Scribd. Dependency graph visualizer. Dependency graph optimizations. Detect retain cycles. Detect suspicious graph shapes.
  • 151. ROOM FOR IMPROVEMENTROOM FOR IMPROVEMENT Adopt Weaver more widely at Scribd. Dependency graph visualizer. Dependency graph optimizations. Detect retain cycles. Detect suspicious graph shapes. ...