Skip to content

Property wrappers for dependency container #39

@stefanomondino

Description

@stefanomondino

It would be nice (?) to create a @propertyWrapper around dependencies when using a dependency container.

Something like:

class Something: Boomerang.DependencyContainer {
let container = ObjectContainer()

@Dependency(closure: { MyThingImplementation() }
var myThing: MyThing

init() {
}

}

As of today the main issues are:

  1. there is no official way to access the "enclosing" object from withing a property wrapper. Some workaround is possible with undocumented api (see below)
  2. when actually using the propertyWrapper (@Dependency in my example above), you cannot reference self, as it's not yet available. Therefore, all closures used for registration cannot use variables that may have been stored in self, and self should be passed to the closure itself
  3. To pass self to the closure, we should define the enclosing type upon PW creation itself, which sounds awful and superfluous.

A possible implementation (for future reference) is:

@propertyWrapper struct Dependency<Value, Container: Boomerang.DependencyContainer> where Container.DependencyKey == ObjectIdentifier {
    private let closure: (Container) -> Value
    private let scope: Boomerang.Container<ObjectIdentifier>.Scope
    init(_ containerType: Container.Type = Container.self,
         scope: Boomerang.Container<ObjectIdentifier>.Scope = .singleton,
         closure: @escaping (Container) -> Value) {
        self.scope = scope
        self.closure = closure
    }
    
    @available(*, unavailable,
       message: "This property wrapper can only be applied to classes"
   )
    public var wrappedValue: Value {
            get { fatalError() }
            // swiftlint:disable unused_setter_value
            set { fatalError() }
        }
        
    public static subscript(
            _enclosingInstance instance: Container,
            wrapped wrappedKeyPath: ReferenceWritableKeyPath<Container, Value>,
            storage storageKeyPath: ReferenceWritableKeyPath<Container, Self>
        ) -> Value {
                get { 
                   guard let value = instance.resolve(Value.self) else {
                    instance.register(for: Value.self,
                                         scope: instance[keyPath: storageKeyPath].scope,
                                         handler: { instance[keyPath: storageKeyPath].closure(instance) })
                    return instance.unsafeResolve()
                }
                return value
             }
              set {}
        }
}

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions