Swiftpack.co -  nicholascross/Resolve as Swift Package
Swiftpack.co is a collection of thousands of indexed Swift packages. Search packages.
nicholascross/Resolve
A swift package enabling decentralised dependency resolution
.package(url: "https://github.com/nicholascross/Resolve.git", from: "4.0.0")

Resolve

A swift package to support dependency resolution with property wrapper support for ease of use.

Usage Example

This is how you would do something similar to Swinject basic usage

let context = ResolutionContext()

context.register { Cat(name: "Mimi") as Animal }
context.register { PetOwner() as Person }
let petOwner: Person = context.resolve()
petOwner.play()
// print: I'm playing with Mimi.

Where the types are defined as follows.

protocol Animal {
    var name: String? { get }
}

class Cat: Animal {
    let name: String?

    init(name: String?) {
        self.name = name
    }
}

protocol Person {
    func play()
}

class PetOwner: Person {
    @Resolve var pet: Animal

    func play() {
        let name = pet.name ?? "someone"
        print("I'm playing with \(name).")
    }
}

What can Resolve do?

@Resolve property wrapper

Resolving registered dependencies is simple just add the @Resolve property wrapper in front of your property.

@Resolve var pet: Animal

This will find the first ResolutionContext that registered this type to resolve the value.

If using a single ResolutionContext per type variant is not appropriate for your use case you can include this as part of your property declaration.

@Resolve(container: someContext) var pet: Animal

Type registration

Before the above will work there must be a defined way to resolve the object that will be returned. This is done by registering a closure that returns the type to be resolved.

Note the casting to Animal, this is allows registration of a new Cat instance any time we resolve the Animal type.

let context = ResolutionContext()
context.register { Cat(name: "Mimi") as Animal }

There can be only a single registration for a given type variant this allows the default registrations to be ignored which might be useful for testing purposes. Earlier registration of mock/stub objects will take precedence allowing you to provide alternate implementation for testing purposes.

If an alternate registration is truly required the old registration can be removed and a new one registered.

context.removeResolver(for: Person.self)

Variant resolution

There can be multiple variants registered for a single type.

context.register(variant: "long_date") { () -> DateFormatter in
    let formatter = DateFormatter()
    formatter.dateFormat = "MMM yyyy"
    return formatter
}

context.register(variant: "short_date") { () -> DateFormatter in
    let formatter = DateFormatter()
    formatter.dateFormat = "dd/MM/yyyy"
    return formatter
}
// This will resolve expected date formatter
@Resolve(variant:"long_date") var formatter: DateFormatter

Object lifetime management

Resolve does not require explicit management of the lifetime of any resolved objects. The above date example would need to be modified in order to prevent a new date formatter being created everytime it was resolved.

let dateFormatter: DateFormatter = {
    let formatter = DateFormatter()
    formatter.dateFormat = "MMM yyyy"
    return formatter
}()

context.register(variant: "long_date") { dateFormatter }

Objects lifetime can be specified explictly using the convenience functions persistent, transient, ephemeral.

// persistent life time will always resolve the same object
context.persistent { Example() }

// transient life time will resolve the same object provided there is a strong reference to it elsewhere
context.transient { Example2() }

// ephemeral life time will always resolve a new object
context.ephemeral { Example3() }

Optional property storage

Assigning to the formatter property would currently be a no-op but backing storage can be updated by implementing a storer closure.

var dateFormatter: DateFormatter = {
    let formatter = DateFormatter()
    formatter.dateFormat = "MMM yyyy"
    return formatter
}()

context.register(variant: "long_date", resolver: { dateFormatter }, storer: { f in dateFormatter = f })

The above registration will allow the following to property to be used as a setter as well.

// This will resolve expected date formatter
@Resolve(variant:"long_date") var formatter: DateFormatter

The property can be set directly or via calling the store function on the ResolutionContext.

self.formatter = someOtherFormatter
// OR
context.store(object: someOtherFormatter, variant: "long_date")

Type variants registered with the persistent or transient functions may have thier stored values replaced.

let petOwner = context.register { PetOwner() }
let mimi = context.transient(variant: "Mimi") { Cat(name: "Mimi") as Animal }

petOwner.play()
// print: I'm playing with Mimi.

petOwner.pet = Cat(name: "Franky")
petOwner.play()
// print: I'm playing with Franky.

Hierarchical registration

When registering a type conforming to DependencyRegister protocol the registerDependencies function will be called giving you an opportunity to register any further dependencies.

This allows the distribution of dependency registration through out the application in hierarchical manner, as one register may register another whilst in turn registering its own dependencies.

final class ExampleRegister: DependencyRegister {
    func registerDependencies(container: DependencyContainer) {
        container.register(variant: "Mimi") { Cat(name: "Mimi") as Animal }
        container.register { PetOwner() as Person }
    }
}

let context = ResolutionContext()
context.register { ExampleObject() }

let petOwner: Person = context.resolve()
petOwner.play()
// print: I'm playing with Mimi.

GitHub

link
Stars: 0
Last commit: 1 year ago

Ad: Job Offers

iOS Software Engineer @ Perry Street Software
Perry Street Software is Jack’d and SCRUFF. We are two of the world’s largest gay, bi, trans and queer social dating apps on iOS and Android. Our brands reach more than 20 million members worldwide so members can connect, meet and express themselves on a platform that prioritizes privacy and security. We invest heavily into SwiftUI and using Swift Packages to modularize the codebase.

Submit a free job ad (while I'm testing this). The analytics numbers for this website are here.

Swiftpack is being maintained by Petr Pavlik | @ptrpavlik | @swiftpackco | API | Analytics