Pure makes Pure DI easy in Swift.

Overview

Pure

Swift CocoaPods Build Status Codecov

Pure makes Pure DI easy in Swift. This repository also introduces a way to do Pure DI in a Swift application.

Table of Contents

Background

Pure DI

Pure DI is a way to do a dependency injection without a DI container. The term was first introduced by Mark Seemann. The core concept of Pure DI is not to use a DI container and to compose an entire object dependency graph in the Composition Root.

Composition Root

The Composion Root is where the entire object graph is resolved. In a Cocoa application, AppDelegate is the Composition Root.

AppDependency

The root dependencies are the app delegate's dependency and the root view controller's dependency. The best way to inject those dependencies is to create a struct named AppDependency and store both dependencies in it.

struct AppDependency {
  let networking: Networking
  let remoteNotificationService: RemoteNotificationService
}

extension AppDependency {
  static func resolve() -> AppDependency {
    let networking = Networking()
    let remoteNotificationService = RemoteNotificationService()

    return AppDependency(
      networking: networking
      remoteNotificationService: remoteNotificationService
    )
  }
}

It is important to separate a production environment from a testing environment. We have to use an actual object in a production environment and a mock object in a testing environment.

AppDelegate is created automatically by the system using init(). In this initializer we're going to initialize the actaul app dependency with AppDependency.resolve(). On the other hand, we're going to provide a init(dependency:) to inject a mock app dependency in a testing environment.

class AppDelegate: UIResponder, UIApplicationDelegate {
  private let dependency: AppDependency

  /// Called from the system (it's private: not accessible in the testing environment)
  private override init() {
    self.dependency = AppDependency.resolve()
    super.init()
  }

  /// Called in a testing environment
  init(dependency: AppDependency) {
    self.dependency = dependency
    super.init()
  }
}

The app dependency can be used as the code below:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
  // inject rootViewController's dependency
  if let viewController = self.window?.rootViewController as? RootViewController {
    viewController.networking = self.dependency.networking
  }
}

func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
  // delegates remote notification receive event
  self.dependency.remoteNotificationService.receiveRemoteNotification(userInfo)
}

Testing AppDelegate

AppDelegate is one of the most important class in a Cocoa application. It resolves an app dependency and handles app events. It can be easily tested as we separated the app dependency.

This is an example test case of AppDelegate. It verifies that AppDelegate correctly injects root view controller's dependency in application(_:didFinishLaunchingWithOptions).

class AppDelegateTests: XCTestCase {
  func testInjectRootViewControllerDependencies() {
    // given
    let networking = MockNetworking()
    let mockDependency = AppDependency(
      networking: networking,
      remoteNotificationService: MockRemoteNotificationService()
    )
    let appDelegate = AppDelegate(dependency: mockDependency)
    appDelegate.window = UIWindow()
    appDelegate.window?.rootViewController = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController()

    // when
    _ = appDelegate.application(.shared, didFinishLaunchingWithOptions: nil)

    // then
    let rootViewController = appDelegate.window?.rootViewController as? RootViewController
    XCTAssertTrue(rootViewController?.networking === networking)
  }
}

You can write tests for verifying remote notification events, open url events and even an app termination event.

Separating AppDelegate

But there is a problem: AppDelegate is still created by the system while testing. It causes AppDependency.resolve() gets called so we have to use a fake app delegate class in a testing environment.

First of all, create a new file in the test target. Define a new class named TestAppDelegate and implement basic requirements of the delegate protocol.

// iOS
class TestAppDelegate: NSObject, UIApplicationDelegate {
  var window: UIWindow?
}

// macOS
class TestAppDelegate: NSObject, NSApplicationDelegate {
}

Then create another file named main.swift to your application target. This file will replace the entry point of the application. We are going to provide different app delegates in this file. Don't forget to replace "MyAppTests.TestAppDelegate" with your project target and class name.

