hmlongco / Resolver

Swift Ultralight Dependency Injection / Service Locator framework
MIT License
2.14k stars 188 forks source link

Implements doesn't pass the arguments to the resolve #100

Closed georgescumihai closed 3 years ago

georgescumihai commented 3 years ago

Version: 1.4.1 Implements function calls register and but the factory is not passing the arguments parameter to the resolve function.

Use case

You register your service for your scope

register { _, args in
            XYZService(uuid: args())
        }
        .scope(.deviceDebug)
        .implements(XYZServiceType.self)

You resolve the service

let service: XYZServiceType = Resolver.resolve(args: UUID())

This will cause a crash, in the register factory function because args is ["": nil].

Possible solution

public final func implements<Protocol>(_ type: Protocol.Type, name: Resolver.Name? = nil) -> ResolverOptions<Service> {
-        resolver?.register(type.self, name: name) { r, _ in r.resolve(Service.self) as? Protocol }
+        resolver?.register(type.self, name: name) { r, args in r.resolve(Service.self, args: args) as? Protocol }
        return self
}
hmlongco commented 3 years ago

I think your solution works. Check develop branch.

georgescumihai commented 3 years ago

The change seems to fix the issue, still I have a question on how to use the scope.

Call scope first ?

register { _, args in
            XYZService(uuid: args())
        }
        .scope(.deviceDebug)
        .implements(XYZServiceType.self)

Call scope last ?

register { _, args in
            XYZService(uuid: args())
        }
        .implements(XYZServiceType.self)
        .scope(.deviceDebug)

Because calling register will create the default resolver ResolverScopeGraph.

hmlongco commented 3 years ago

Scope affects the base registration factory. Doesn't matter if it's before or after.

I revised one of the scope tests to also use a protocol and it seems to check out.

    func testResolverScopeCachedImplements() {
        // Reset...
        ResolverScope.cached.reset()
        resolver.register { XYZSessionService() }
            .implements(XYZSessionProtocol.self)
            .scope(.cached)
        let service1: XYZSessionService? = resolver.optional()
        let service2: XYZSessionService? = resolver.optional()
        let service3: XYZSessionProtocol? = resolver.optional()
        XCTAssertNotNil(service1)
        XCTAssertNotNil(service2)
        XCTAssertNotNil(service3)
        var originalID: UUID!
        if let s1 = service1, let s2 = service2, let s3 = service3 {
            XCTAssert(s1.id == s2.id)
            XCTAssert(s2.id == s3.id) // if all ids match then both services and the protocol are the same object.
            originalID = s1.id
        } else {
            XCTFail("sessions not cached")
        }
        // Reset...
        ResolverScope.cached.reset()
        // ...and try again
        if let newService: XYZSessionService = resolver.optional() {
            XCTAssert(originalID != newService.id)
        } else {
            XCTFail("newService not resolved")
        }
        // Reset...
        ResolverScope.cached.reset()
        // ...and try again with protocol
        if let newService: XYZSessionProtocol = resolver.optional() {
            XCTAssert(originalID != newService.id)
        } else {
            XCTFail("newService not resolved")
        }
    }
georgescumihai commented 3 years ago

The issue was with the parameter pass, it would have worked probably without parameter as well in the past.

hmlongco commented 3 years ago

Fixed in 1.4.2

marko8994 commented 2 years ago

@hmlongco, can you please revise the @georgescumihai question regarding order of scope func call when registering service that implements some protocol? I have encountered some issues since I needed this specific service to have unique scope and I couldn't change it for this specific registration when using implements function. Instead, as a workaround I used:

register { XYZService() as XYZServiceType }.scope(.unique)

and it seems that it works fine this way.