Giter VIP home page Giter VIP logo

needle's Introduction

Build Status Carthage compatible License

Needle is a dependency injection (DI) system for Swift. Unlike other DI frameworks, such as Cleanse, Swinject, Needle encourages hierarchical DI structure and utilizes code generation to ensure compile-time safety. This allows us to develop our apps and make code changes with confidence. If it compiles, it works. In this aspect, Needle is more similar to Dagger for the JVM.

Needle aims to achieve the following primary goals:

  1. Provide high reliability by ensuring dependency injection code is compile-time safe.
  2. Ensure code generation is highly performant even when used with multi-million-line codebases.
  3. Be compatible with all iOS application architectures, including RIBs, MVx etc.

The gist

Using Needle to write DI code for your application is easy and compile-time safe. Each dependency scope is defined by a Component. And its dependencies are encapsulated in a Swift protocol. The two are linked together using Swift generics.

/// This protocol encapsulates the dependencies acquired from ancestor scopes.
protocol MyDependency: Dependency {
    /// These are objects obtained from ancestor scopes, not newly introduced at this scope.
    var chocolate: Food { get }
    var milk: Food { get }
}

/// This class defines a new dependency scope that can acquire dependencies from ancestor scopes
/// via its dependency protocol, provide new objects on the DI graph by declaring properties,
/// and instantiate child scopes.
class MyComponent: Component<MyDependency> {

    /// A new object, hotChocolate, is added to the dependency graph. Child scope(s) can then
    /// acquire this via their dependency protocol(s).
    var hotChocolate: Drink {
        return HotChocolate(dependency.chocolate, dependency.milk)
    }

    /// A child scope is always instantiated by its parent(s) scope(s).
    var myChildComponent: MyChildComponent {
        return MyChildComponent(parent: self)
    }
}

This is pretty much it, when writing DI code with Needle. As you can see, everything is real, compilable Swift code. No fragile comments or "annotations". To quickly recap, the three key concepts here are dependency protocol, component and instantiation of child component(s). Please refer to the Getting started with Needle section below for more detailed explanations and advanced topics.

Getting started with Needle

Using and integrating with Needle has two steps. Each of the following steps has detailed instructions and explanations in the linked documents.

  1. Integrate Needle's code generator with your Swift project.
  2. Write application DI code following NeedleFoundation's API.

Installation

Needle has two parts, the NeedleFoundation framework and the executable code generator. Both parts need to be integrated with your Swift project in order to use Needle as your DI system.

Install NeedleFoundation framework

Using Carthage

Please follow the standard Carthage installation process to integrate the NeedleFoundation framework with your Swift project.

github "https://github.com/uber/needle.git" ~> VERSION_OF_NEEDLE

Please specify Needle as a dependency via the standard Swift Package Manager package definition process to integrate the NeedleFoundation framework with your Swift project.

dependencies: [
    .package(url: "https://github.com/uber/needle.git", .upToNextMajor(from: "VERSION_NUMBER")),
],
targets: [
    .target(
        name: "YOUR_MODULE",
        dependencies: [
            "NeedleFoundation",
        ]),
],

Using CocoaPods

Please follow the standard pod integration process and use NeedleFoundation pod.

Install code generator

Using Carthage

If Carthage is used to integrate the NeedleFoundation framework, then a copy of the code generator executable of the corresponding version is already downloaded in the Carthage folder. It can be found at Carthage/Checkouts/needle/Generator/bin/needle.

Using Homebrew

Regardless of how the NeedleFoundation framework is integrated into your project, the generator can always be installed via Homebrew.

brew install needle

The linked document uses a somewhat real example to explain what the dependency injection pattern is, and its benefits.

Translations

Related projects

If you like Needle, check out other related open source projects from our team:

  • Swift Concurrency: a set of concurrency utility classes used by Uber, inspired by the equivalent java.util.concurrent package classes.
  • Swift Abstract Class: a light-weight library along with an executable that enables compile-time safe abstract class development for Swift projects.
  • Swift Common: common libraries used by this set of Swift open source projects.

