devxoul / SwinjectFactory

Experimental Swinject extension for factory injection.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

SwinjectFactory

Swift CocoaPods Build Status Codecov

Swinject extension for factory injection.

Background

Imagine that your component has a runtime parameter (userID) which cannot be determined statically.

class UserDetailViewController {
  init(userID: Int, userService: UserServiceType, otherService: OtherServiceType)
}

Swinject also supports arguments but Swift compiler cannot check the arguments statically. It can cause a human mistake.

// it takes 1 parameter
container.register(UserDetailViewController.self) { r, userID in
  return UserDetailViewController(userID: userID, userService: r.resolve(UserServiceType.self)!)
}

// when pass 3 parameters it will return nil
container.resolve(UserServiceType.self, arguments: 1, 2, 3)

So in this case we shoulud use a factory instead:

// register UserDetailViewController factory
container.register(((userID: Int) -> UserDetailViewController).self) { r in
  return { userID in
    return UserDetailViewController(userID: userID, userService: r.resolve(UserServiceType.self)!)
  }
}

// register a component that has a dependency to UserDetailViewController
container.register(UserListViewController.self) { r in
  return UserListViewController(detailFactory: r.resolve(((userID: Int) -> UserDetailViewController).self)!)
}

But it looks really complicated. SwinjectFactory makes it really clean.

At a Glance

The code above becomes short and readable with SwinjectFactory.

// register UserDetailViewController factory
container.register(factory: UserDetailViewController.self)

// register a component that has a dependency to UserDetailViewController
container.register(UserListViewController.self) { r in
  UserListViewController(detailFactory: r.resolve(factory: UserDetailViewController.self)!)
}

It can be even shorter with SwinjectAutoregistration.

container.register(factory: UserDetailViewController.self)
container.autoregister(UserListViewControllerType.self, initializer: UserListViewController.init)

Usage

Conform your component to FactoryComponent. This protocol requires a single static variable: factory. This is a curried function which takes a Swinject.Resolver as a first function parameter. The nested function takes a runtime parameter which is required for constructing a component class. It finally returns a service type.

class UserDetailViewController: FactoryComponent {
  init(userID: Int, userService: UserServiceType) {
  }

  // (Resolver) -> (Runtime Paramters) -> Service
  static var factory: (Resolver) -> (_ userID: Int) -> UserDetailViewController {
    return self.autocreate(UserDetailViewController.init) // it will automatically fill parameters
  }
}

Here is an example of using UserDetailViewController as a dependency:

class UserListViewController {
  let detailViewControllerFactory: UserDetailViewController.Factory

  init(detailViewControllerFactory: UserDetailViewController.Factory) {
    self.detailViewControllerFactory = detailViewControllerFactory
  }
  
  func presentDetailViewController(userID: Int) {
    let viewController = self.detailViewControllerFactory(userID)
    self.navigationController?.push(viewController, animated: true)
  }
}

Installation

SwinjectFactory currently support CocoaPods only.

pod 'SwinjectFactory'

Contributing

Any discussions and pull requests are welcomed đź’–

To create a Xcode project:

$ make project

This will automatically create a Xcode project file and configure code generating environment.

License

SwinjectFactory is under MIT license. See the LICENSE file for more info.

About

Experimental Swinject extension for factory injection.

License:MIT License


Languages

Language:Swift 95.1%Language:Ruby 2.2%Language:HTML 2.2%Language:Shell 0.3%Language:Makefile 0.2%