NSApplicationDelegate { if let cls = NSClassFromString("AllkdicTests.TestAppDelegate") as? (NSObject & NSApplicationDelegate).Type { return cls.init() } else { return AppDelegate(dependency: AppDependency.resolve()) } } let application = NSApplication.shared application.delegate = createAppDelegate() _ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv) ">
// iOS
UIApplicationMain(
  CommandLine.argc,
  CommandLine.unsafeArgv,
  NSStringFromClass(UIApplication.self),
  NSStringFromClass(NSClassFromString("MyAppTests.TestAppDelegate") ?? AppDelegate.self)
)

// macOS
func createAppDelegate() -> NSApplicationDelegate {
  if let cls = NSClassFromString("AllkdicTests.TestAppDelegate") as? (NSObject & NSApplicationDelegate).Type {
    return cls.init()
  } else {
    return AppDelegate(dependency: AppDependency.resolve())
  }
}

let application = NSApplication.shared
application.delegate = createAppDelegate()
_ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)

Finally, remove the @UIApplicationMain and @NSApplicationMain from the AppDelegate.

  // iOS
- @UIApplicationMain
  class AppDelegate: UIResponder, UIApplicationDelegate

  // macOS
- @NSApplicationMain
  class AppDelegate: NSObject, NSApplicationDelegate

It is also a good practice to add a test case to verify that the application is using TestAppDelegate in a testing environment.

XCTAssertTrue(UIApplication.shared.delegate is TestAppDelegate)

Lazy Dependency

Using Factory

In Cocoa applications, view controllers are created lazily. For example, DetailViewController is not created until the user taps an item on ListViewController. In this case we have to pass a factory closure of DetailViewController to ListViewController.

DetailViewController = { selectedItem in let detailViewController = storyboard.instantiateViewController(withIdentifier: "DetailViewController") as! DetailViewController detailViewController.networking = networking detailViewController.item = selectedItem return detailViewController } return AppDependency( networking: networking, detailViewControllerFactory: detailViewControllerFactory ) } ">
/// A root view controller
class ListViewController {
  var detailViewControllerFactory: ((Item) -> DetailViewController)!

  func presentItemDetail(_ selectedItem: Item) {
    let detailViewController = self.detailViewControllerFactory(selectedItem)
    self.present(detailViewController, animated: true)
  }
}

static func resolve() -> AppDependency {
  let storyboard = UIStoryboard(name: "Main", bundle: nil)
  let networking = Networking()

  let detailViewControllerFactory: (Item) -> DetailViewController = { selectedItem in
    let detailViewController = storyboard.instantiateViewController(withIdentifier: "DetailViewController") as! DetailViewController
    detailViewController.networking = networking
    detailViewController.item = selectedItem
    return detailViewController
  }

  return AppDependency(
    networking: networking,
    detailViewControllerFactory: detailViewControllerFactory
  )
}

But it has a critical problem: we cannot test the factory closure. Because the factory closure is created in the Composition Root but we should not access the Composition Root in a testing environment. What if I forget to inject the DetailViewController.networking property?

One possible approach is to create a factory closure outside of the Composition Root. Note that Storyboard and Networking is from the Composition Root, and Item is from the previous view controller so we have to separate the scope.

AppDependency { let storyboard = ... let networking = ... return .init( detailViewControllerFactory: DetailViewController.factory(storyboard, networking) ) } ">
extension DetailViewController {
  static let factory: (UIStoryboard, Networking) -> (Item) -> DetailViewController = { storyboard, networking in
    return { selectedItem in
      let detailViewController = storyboard.instantiateViewController(withIdentifier: "DetailViewController") as! DetailViewController
      detailViewController.networking = networking
      detailViewController.item = selectedItem
      return detailViewController
    }
  }
}

static func resolve() -> AppDependency {
  let storyboard = ...
  let networking = ...
  return .init(
    detailViewControllerFactory: DetailViewController.factory(storyboard, networking)
  )
}

Now we can test the DetailViewController.factory closure. Every dependencies are resolved in the Composition Root and a selected item can be passed from ListViewController to DetailViewController in runtime.

Using Configurator

There is another lazy dependency. Cells are created lazily but we cannot use the factory closure because the cells are created by the framework. We can just configure the cells.