License

FOSSA Status

needle's People

Contributors

andrewsb avatar balavor avatar brentleyjones avatar catri0na avatar cgossain avatar edrew08 avatar elonpark avatar fchiusolo avatar ffittschen avatar franzbusch avatar iprox avatar lvergos avatar magi82 avatar michaelversus avatar mzheng avatar neakor avatar ohkanghoon avatar ronanrodrigo avatar rudro avatar ryanaveo avatar sbarow avatar shali3 avatar tapthaker avatar tinder-owenthomas avatar toddlee avatar varahash avatar vivekvichare avatar wbond avatar yashrajbharti avatar zacsweers avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

needle's Issues

Carthage build fails on 0.8.1

In 0.8.1 it looks like the sample projects schemes have changed to "Shared" if this is the case then Carthage will try to build the project, this build will fail because carthage update has not been run in the sample directory.

`Why DI?` document does not explain benefits of DI library.

The current Why DI document explains only the benefits of writing loosely coupled code using the DI pattern but doesn't offer any details on how needle specifically helps or why anyone would want to use a dependency injection library in general.

Add error when components are instantiated outside of parent components

Because Needle needs to parse instantiations of Component to generate the DI graph, if a Component is instantiated outside of a parent Component, Needle may miss a path. A missed path will lead to a runtime error of missing dependency provider.

At the same time, simply parsing all source code is not enough to gather all DI paths, because SourceKit cannot provide all type information. For instance, the code below would require a global static analysis to retrieve the DI path:

func globalFunToCreateComponent(withParent parent: ComponentType) -> MyComponent {
    return MyComponent(parent: parent)
}

The actual type of the parent component of MyComponent is unknown until a global static analysis is performed. This would significantly violate Needle's performance requirements.

Provide instructions how to use it

I would love to give it a try and maybe contribute to this project.

How am I suppose to use it? Is there some wiki page or should I look at the code?

Btw, thanks guys, highly appreciated for these efforts.

Support Installation through CocoaPods

Various other code generation tools support CocoaPods as an installation option. e.g. Sourcery, SwiftGen, SwiftyMocky, etc. This makes it easy to onboard new team members since they can just run a pod install and the binary gets downloaded and the run script just runs it from the Pods directory.

Setup generator project

Setup the generator project as a sibling project to the framework project, to host the parser and generator classes.

Feature Request: Handle Component extensions

Currently needle does not parse the extensions of a Component. All your code needs to live in the FooComponent class definition. If you have some reason to put some of the vars in a extension FooComponent (e.g. you like organizing things this way) then the current version just ignores these properties.

Would be nice if, for all the known Component subclasses, needle considered any extensions made to these subclasses.

Needle output order is not stable

This means the generated code will switch around without any changes to the input -- which is pretty annoying.

Also, this means our unit tests are actually flaky at the moment due to this.

This is caused by Dictionary key order not being stable.

The order of key-value pairs in a dictionary is stable between mutations but is otherwise unpredictable.

From : DIctionary reference

Address usage with RIBs

I see that #3 has disposed of RIBs as something that would unnecessary complicate the tutorial / example project and even obscure the subject matter. Nonetheless, it is my sincere desire to learn more about the best-practice workflows that exist when using Needle to manage RIBs components.

Add support for custom header doc

Need to add support for custom header doc for things like disabling linters and marking files as generated for Uber specific use cases.

//  @generated by Needle
//  swiftlint:disable custom_rules

Indicate when a force unwrap/fatalerror is "guarenteed" not to fail

My understanding is that a lot of the force unwraps/fatalerrors in Needle are expected to never crash because the code generator will ensure that they always succeed.

It would be useful for code reviewers without a lot of context, or for users who might be debugging crashes in Needle, to know which code is guaranteed, and which code is potentially dangerous.

This could take the form of new method of force unwrapping, for example:

extension Optional {
     var guarenteedUnwrap: Wrapped {
          return self!
     }
}

Or just be a commitment in the Needle Code Standard (if/when they're written) to only force unwrap when the result is guaranteed to succeed.

Allow configuring logging

Currently warning messages are always printed out. We should accept a command line argument to silence all logs.

At the same time, we should turn warning messages into closures to allow lazy evaluations. This means running in silence mode, we don't even have to create the Strings.

Generated error with different swift code style

When I use Google Swift Style Guide to write code, it has a column limit of 100 characters. So i will line-wrapping like this

public final class SignInComponent: PluginizedComponent<
  SignInDependency,
  SignInPluginExtension,
  SignInNonCoreComponent
> {
  //...
}

But needle can't Recognition these generic arguments, can't generated Dependency Provider code in Factories.

When I write all generic arguments in one line like sample code, it work better.

public final class SignInComponent: PluginizedComponent<SignInDependency, SignInPluginExtension, SignInNonCoreComponent> {
  //...
}

Hope to support different Swift Code Style.
My needle version is v0.8.5.

Support multi-level branching

A component may have multiple parents. Its parents can also have multiple parents. This means a dependency the said component requires must be satisfied at all the ancestor paths. Each of these paths require a different DependencyProvider since the runtime object acquisition paths are different. The current implementation only uses the component itself and its parent as key to retrieve the DependencyProvider. This does not support the multi-level branching case.

Handle Optionality in properties

Right now, we'll match a non-optional property to a contractor that returns an optional and vice-versa. This will cause a crash at run-time.

We need to make sure we track the Optionality in the property's type.

Protocols should inherit from `AnyObject`

There's a bunch of protocols in the project that don't inherit from AnyObject in NeedleFoundation and in generated code. However, it looks like no structs will ever conform to these protocols, so making them inherit from AnyObject might be a minor performance and code size improvement.

Is DependencyGraphParser thread-safe?

The task sequence produces ASTComponent which is a reference type on different threads. The DependencyGraphParser.parse method consumes these objects on a different thread. Even though the CountDownLatch used to retrieve these objects have a NSCondition lock inside, these are separate lock instances per task sequence. This read/write access to ASTComponent may not be thread-safe.

Deal with duplicate constructors in Component classes

needle uses constructors inside of Components to determine parent-child relationships. However, if a parent component calls the same child constructor in more than one place, that child gets repeated and the generated code is invalid as the same path shows up twice in the body.

Generated dependency provider count grows exponentially

Since we currently generate a new dependency provider for each scope, for all paths of that scope, this grows exponentially. We should be able to leverage the parent scope's dependency provider to avoid this explosion of generated code.

Parse expression calls in Component

Expression call types can be used to connect parent and child components. We cannot directly use property types, since a child component may be instantiated within a property but not returned as the return type.

Multi-thread ComponentInstantiationValidator

Since the logic in ComponentInstantiationValidator runs on a large number of file contents with complex Regex, this can become a performance bottleneck. We should multi-thread this processor.

Add plugin extension cycle detection

The following scenario would cause a runtime stack overflow crash.

public protocol CyclicPluginExtension: PluginExtension {
    var cyclicObject: Object { get }
}

public class CyclicCoreComponent<Dep, CyclicPluginExtension, CyclicNonCoreComponent> {
    var cyclicObject: Object {
        return pluginExtension.cyclicObject
    }
}

public protocol CyclicNonCoreDependency {
    var cyclicObject: Object { get }
}

public class CyclicNonCoreComponent<CyclicNonCoreDependency> {
    var cyclicObject: Object {
        return dependency.cyclicObject
    }
}

Feature Request: Handle inheritance with Dependencies

When a dependency is declared that is used by some Component in the system, it must directly inherit from NeedleFoundation.Dependency. You can't have some other dependency and then add to it by using it as a parent. Would be nice if needle looked at protocol inheritance and considered the union of all the properties.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.