Skip to content

1.0.0 - Property Wrappers

Compare
Choose a tag to compare
@rockbruno rockbruno released this 12 Aug 14:15
· 44 commits to master since this release
73ee81d
  • Removed AnyDependenciesInitializer
  • Removed AnyFeature
  • Remove the Dependency protocol
  • Added the @Dependency property wrapper

The ugly boilerplate of this framework like AnyFeature and AnyDependenciesInitializer are a result of the Swift compiler's limitation on generics. In this PR, I tested the creation of features using property wrappers instead of the usual dependencies struct. It worked, and by using Mirror to resolve the properties, I am able to drop the associated type from the Feature and all other ugly type-erasures alongside it :)

Here's how features looked before this change:

private enum ProfileFeature: Feature {
    struct Dependencies {
        let client: HTTPClientProtocol
    }

    static var dependenciesInitializer: AnyDependenciesInitializer {
        return AnyDependenciesInitializer(Dependencies.init)
    }

    static func build(
        dependencies: ProfileFeature.Dependencies,
        fromRoute route: Route?
    ) -> UIViewController {
        return ProfileViewController(dependencies: dependencies)
    }
}

With property wrappers, I can remove all the boilerplate:

private struct ProfileFeature: Feature {

    @Dependency var client: HTTPClientProtocol

    func build(fromRoute route: Route?) -> UIViewController {
        return ProfileViewController(client: client)
    }
}

The Feature became a struct to make use of the compiler's synthesized initializers, and the properties are automatically resolved by RouterService. You don't need to create the annoying dependenciesInitializer type anymore and you don't need to worry about features that have zero or one dependency.

This simplified structure makes it easier to develop and test new features. If you want to test build(), you can do either:

let feature = ProfileFeature()
feature.resolve(withStore: yourCustomStore)
feature.build(...)

or the more useful injection through the init:

let feature = ProfileFeature(client: .init(resolvedValue: myMockClient))
feature.build(...)

The original Dependency protocol was also removed because it was just an alias for AnyObject.