Code Monkey home page Code Monkey logo

dikit's People

Contributors

benjohnde avatar brototyp avatar gaebel avatar ishansharma 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

dikit's Issues

Consider Scope.prototype renaming

I stumbled upon the Scope enum and found the prototype case a bit misleading:

/// Defines the `Component` scope.
public enum Scope {
    /// The same instance of the class is returned each time it is injected.
    case singleton

    /// A new instance of the class is created each time it is injected.
    case prototype // Consider renaming this to `clone` or `instance`?
}
// I'd consider prototype as a term for some form of an abstract class
// the attached documentation above even describes the behavior as creating an instance
container.register(as: .prototype) { ComponentX() }

// I'd found one of these more meaningful
container.register(as: .clone) { ComponentX() }
container.register(as: .instance) { ComponentX() }

Working with contexts

Ability to add components linked with a context / scope in order to instantiate and release objects for a very specific target / action.

Rethink lazy injection

Currently lazy injection works through a different computed propery behaviour. Due to using an enum for @Inject it is mutating. Hence it is not really useful in a struct, as we are not allowed to alter a variable within a struct Cannot use mutating getter on immutable value: function call returns immutable value. And as 'mutating' may only be used on 'func' declarations we need a different approach.

Add K/N support

Add support for Kotlin/Native, still depends on proper graph validation.

Bug when resolving some dependencies

Hello, i have a bug when trying to resolve a dependency that i have in multiple targets

My componentStack is this:

(lldb) po self.componentStack
▿ 8 elements
  ▿ 0 : 2 elements
    ▿ key : AnyHashable(DIKit.ComponentIdentifier(tag: nil, type: Restaurants.RestaurantsLocalDataSource))
      ▿ value : ComponentIdentifier
        - tag : nil
        - type : Restaurants.RestaurantsLocalDataSource
    ▿ value : <Component<RestaurantsLocalDataSource>: 0x600000b0c060>
  ▿ 1 : 2 elements
    ▿ key : AnyHashable(DIKit.ComponentIdentifier(tag: nil, type: Restaurants.RestaurantsRemoteDataSource))
      ▿ value : ComponentIdentifier
        - tag : nil
        - type : Restaurants.RestaurantsRemoteDataSource
    ▿ value : <Component<RestaurantsRemoteDataSource>: 0x600000b0c120>
  ▿ 2 : 2 elements
    ▿ key : AnyHashable(DIKit.ComponentIdentifier(tag: nil, type: RestaurantList.RestaurantListViewModel))
      ▿ value : ComponentIdentifier
        - tag : nil
        - type : RestaurantList.RestaurantListViewModel
    ▿ value : <Component<RestaurantListViewModel>: 0x600000b14360>
  ▿ 3 : 2 elements
    ▿ key : AnyHashable(DIKit.ComponentIdentifier(tag: nil, type: Chopsticks.StartupViewModel))
      ▿ value : ComponentIdentifier
        - tag : nil
        - type : Chopsticks.StartupViewModel
    ▿ value : <Component<StartupViewModel>: 0x600000b043c0>
  ▿ 4 : 2 elements
    ▿ key : AnyHashable(DIKit.ComponentIdentifier(tag: nil, type: Swift.Optional<__C.FIRApp>))
      ▿ value : ComponentIdentifier
        - tag : nil
        - type : Swift.Optional<__C.FIRApp>
    ▿ value : <Component<Optional<FIRApp>>: 0x600000b04360>
  ▿ 5 : 2 elements
    ▿ key : AnyHashable(DIKit.ComponentIdentifier(tag: nil, type: RealmSwift.Realm))
      ▿ value : ComponentIdentifier
        - tag : nil
        - type : RealmSwift.Realm
    ▿ value : <Component<Realm>: 0x600000b14240>
  ▿ 6 : 2 elements
    ▿ key : AnyHashable(DIKit.ComponentIdentifier(tag: nil, type: Restaurants.RestaurantRepository))
      ▿ value : ComponentIdentifier
        - tag : nil
        - type : Restaurants.RestaurantRepository
    ▿ value : <Component<RestaurantRepository>: 0x600000b0c000>
  ▿ 7 : 2 elements
    ▿ key : AnyHashable(DIKit.ComponentIdentifier(tag: nil, type: FIRFirestore))
      ▿ value : ComponentIdentifier
        - tag : nil
        - type : FIRFirestore
    ▿ value : <Component<FIRFirestore>: 0x600000b14300>

The identifier is this:

(lldb) po identifier
▿ ComponentIdentifier
  - tag : nil
  - type : FIRFirestore

But this still finds no matching component in the stack.
I have a module providing the dependency in one target (dynamic framework) of my app, and the dependency itself is requested in another target

Schermata 2021-07-09 alle 15 32 00

Schermata 2021-07-09 alle 15 32 31

Runtime module override

I'm trying to cover a use case when the root module needs to be rebuilt due to an API environment change. Once the network client changes, the affected modules should be redefined.

In Koin there's a loadKoinModules that lets the developer replace affected modules after startKoin is called (reset cached data when the user logs out).

Let me know if you had something similar in mind and I'm happy to contribute.

This would also fix #42

Define method to use at runtime

It should be so useful, expose another method beyond the one already declared to permit to set at runtime a new dependency container.
You have a strict rule on setting root (and it's clear the motivation) but if I want to inject a new dependecies at runtime for unit test?

Let me know and thanks a lot for this useful lib.

@OptionalInject does not work with actual optional

The @OptionalInject wrapper only works if one provides a non-optional component. If the component is optional, the @OptionalInject wrapped instance always ends up nil.

This appears to be because the component is registered using its optional type (e.g. Optional<ComponentF>) but then the resolve fails as it is looking for non-optional type (e.g. ComponentF).

This can be demonstrated in DIKitTests testOptionalInjection by making ComponentF's init optional.

optional_init

Doing this causes the test to fail.

Thoughts: loss of types

When playing around with the example application I kind of stumbled upon a potential issue with the implementation itself:

Having a DependencyContainer register a service:

public extension DependencyContainer {
    static var app: DependencyContainer {
        return DependencyContainer { container in
            container.register(as: .prototype) { 
                 // This will register LocalStorage explicitly with it's protocol
                 LocalStorage() as LocalStorageProtocol
            }
        }
    }
}

The type of the registered service/component kind of gets lost when using the inject() method:
As a developer I may not know what type of services are registered 🤔

class ModalViewController: UIViewController {
    let storage: LocalStorageProtocol = inject() // This will compile and will run
    let storage: LocalStorage = inject() // This will compile but won't run

    @IBAction func close(_ sender: Any) {
        dismiss(animated: true)
    }
}

I'm not sure if that's possible from my current understanding of the DIKit code but having something like inject(ofType: LocalStorage) could help? Maybe the resolve() function could be improved to use stacks of components/instances that are better typed that using a String representation? 🤔

Graph validation at compile-time

Could be implemented by traversing the Sources and collect each @Inject for creating the dependency-graph. If no circular dependencies are found, continue with collecting information about the dependency container creations and derive the final module catalogue. From here on it is rather easy to validate.

Rename Injectable to Inject

Breaks the API but I like the intent of an action (which can also break) a lot more of being an injectable.

SwiftUI no AppDelegate support

Hi I'm trying to use this library with the new SwiftUI lifecycle that doesn't have an AppDelegate, it seemed ok to initialize it in the "App" class (the one with @main annotation) but when i do, it throws an error "Fatal error: root DependencyContainer is not yet set."

@main
class ChopsticksApp: App {
    @Inject var viewModel: StartupViewModel
    
    required init() {
        FirebaseApp.configure()
        DependencyContainer.defined(by: modules { .main; .restaurant; .common; .restaurantList })
    }
    
    var body: some Scene {
        WindowGroup {
            
        }
    }
}

Also this method of initialization is giving another error
Schermata 2021-07-05 alle 01 35 38

The modules are defined like this

public extension DependencyContainer {
    static var main = module {
        single { FirebaseApp.app() as FirebaseApp? }
    }
}

Small validation of graph

This could be implemented by checking if all registered components in a container can resolve all their dependencies. Some other ideas?

Add integration test

We want to test the full cycle with an running mobile app (which gets tested) within a sim.

Constructor dependency injection

Currently, a propertyWrapper can neither be used on local variables nor on a constructor / functions. There is already a possibility to use constructor dependency injection like:

let backend: BackendProtocol
init(backend: BackendProtocol = resolve()) {
   self.backend = backend
}

Rework root container context

Removal of DefinesContainer, use proper syntax and create some kind of app context for the shared instance of the root container.

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.