Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added ManualResolver #365

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 117 additions & 0 deletions Sources/ManualResolver.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
//
// ManualResolver.swift
// Swinject
//
// Created by Brian Radebaugh on 8/12/18.
// Copyright © 2018 Swinject Contributors. All rights reserved.
//

/**
This class is used to registered types where the initializer is controlled by some third party framework
(i.e. UIViewController, UIView)

--- Example ---
```
// Object Being Resolved
class MyViewController: UIViewController {
var myObject: MyObject!

init(withCoder coder: Coder) {
super.init(coder: coder)
ManualResolver.finishConstruction(me: self)
}

init(withNibNameOrNil nibNameOrNil: Coder) {
super.init(nibNameOrNil: nibNameOrNil)
ManualResolver.finishConstruction(me: self)
}

func inject(myObject: MyObject) {
self.myObject = myObject
}
}

// Registration
class MyAssembly: Assembly {
func assemble(container: Container) {
container.registerManualConstruction(MyViewController.self) { (resolver, myViewController) in
let myObject = resolver.resolve(MyObject.self)!
myViewController.inject(myObject)
}
}
}

// Starting the Application
let assembler = Assembler([MyAssembly()], container: ManualResolver.container)
assembler.resolver.resolve(RootObjectThatKicksEverythingOff.self)
```
*/
public class ManualResolver {
/**
This is the container all that holds all of the manual registrations.
*/
public static var container = Container()

/**
This property is used to determine if the object being resolved should be resolved, and primarily used for testing.

During testing, to get the correct test doubles injected you might need to call the inject function manually.
Instead of having the object injected twice and ensuring that all dependent types are registered properly,
the ManualResolver can be turned off by setting this property to false.

Defaults to `true`.
*/
static var shouldResolve = true

/**
This function should be called in all designated initializers (or other logic starting points).

- Parameter me: This is the object being resolved.
Typically will be passing self in when executing an initializer.
- Parameter as: This is the type the object was registered as.
Defaults to type inference.
If the object is registered as a protocol then that protocol should be specified here.
- Parameter name: This is the name for the registration.
Typically used for multiple registrations of the same type.
Defaults to nil.
*/
public static func finishConstruction<T>(me object: T, as type: T.Type = T.self, name: String? = nil) {
guard shouldResolve else {
return
}

typealias FactoryType = ((Resolver, T)) -> Any
_ = container._resolve(name: name, option: nil) { (factory: FactoryType) in
factory((self.container, object)) as Any
} as T?
}
}

extension Container {
/**
This function should be used to register manually resolved objects instead of the standard `register()`.

- Parameter objectType: This is the type that is being registered.
- Parameter name: This is the name for the registration.
Typically used for multiple registrations of the same type.
Defaults to nil.
- Parameter injectionHandler: This is the block that will be executed when the object is resolved.
It provides the resolver and the object being resolved.
In this closure is were the manually created inject function should be called.

- Returns: The service entry for this registration.
Typically, this object is ignored during registration or to specify options for the registration
(i.e. `inObjectScope()`).
*/
@discardableResult
public func registerManualConstruction<T>(_ objectType: T.Type,
name: String? = nil,
injectionHandler: @escaping (Resolver, T) -> Void) -> ServiceEntry<T> {
let factory = { (r: Resolver, o: T) -> T in
injectionHandler(r, o)
return o
}

return _register(objectType, factory: factory, name: name, option: nil)
}
}
26 changes: 26 additions & 0 deletions Swinject.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@
objects = {

/* Begin PBXBuildFile section */
7AF5D8FA2120F21800B08AD5 /* ManualResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF5D8F92120F21800B08AD5 /* ManualResolver.swift */; };
7AF5D8FB2120F21800B08AD5 /* ManualResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF5D8F92120F21800B08AD5 /* ManualResolver.swift */; };
7AF5D8FC2120F21800B08AD5 /* ManualResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF5D8F92120F21800B08AD5 /* ManualResolver.swift */; };
7AF5D8FD2120F21800B08AD5 /* ManualResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF5D8F92120F21800B08AD5 /* ManualResolver.swift */; };
7AF5D9042120F3B600B08AD5 /* ManualResolverSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF5D9032120F3B600B08AD5 /* ManualResolverSpec.swift */; };
7AF5D9052120F3B600B08AD5 /* ManualResolverSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF5D9032120F3B600B08AD5 /* ManualResolverSpec.swift */; };
7AF5D9062120F3B600B08AD5 /* ManualResolverSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF5D9032120F3B600B08AD5 /* ManualResolverSpec.swift */; };
7AF5D9072120F3BA00B08AD5 /* BasicViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF5D8FE2120F2BB00B08AD5 /* BasicViewController.swift */; };
7AF5D9082120F3BD00B08AD5 /* BasicViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF5D8FE2120F2BB00B08AD5 /* BasicViewController.swift */; };
7AF5D9092120F3BF00B08AD5 /* BasicViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF5D8FE2120F2BB00B08AD5 /* BasicViewController.swift */; };
90B029751C18599200A6A521 /* Assembly.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90B029741C18599200A6A521 /* Assembly.swift */; };
90B029761C18599200A6A521 /* Assembly.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90B029741C18599200A6A521 /* Assembly.swift */; };
90B029771C18599200A6A521 /* Assembly.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90B029741C18599200A6A521 /* Assembly.swift */; };
Expand Down Expand Up @@ -252,6 +262,9 @@
/* End PBXCopyFilesBuildPhase section */

