Fork me on GitHub

Alex Rupérez

Link: http://j.mp/2CNWMZU
Published: 29 Jan 13:07
I starred devmynd/drip at GitHub.
Dagger-ish dependency injector for Swift
View on GitHub


View README


Drip

A lightweight, dagger-ish dependency injection framework for Swift. May sharedInstance blot your vision nevermore.

Dependencies are resolved through type inference whenever possible. Two types, Components and Modules, compose the dependency container. At a high level, a Module provides specific dependencies, and a Component both contains a set of Modules and constrains their scope.

Modules

A module provides, and is a logical grouping of, related dependencies. For example, in your application you might define a ServiceModule that provides a set of Api objects.

A module is a class conforming to ModuleType, but typically subclassing Module. A module is coupled to a specific component, and it must specify that component in its type declaration.

import Drip

class ViewModule: Module<ViewComponent> {
  required init(_ component: ViewComponent) {
    super.init(component)
  }
}

A module declares methods describing the strategy to resolve individual dependencies. At present, modules have two built-in strategies (exposed as instance methods on the module) for resolving dependencies:

  • single: Only one instance is created per-component.
  • transient: A new instance is created per-resolution.

Each strategy method receives the component as its only parameter. In the event that a dependency depends on other types provided by the module's component, you to inject them using this component reference.

func inject() -> Api {
  return transient { Api() }
}

func inject() -> ViewModel {
  return single { c in
    ViewModel(api: c.core.inject())
  }
}

Components

A Component constrains the scope for a set of modules, and is the container for the modules' dependencies. Components have no built-in lifecycle, but are instead owned by (and match the lifecycle of) a member of your application. A component serves dependencies appropriate for the scope of its owning object.

For example, an Application object might own an ApplicationComponent that serves application-wide dependencies like a Repository and a Configuration.

A component is a class conforming to ComponentType, but typically subclassing Component.

import Drip

final class ViewComponent: Component {
  var root: ApplicationComponent { return parent() }
  var core: ViewModule { return module() }
}

A component declares accessors for referencing its modules (the module method will automatically resolve the correct instance).

var core: ViewModule { return module() }

A component may also declare accessors for referencing parent components (the parent method will automatically resolve the correct instance). This is useful when declaring a component with a narrower scope that requires dependencies provided by a wider-scoped component.

var root: ApplicationComponent { return parent() }

Components are constrcuted through chainable methods that register its modules (and optionally parents). The type of parents is inferred, but the type of modules must be specified explicitly.

lazy var component: ViewComponent = ViewComponent()
  .parent { self.app.component }
  .module(ViewModule.self) { ViewModule($0) }