Imagine that an UICollectionViewCell or UITableViewCell displays an image. There is an imageDownloader which downloads an actual image in a production environment and returns a mock image in a testing environment.

class ItemCell {
  var imageDownloader: ImageDownloaderType?
  var imageURL: URL? {
    didSet {
      guard let imageDownloader = self.imageDownloader else { return }
      self.imageView.setImage(with: self.imageURL, using: imageDownloader)
    }
  }
}

This cell is displayed in DetailViewController . DetailViewController should inject imageDownloader to the cell and sets the image property. Like we did in the factory, we can create a configurator closure for it. But this closure takes an existing instance and doens't have a return value.

class ItemCell {
  static let configurator: (ImageDownloaderType) -> (ItemCell, UIImage) -> Void = { imageDownloader
    return { cell, image in
      cell.imageDownloader = imageDownloader
      cell.image = image
    }
  }
}

DetailViewController can have the configurator and use it when configurating cell.

class DetailViewController {
  var itemCellConfigurator: ((ItemCell, UIImage) -> Void)?

  func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    ...
    self.itemCellConfigurator?(cell, image)
    return cell
  }
}

DetailViewController.itemCellConfigurator is injected from a factory.

extension DetailViewController {
  static let factory: (UIStoryboard, Networking, (ItemCell, UIImage) -> Void) -> (Item) -> DetailViewController = { storyboard, networking, imageCellConfigurator in
    return { selectedItem in
      let detailViewController = storyboard.instantiateViewController(withIdentifier: "DetailViewController") as! DetailViewController
      detailViewController.networking = networking
      detailViewController.item = selectedItem
      detailViewController.imageCellConfigurator = imageCellConfigurator
      return detailViewController
    }
  }
}

And the Composition Root finally looks like:

static func resolve() -> AppDependency {
  let storybard = ...
  let networking = ...
  let imageDownloader = ...
  let listViewController = ...
  listViewController.detailViewControllerFactory = DetailViewController.factory(
    storyboard,
    networking,
    ImageCell.configurator(imageDownloader)
  )
  ...
}

Problem

Theoretically it works. But as you can see in the DetailViewController.factory it will be very complicated when there are many dependencies. This is why I created Pure. Pure makes factories and configurators neat.

Getting Started

Dependency and Payload

First of all, take a look at the factory and the configurator we used in the example code.

static let factory: (UIStoryboard, Networking, (ItemCell, UIImage) -> Void) -> (Item) -> DetailViewController
static let configurator: (ImageDownloaderType) -> (ItemCell, UIImage) -> Void

Those are the functions that return another function. The outer functions are executed in the Composition Root to inject static dependencies like Networking and the inner functions are executed in the view controllers to pass a runtime information like selectedItem. The parameter of the outer function is Dependency. The parameter of the inner function is Payload.

static let factory: (UIStoryboard, Networking, (ItemCell, UIImage) -> Void) -> (Item) -> DetailViewController
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^      ^^^^
                                        Dependency                             Payload

static let configurator: (ImageDownloaderType) -> (ItemCell, UIImage) -> Void
                          ^^^^^^^^^^^^^^^^^^^      ^^^^^^^^^^^^^^^^^
                              Dependency                Payload

Pure generalizes the factory and configurator using Dependency and Payload.

Module

Pure treats every class that requires a dependency and a payload as a Module. A protocol Module requires two types: Dependency and Payload.

protocol Module {
  /// A dependency that is resolved in the Composition Root.
  associatedtype Dependency

  /// A runtime information for configuring the module.
  associatedtype Payload
}

There are two types of module: FactoryModule and ConfiguratorModule.

Factory Module

FactoryModule is a generalized version of factory closure. It requires an initializer which takes both dependency and payload.

protocol FactoryModule: Module {
  init(dependency: Dependency, payload: Payload)
}

class DetailViewController: FactoryModule {
  struct Dependency {
    let storyboard: UIStoryboard
    let networking: Networking
  }

  struct Payload {
    let selectedItem: Item
  }

