SkillAgentSearch skills...

Weaver

Dependency Injection framework for Swift (iOS/macOS/Linux)

Install / Use

/learn @scribd/Weaver
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

| :exclamation: Deprecation Notice | |:-| |We want to express our sincere gratitude for your support and contributions to the Weaver open source project. As we are no longer using this technology internally, we have come to the decision to archive the Weaver repository. While we won't be providing further updates or support, the existing code and resources will remain accessible for your reference. We encourage anyone interested to fork the repository and continue the project's legacy independently. Thank you for being a part of this journey and for your patience and understanding.|

logo

<p align="center">Declarative, easy-to-use and safe Dependency Injection framework for Swift (iOS/macOS/Linux)</p>

Build Status codecov Gitter

<a href="https://youtu.be/Rxhc9VJBoOI" alt="Watch the demo" target="_blank"> <img src="weaver.gif" alt="Watch the video" width="960" style="display: block; margin: 0 auto" /> </a>

Features

  • [x] Dependency declaration via property wrappers or comments
  • [x] DI Containers auto-generation
  • [x] Dependency Graph compile time validation
  • [x] ObjC support
  • [x] Non-optional dependency resolution
  • [x] Type safety
  • [x] Injection with arguments
  • [x] Registration Scopes
  • [x] DI Container hierarchy
  • [x] Thread safe

Talks

Tutorials

If you're looking for a step by step tutorial, check out these links.

Dependency Injection

Dependency Injection basically means "giving an object its instance variables" ¹. It seems like it's not such a big deal, but as soon as a project gets bigger, it gets tricky. Initializers become too complex, passing down dependencies through several layers becomes time consuming and just figuring out where to get a dependency from can be hard enough to give up and finally use a singleton.

However, Dependency Injection is a fundamental aspect of software architecture, and there is no good reason not to do it properly. That's where Weaver can help.

What is Weaver?

Weaver is a declarative, easy-to-use and safe Dependency Injection framework for Swift.

  • Declarative because it allows developers to declare dependencies via annotations directly in the Swift code.
  • Easy-to-use because it generates the necessary boilerplate code to inject dependencies into Swift types.
  • Safe because it's all happening at compile time. If it compiles, it works.

How does Weaver work?

                                                                         |-> validate() -> valid/invalid 
swift files -> scan() -> [Token] -> parse() -> AST -> link() -> Graph -> | 
                                                                         |-> generate() -> source code 

Weaver scans the Swift sources of the project, looking for annotations, and generates an AST (abstract syntax tree). It uses SourceKitten which is backed by Apple's SourceKit.

The AST then goes through a linking phase, which outputs a dependency graph.

Some safety checks are then performed on the dependency graph in order to ensure that the generated code won't crash at runtime. Issues are friendly reported in Xcode to make their correction easier.

Finally, Weaver generates the boilerplate code which can directly be used to make the dependency injections happen.

Installation

(1) - Weaver command

Weaver can be installed using Homebrew, CocodaPods or manually.

Binary form

Download the latest release with the prebuilt binary from release tab. Unzip the archive into the desired destination and run bin/weaver

Homebrew

$ brew install weaver

CocoaPods

Add the following to your Podfile:

pod 'WeaverDI'

This will download the Weaver binaries and dependencies in Pods/ during your next pod install execution and will allow you to invoke it via ${PODS_ROOT}/WeaverDI/weaver/bin/weaver in your Script Build Phases.

This is the best way to install a specific version of Weaver since Homebrew cannot automatically install a specific version.

Mint

To use Weaver via Mint, prefix the normal usage with mint run scribd/Weaver like so:

mint run scribd/Weaver version

To use a specific version of Weaver, add the release tag like so:

mint run scribd/Weaver@1.0.7 version

Building from source

Download the latest release source code from the release tab or clone the repository.

In the project directory, run brew update && brew bundle && make install to build and install the command line tool.

Check installation

Run the following to check if Weaver has been installed correctly.

$ weaver swift --help

Usage:

    $ weaver swift

Options:
    --project-path - Project's directory.
    --config-path - Configuration path.
    --main-output-path - Where the swift code gets generated.
    --tests-output-path - Where the test helpers gets generated.
    --input-path - Paths to input files.
    --ignored-path - Paths to ignore.
    --cache-path - Where the cache gets stored.
    --recursive-off
    --tests - Activates the test helpers' generation.
    --testable-imports - Modules to imports in the test helpers.
    --swiftlint-disable-all - Disables all swiftlint rules.

(2) - Weaver build phase

In Xcode, add the following command to a command line build phase:

weaver swift --project-path $PROJECT_DIR/$PROJECT_NAME --main-output-path output/relative/path

Important - Move this build phase above the Compile Source phase so that Weaver can generate the boilerplate code before compilation happens.

Basic Usage

For a more complete usage example, please check out the sample project.

Let's implement a simple app displaying a list of movies. It will be composed of three noticeable objects:

  • AppDelegate where the dependencies are registered.
  • MovieManager providing the movies.
  • MoviesViewController showing a list of movies at the screen.

Let's get into the code.

AppDelegate with comment annotations:

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    private let dependencies = MainDependencyContainer.appDelegateDependencyResolver()
    
    // weaver: movieManager = MovieManager <- MovieManaging
    // weaver: movieManager.scope = .container
    
    // weaver: moviesViewController = MoviesViewController <- UIViewController
    // weaver: moviesViewController.scope = .container
    
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        
        window = UIWindow()

        let rootViewController = dependencies.moviesViewController
        window?.rootViewController = UINavigationController(rootViewController: rootViewController)
        window?.makeKeyAndVisible()
        
        return true
    }
}

AppDelegate registers two dependencies:

  • // weaver: movieManager = MovieManager <- MovieManaging
  • // weaver: moviesViewController = MoviesViewController <- UIViewController

These dependencies are made accessible to any object built from AppDelegate because their scope is set to container:

  • // weaver: movieManager.scope = .container
  • // weaver: moviesViewController.scope = .container

A dependency registration automatically generates the registration code and one accessor in AppDelegateDependencyContainer, which is why the rootViewController can be built:

  • let rootViewController = dependencies.moviesViewController.

AppDelegate with property wrapper annotations:

Since Weaver 1.0.1, you can use property wrappers instead of annotations in comments.

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?
    
    // Must be declared first!
    private let dependencies = MainDependencyContainer.appDelegateDependencyResolver()

    @Weaver(.registration, type: MovieManager.self, scope: .container)
    private var movieManager: MovieManaging
	
    @Weaver(.registration, type: MoviesViewController.self, scope: .container)
    private var moviesViewController: UIViewController
    
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        
        window = UIWindow()

        window?.rootViewController = UINavigationController(rootViewController: moviesViewController)
        window?.makeKeyAndVisible()
        
        return true
    }
}
  • Note how dependencies can be accessed from the self instance directly.

  • Also note that the dependencies object must be declared and created prior to any other Weaver annotation. Not doing so would immediately crash the application.

  • It is possible to use comment an

View on GitHub
GitHub Stars770
CategoryDevelopment
Updated8d ago
Forks36

Languages

Swift

Security Score

100/100

Audited on Mar 22, 2026

No findings