/* Begin PBXFileReference section */
7AF5D8F92120F21800B08AD5 /* ManualResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManualResolver.swift; sourceTree = "<group>"; };
7AF5D8FE2120F2BB00B08AD5 /* BasicViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BasicViewController.swift; sourceTree = "<group>"; };
7AF5D9032120F3B600B08AD5 /* ManualResolverSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManualResolverSpec.swift; sourceTree = "<group>"; };
90B029741C18599200A6A521 /* Assembly.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Assembly.swift; sourceTree = "<group>"; };
90B029781C185B1600A6A521 /* Assembler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Assembler.swift; sourceTree = "<group>"; };
90B0297E1C18666200A6A521 /* AssemblerSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AssemblerSpec.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -483,6 +496,7 @@
981ABE861B5FC9DF00294975 /* Info.plist */,
CDC4FCF3205028B00021C60F /* InstanceWrapper.swift */,
CD5E9A3520515898009090F9 /* GraphIdentifier.swift */,
7AF5D8F92120F21800B08AD5 /* ManualResolver.swift */,
);
path = Sources;
sourceTree = "<group>";
Expand All @@ -500,6 +514,7 @@
CD6A9F811EEFCB7A0087E851 /* ContainerSpec.CustomStringConvertible.swift */,
CD89E35120371D7900A9ED33 /* ContainerSpec.Behavior.swift */,
CDBD0E672035EDF20030F566 /* ContainerSpec.TypeForwarding.swift */,
7AF5D9032120F3B600B08AD5 /* ManualResolverSpec.swift */,
984774FE1C034C5C0092A757 /* SynchronizedResolverSpec.swift */,
9884E2A91B60C51C00120259 /* ServiceKeySpec.swift */,
9855C5C71B689D9000DADB0B /* ServiceEntrySpec.swift */,
Expand Down Expand Up @@ -547,6 +562,7 @@
90B0298A1C186D5300A6A521 /* LoadAwareAssembly.swift */,
CD65F048205943D400FB43EE /* EmploymentAssembly.swift */,
CDB19E51204ED97400D67123 /* BehaviorFakes.swift */,
7AF5D8FE2120F2BB00B08AD5 /* BasicViewController.swift */,
);
name = Fakes;
sourceTree = "<group>";
Expand Down Expand Up @@ -960,6 +976,7 @@
CDC4FCF5205028B00021C60F /* InstanceWrapper.swift in Sources */,
CD5E9A3720515898009090F9 /* GraphIdentifier.swift in Sources */,
90B029761C18599200A6A521 /* Assembly.swift in Sources */,
7AF5D8FB2120F21800B08AD5 /* ManualResolver.swift in Sources */,
CD3B32931DD6123F00B208A3 /* InstanceStorage.swift in Sources */,
984774F11C02F25D0092A757 /* SynchronizedResolver.swift in Sources */,
981899E51B5FFE5800C702D0 /* Container.swift in Sources */,
Expand All @@ -985,7 +1002,9 @@
CD5E9A33205157E3009090F9 /* ContainerSpec.GraphCaching.swift in Sources */,
CD3C2CF31D98EF4A00863421 /* ContainerSpec.DebugHelper.swift in Sources */,
CDC4FCFE20502AA00021C60F /* LazySpec.swift in Sources */,
7AF5D9052120F3B600B08AD5 /* ManualResolverSpec.swift in Sources */,
984775001C034C5C0092A757 /* SynchronizedResolverSpec.swift in Sources */,
7AF5D9082120F3BD00B08AD5 /* BasicViewController.swift in Sources */,
CD6A9F831EEFCB7A0087E851 /* ContainerSpec.CustomStringConvertible.swift in Sources */,
CD3B329D1DD61F2400B208A3 /* ContainerSpec.CustomScope.swift in Sources */,
9848611D1B6F9B7000C07072 /* ContainerSpec.Arguments.swift in Sources */,
Expand Down Expand Up @@ -1032,6 +1051,7 @@
CDBBACF61D9EAD60002F5EF9 /* Container.Logging.swift in Sources */,
98E550C31DEF066300BE6304 /* UnavailableItems.swift in Sources */,
90B029751C18599200A6A521 /* Assembly.swift in Sources */,
7AF5D8FA2120F21800B08AD5 /* ManualResolver.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand All @@ -1046,7 +1066,9 @@
CD5E9A32205157E3009090F9 /* ContainerSpec.GraphCaching.swift in Sources */,
CD3C2CF21D98EF4A00863421 /* ContainerSpec.DebugHelper.swift in Sources */,
CDC4FCFD20502A9F0021C60F /* LazySpec.swift in Sources */,
7AF5D9042120F3B600B08AD5 /* ManualResolverSpec.swift in Sources */,
90B0298B1C186D5300A6A521 /* LoadAwareAssembly.swift in Sources */,
7AF5D9072120F3BA00B08AD5 /* BasicViewController.swift in Sources */,
CD6A9F821EEFCB7A0087E851 /* ContainerSpec.CustomStringConvertible.swift in Sources */,
CD3B329C1DD61F2400B208A3 /* ContainerSpec.CustomScope.swift in Sources */,
9848611C1B6F9B7000C07072 /* ContainerSpec.Arguments.swift in Sources */,
Expand Down Expand Up @@ -1081,6 +1103,7 @@
CDC4FCF6205028B00021C60F /* InstanceWrapper.swift in Sources */,
CD5E9A3820515898009090F9 /* GraphIdentifier.swift in Sources */,
9850111F1BBE7E8900A2CCFC /* ServiceKey.swift in Sources */,
7AF5D8FC2120F21800B08AD5 /* ManualResolver.swift in Sources */,
CD3B32941DD6123F00B208A3 /* InstanceStorage.swift in Sources */,
90B0297B1C185B1600A6A521 /* Assembler.swift in Sources */,
985011201BBE7E8900A2CCFC /* ServiceEntry.swift in Sources */,
Expand Down Expand Up @@ -1110,6 +1133,7 @@
CDC4FCF7205028B00021C60F /* InstanceWrapper.swift in Sources */,
CD5E9A3920515898009090F9 /* GraphIdentifier.swift in Sources */,
98689CA01BBFC7EB0005C6D3 /* Resolver.swift in Sources */,
7AF5D8FD2120F21800B08AD5 /* ManualResolver.swift in Sources */,
98689C991BBFC7EB0005C6D3 /* Container.swift in Sources */,
CD3B32951DD6124000B208A3 /* InstanceStorage.swift in Sources */,
98689C9A1BBFC7EB0005C6D3 /* ServiceKey.swift in Sources */,
Expand All @@ -1135,7 +1159,9 @@
CD5E9A34205157E3009090F9 /* ContainerSpec.GraphCaching.swift in Sources */,
CD3C2CF41D98EF4A00863421 /* ContainerSpec.DebugHelper.swift in Sources */,
CDC4FCFF20502AA10021C60F /* LazySpec.swift in Sources */,
7AF5D9062120F3B600B08AD5 /* ManualResolverSpec.swift in Sources */,
98689CB61BBFD5110005C6D3 /* Person.swift in Sources */,
7AF5D9092120F3BF00B08AD5 /* BasicViewController.swift in Sources */,
CD6A9F841EEFCB7A0087E851 /* ContainerSpec.CustomStringConvertible.swift in Sources */,
CD3B329E1DD61F2400B208A3 /* ContainerSpec.CustomScope.swift in Sources */,
90B0298D1C186D5300A6A521 /* LoadAwareAssembly.swift in Sources */,
Expand Down
42 changes: 42 additions & 0 deletions Tests/SwinjectTests/BasicViewController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//
// BasicViewController.swift
// Swinject
//
// Created by Brian Radebaugh on 8/12/18.
// Copyright © 2018 Swinject Contributors. All rights reserved.
//