  init(dependency: Dependency, payload: Payload) {
  }
}

When a class conforms to FactoryModule, it will have a nested type Factory. Factory.init(dependency:) takes a dependency of the module and has a method create(payload:) which creates a new instance.

class Factory<Module> {
  let dependency: Module.Dependency
  func create(payload: Module.Payload) -> Module
}

// In AppDependency
let factory = DetailViewController.Factory(dependency: .init(
  storyboard: storyboard
  networking: networking
))

// In ListViewController
let viewController = factory.create(payload: .init(
  selectedItem: selectedItem
))

Configurator Module

ConfiguratorModule is a generalized version of configurator closure. It requires a configure() method which takes both dependency and payload.

protocol ConfiguratorModule: Module {
  func configure(dependency: Dependency, payload: Payload)
}

class ItemCell: ConfiguratorModule {
  struct Dependency {
    let imageDownloader: ImageDownloaderType
  }

  struct Payload {
    let image: UIImage
  }

  func configure(dependency: Dependency, payload: Payload) {
    self.imageDownloader = dependency.imageDownloader
    self.image = payload.image
  }
}

When a class conforms to ConfiguratorModule, it will have a nested type Configurator. Configurator.init(dependency:) takes a dependency of the module and has a method configure(_:payload:) which configures an existing instance.

class Configurator<Module> {
  let dependency: Module.Dependency
  func configure(_ module: Module, payload: Module.Payload)
}

// In AppDependency
let configurator = ItemCell.Configurator(dependency: .init(
  imageDownloader: imageDownloader
))

// In DetailViewController
configurator.configure(cell, payload: .init(image: image))

With FactoryModule and ConfiguratorModule, the example can be refactored as below:

static func resolve() -> AppDependency {
  let storybard = ...
  let networking = ...
  let imageDownloader = ...
  return .init(
    detailViewControllerFactory: DetailViewController.Factory(dependency: .init(
      storyboard: storyboard,
      networking: networking,
      itemCellConfigurator: ItemCell.Configurator(dependency: .init(
        imageDownloader: imageDownloader
      ))
    ))
  )
}

Customizing

Factory and Configurator are customizable. This is an example of customized factory:

extension Factory where Module == DetailViewController {
  func create(payload: Module.Payload, extraValue: ExtraValue) -> Payload {
    let module = self.create(payload: payload)
    module.extraValue = extraValue
    return module
  }
}

Storyboard Support

FactoryModule can support Storyboard-instantiated view controllers using customizing feature. The code below is an example for storyboard support of DetailViewController:

extension Factory where Module == DetailViewController {
  func create(payload: Module.Payload) -> Payload {
    let module = self.dependency.storyboard.instantiateViewController(withIdentifier: "DetailViewController") as! Module
    module.networking = dependency.networking
    module.itemCellConfigurator = dependency.itemCellConfigurator
    module.selectedItem = payload.selectedItem
    return module
  }
}

URLNavigator Support

URLNavigator is an elegant library for deeplink support. Pure can be also used in registering a view controller to a navigator.

", UserViewController.Factory().create) ">
class UserViewController {
  struct Payload {
    let userID: Int
  }
}

extension Factory where Module == UserViewController {
  func create(url: URLConvertible, values: [String: Any], context: Any?) -> Module? {
    guard let userID = values["id"] else { return nil }
    return self.create(payload: .init(userID: userID))
  }
}

let navigator = Navigator()
navigator.register("myapp://user/", UserViewController.Factory().create)

Installation

Contribution

Any discussions and pull requests are welcomed 💖

  • To development:

    $ make project
  • To test:

    $ swift test

License

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