import Swinject

/**
Not actually a ViewController so it works with all test targets.
The inits are to make this class look and feel like a ViewController.
*/
class BasicViewController {
/**
This is force unwrapped because this class will not work correctly if it is not resolved first.
Since this class is resolved in all initializers then there is not reasonable way this will be nil
(assuming it is registered correctly).
*/
var food: Food!

init(withCoder coder: Any) {
ManualResolver.finishConstruction(me: self)
}

init(nilOrNibName: Any?) {
ManualResolver.finishConstruction(me: self)
}

convenience init() {
self.init(nilOrNibName: nil)
}

/**
This is the function that will be called when registering the service,
or during testing to inject a test double.
*/
func inject(food: Food) {
self.food = food
}
}
40 changes: 40 additions & 0 deletions Tests/SwinjectTests/ManualResolverSpec.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//
// ManualResolverSpec.swift
// Swinject
//
// Created by Brian Radebaugh on 8/12/18.
// Copyright © 2018 Swinject Contributors. All rights reserved.
//

import Foundation
import Quick
import Nimble
import Swinject

class MyAssembly: Assembly {
func assemble(container: Container) {
container.register(Food.self) { _ in
return Sushi()
}

container.registerManualConstruction(BasicViewController.self) { (resolver, basicViewController) in
let food = resolver.resolve(Food.self)!

basicViewController.inject(food: food)
}
}
}

class ManualResolverSpec: QuickSpec {
override func spec() {

describe("ManualResolver") {
let assembler: Assembler = Assembler([MyAssembly()], container: ManualResolver.container)

it("should resolve manually registered ") {
let basicViewController = assembler.resolver.resolve(BasicViewController.self)
expect(basicViewController).toNot(beNil())
}
}
}
}