Comments
  • Update package dependencies (Quick & Nimble)

    Update package dependencies (Quick & Nimble)

    Quick (2.0.0) and Nimble (8.0.0) support swift 5!

    SPM can not resolve dependencies. Refer this message

    Showing All Messages : the package pure[https://github.com/devxoul/pure] @ 1.1.1 contains incompatible dependencies: quick[https://github.com/devxoul/Quick.git] @ swift-5 nimble[https://github.com/devxoul/Nimble.git] @ swift-5

    opened by tokijh 2
  • Fix to create `Dependency` only once.

    Fix to create `Dependency` only once.

    Dependency should be initialized only once because it is static, but it is being initialized every time it uses dependency.

    I fixed to initialize dependency only once using lazy var.

    Thanks to @OhKanghoon

    opened by tokijh 1
  • Bump tzinfo from 1.2.4 to 1.2.10

    Bump tzinfo from 1.2.4 to 1.2.10

    Bumps tzinfo from 1.2.4 to 1.2.10.

    Release notes

    Sourced from tzinfo's releases.

    v1.2.10

    TZInfo v1.2.10 on RubyGems.org

    v1.2.9

    • Fixed an incorrect InvalidTimezoneIdentifier exception raised when loading a zoneinfo file that includes rules specifying an additional transition to the final defined offset (for example, Africa/Casablanca in version 2018e of the Time Zone Database). #123.

    TZInfo v1.2.9 on RubyGems.org

    v1.2.8

    • Added support for handling "slim" format zoneinfo files that are produced by default by zic version 2020b and later. The POSIX-style TZ string is now used calculate DST transition times after the final defined transition in the file. The 64-bit section is now always used regardless of whether Time has support for 64-bit times. #120.
    • Rubinius is no longer supported.

    TZInfo v1.2.8 on RubyGems.org

    v1.2.7

    • Fixed 'wrong number of arguments' errors when running on JRuby 9.0. #114.
    • Fixed warnings when running on Ruby 2.8. #112.

    TZInfo v1.2.7 on RubyGems.org

    v1.2.6

    • Timezone#strftime('%s', time) will now return the correct number of seconds since the epoch. #91.
    • Removed the unused TZInfo::RubyDataSource::REQUIRE_PATH constant.
    • Fixed "SecurityError: Insecure operation - require" exceptions when loading data with recent Ruby releases in safe mode.
    • Fixed warnings when running on Ruby 2.7. #106 and #111.

    TZInfo v1.2.6 on RubyGems.org

    v1.2.5

    • Support recursively (deep) freezing Country and Timezone instances. #80.
    • Allow negative daylight savings time offsets to be derived when reading from zoneinfo files. The utc_offset and std_offset are now derived correctly for Europe/Dublin in the 2018a and 2018b releases of the Time Zone Database.

    TZInfo v1.2.5 on RubyGems.org

    Changelog

    Sourced from tzinfo's changelog.

    Version 1.2.10 - 19-Jul-2022

    Version 1.2.9 - 16-Dec-2020

    • Fixed an incorrect InvalidTimezoneIdentifier exception raised when loading a zoneinfo file that includes rules specifying an additional transition to the final defined offset (for example, Africa/Casablanca in version 2018e of the Time Zone Database). #123.

    Version 1.2.8 - 8-Nov-2020

    • Added support for handling "slim" format zoneinfo files that are produced by default by zic version 2020b and later. The POSIX-style TZ string is now used calculate DST transition times after the final defined transition in the file. The 64-bit section is now always used regardless of whether Time has support for 64-bit times. #120.
    • Rubinius is no longer supported.

    Version 1.2.7 - 2-Apr-2020

    • Fixed 'wrong number of arguments' errors when running on JRuby 9.0. #114.
    • Fixed warnings when running on Ruby 2.8. #112.

    Version 1.2.6 - 24-Dec-2019

    • Timezone#strftime('%s', time) will now return the correct number of seconds since the epoch. #91.
    • Removed the unused TZInfo::RubyDataSource::REQUIRE_PATH constant.
    • Fixed "SecurityError: Insecure operation - require" exceptions when loading data with recent Ruby releases in safe mode.
    • Fixed warnings when running on Ruby 2.7. #106 and #111.

    Version 1.2.5 - 4-Feb-2018

    • Support recursively (deep) freezing Country and Timezone instances. #80.
    • Allow negative daylight savings time offsets to be derived when reading from zoneinfo files. The utc_offset and std_offset are now derived correctly for Europe/Dublin in the 2018a and 2018b releases of the Time Zone Database.
    Commits
    • 0814dcd Fix the release date.
    • fd05e2a Preparing v1.2.10.
    • b98c32e Merge branch 'fix-directory-traversal-1.2' into 1.2
    • ac3ee68 Remove unnecessary escaping of + within regex character classes.
    • 9d49bf9 Fix relative path loading tests.
    • 394c381 Remove private_constant for consistency and compatibility.
    • 5e9f990 Exclude Arch Linux's SECURITY file from the time zone index.
    • 17fc9e1 Workaround for 'Permission denied - NUL' errors with JRuby on Windows.
    • 6bd7a51 Update copyright years.
    • 9905ca9 Fix directory traversal in Timezone.get when using Ruby data source
    • Additional commits viewable in compare view

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    opened by dependabot[bot] 0
  • Bump cocoapods-downloader from 1.1.3 to 1.6.3

    Bump cocoapods-downloader from 1.1.3 to 1.6.3

    Bumps cocoapods-downloader from 1.1.3 to 1.6.3.

    Release notes

    Sourced from cocoapods-downloader's releases.

    1.6.3

    Enhancements
    • None.
    Bug Fixes
    • None.

    1.6.2

    Enhancements
    • None.
    Bug Fixes
    • None.

    1.6.1

    Enhancements
    • None.
    Bug Fixes
    • None.

    1.6.0

    Enhancements
    • None.
    Bug Fixes
    • Adds a check for command injections in the input for hg and git.
      orta #124

    1.5.1

    Enhancements
    • None.
    Bug Fixes
    • Fix "can't modify frozen string" errors when pods are integrated using the branch option
      buju77 #10920

    1.5.0

    ... (truncated)

    Changelog

    Sourced from cocoapods-downloader's changelog.

    1.6.3 (2022-04-01)

    Enhancements
    • None.
    Bug Fixes
    • None.

    1.6.2 (2022-03-28)

    Enhancements
    • None.
    Bug Fixes
    • None.

    1.6.1 (2022-03-23)

    Enhancements
    • None.
    Bug Fixes
    • None.

    1.6.0 (2022-03-22)

    Enhancements
    • None.
    Bug Fixes
    • Adds a check for command injections in the input for hg and git.
      orta #124

    1.5.1 (2021-09-07)

    Enhancements
    • None.

    ... (truncated)

    Commits
    • c03e2ed Release 1.6.3
    • f75bccc Disable Bazaar tests due to macOS 12.3 not including python2
    • 52a0d54 Merge pull request #128 from CocoaPods/validate_before_dl
    • d27c983 Ensure that the git pre-processor doesn't accidentally bail also
    • 3adfe1f [CHANGELOG] Add empty Master section
    • 591167a Release 1.6.2
    • d2564c3 Merge pull request #127 from CocoaPods/validate_before_dl
    • 99fec61 Switches where we check for invalid input, to move it inside the download fun...
    • 96679f2 [CHANGELOG] Add empty Master section
    • 3a7c54b Release 1.6.1
    • Additional commits viewable in compare view

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    opened by dependabot[bot] 0
  • Sample example app

    Sample example app

    Hey Well done documentation!

    Could you provide example app that follows the readme description with list view and details view controller (xib, storyboard), testable app delegate concept with minimal unit test? It will be much easier to grab the framework usage in real life.

    opened by nonameplum 1
Releases(1.1.4)
Owner
Suyeol Jeon
A lazy developer 😴 I write many code to write less code.
Suyeol Jeon
Kraken - Simple Dependency Injection container for Swift. Use protocols to resolve dependencies with easy-to-use syntax!

Kraken Photo courtesy of www.krakenstudios.blogspot.com Introduction Kraken is a simple Dependency Injection Container. It's aimed to be as simple as

Syed Sabir Salman-Al-Musawi 1 Oct 9, 2020
Deli is an easy-to-use Dependency Injection Container that creates DI containers

Deli is an easy-to-use Dependency Injection Container that creates DI containers with all required registrations and corresponding factories.

Jungwon An 134 Aug 10, 2022
Perform - Easy dependency injection for storyboard segues

Perform Easy dependency injection for storyboard segues. import Perform // ... func tableView(_ tableView: UITableView, didSelectRowAt indexPath: NS

thoughtbot, inc. 280 Feb 6, 2022
DIContainer Swift is an ultra-light dependency injection container made to help developers to handle dependencies easily. It works with Swift 5.1 or above.

?? DIContainer Swift It is an ultra-light dependency injection container made to help developers to handle dependencies easily. We know that handle wi

Victor Carvalho Tavernari 10 Nov 23, 2022
Injector - A Swift package for simple dependency injection that also supports Swift UI previews

A Swift package for simple dependency injection that also supports Swift UI prev

null 6 Aug 9, 2022
Cleanse is a dependency injection framework for Swift.

Cleanse - Swift Dependency Injection Cleanse is a dependency injection framework for Swift. It is designed from the ground-up with developer experienc

Square 1.7k Dec 16, 2022
DIKit Dependency Injection Framework for Swift, inspired by KOIN.

DIKit Dependency Injection Framework for Swift, inspired by KOIN. Basically an implementation of service-locator pattern, living within the applicatio

null 95 Dec 22, 2022
Tranquillity is a lightweight but powerful dependency injection library for swift.

DITranquillity Tranquillity is a lightweight but powerful dependency injection library for swift. The name "Tranquillity" laid the foundation in the b

Ivlev Alexander 393 Dec 24, 2022
A micro-framework that leverages Swift Property Wrappers to implement the Service Locator pattern

Locatable Context Locatable is a Swift micro framework that leverages Property Wrappers to implement the Service Locator pattern, through a custom att

Vincent Pradeilles 116 Jan 9, 2022
Swinject is a lightweight dependency injection framework for Swift.

Swinject Swinject is a lightweight dependency injection framework for Swift. Dependency injection (DI) is a software design pattern that implements In

null 5.6k Dec 31, 2022
Dependency Injection framework for Swift (iOS/macOS/Linux)

Declarative, easy-to-use and safe Dependency Injection framework for Swift (iOS/macOS/Linux) Features Dependency declaration via property wrappers or

Scribd 684 Dec 12, 2022
Swift Ultralight Dependency Injection / Service Locator framework

Swift Ultralight Dependency Injection / Service Locator framework

Michael Long 1.9k Jan 6, 2023
The Package Manager for the Swift Programming Language

Swift Package Manager Project The Swift Package Manager is a tool for managing distribution of source code, aimed at making it easy to share your code

Apple 9.1k Jan 5, 2023
Guise - An elegant, flexible, type-safe dependency resolution framework for Swift

Guise is an elegant, flexible, type-safe dependency resolution framework for Swift. Flexible dependency resolution, with optional caching Elegant, str

null 52 Oct 3, 2022
ViperServices - Simple dependency injection container for services written for iOS in swift supporting boot order

ViperServices Introduction ViperServices is dependency injection container for iOS applications written in Swift. It is more lightweight and simple in

Siarhei Ladzeika 5 Dec 8, 2022
Needle - Compile-time safe Swift dependency injection framework

Needle is a dependency injection (DI) system for Swift. Unlike other DI frameworks, such as Cleanse, Swinject, Needle encourages hierarchical DI struc

Uber Open Source 1.4k Jan 3, 2023
Inject Dylib - Swift code to programmatically perform dylib injection

Inject_Dylib Swift code to programmatically perform dylib injection. You can als

Cedric Owens 40 Sep 27, 2022
SwiftDependencyChecker - Swift Dependency Checker

SwiftDependencyChecker SwiftDependencyChecker can be installed with homebrew wit

Kristiina 34 Dec 6, 2022
CarbonGraph - A Swift dependency injection / lookup framework for iOS

CarbonGraph is a Swift dependency injection / lookup framework for iOS. You can

Baidu 244 Jan 4, 2023