This question is for people with a lot of experience of Swinject for Swift.
I will show the problematic code, and my question is at the bottom.
There's quite a lot of code, sorry about that.
This is MySwinjectStoryboard.swift
registration:
import Swinject
extension SwinjectStoryboard
{
class func setup ()
{
defaultContainer.register( Stopwatch.self )
{
responder in Stopwatch(
signals: responder.resolve( SignalsService.self )!
)
}
defaultContainer.register( SignalsService.self )
{
_ in SignalsService()
}.inObjectScope( .Container )
defaultContainer.register( ImageService.self )
{
responder in ImageService(
signals: responder.resolve( SignalsService.self )!
, stopwatch: responder.resolve( Stopwatch.self )!
)
}.inObjectScope( .Container )
defaultContainer.registerForStoryboard( StartUpViewController.self )
{
resolvable, viewController in
viewController.stopwatch = resolvable.resolve( Stopwatch.self )!
viewController.image = resolvable.resolve( ImageService.self )!
}
}
}
This is Stopwatch.swift
, which simply pauses a while before firing an onComplete handler:
import Foundation
class Stopwatch: StopwatchProtocol
{
var key: String { return "Stopwatch_\( _key ).Complete" }
private var
_signals: SignalsProtocol
, _key: UInt16
, _timer: NSTimer?
, _data: AnyObject?
func startWith (
Delay delay: Double
, ForListener closure: ( String, Any? ) -> Void
){
_data = nil
_startWith( Delay: delay, ForListener: closure )
}
func stop ()
{
guard let timer = _timer else { return }
timer.invalidate()
_timer = nil
_data = nil
}
private func _startWith (
Delay delay: Double
, ForListener closure: ( String, Any? ) -> Void
){
stop()
_timer = NSTimer.scheduledTimerWithTimeInterval(
NSTimeInterval( delay )
, target: self
, selector: #selector( _onTimerComplete )
, userInfo: nil
, repeats: false
)
}
@objc private func _onTimerComplete ()
{
stop()
print( "stopwatch with key `\( key )` complete." )
}
required init ( signals: SignalsProtocol )
{
_signals = signals
_key = getPrimaryKey()
print( "primary key: \( _key )" )
}
}
ImageService.swift
presently simply accepts a signals and a stopwatch property via an init
function:
protocol ImageProtocol {}
class ImageService: ImageProtocol
{
private let
_signals: SignalsProtocol
, _stopwatch: StopwatchProtocol
required init (
signals: SignalsProtocol
, stopwatch: StopwatchProtocol
){
_signals = signals
_stopwatch = stopwatch
lo( "ImageService key: \( _stopwatch.key )" )
}
}
SignalsService.swift
is currently an empty Model class:
protocol SignalsProtocol {}
class SignalsService: SignalsProtocol {}
Whilst StartUpViewController.swift
is a basic UIViewController
that currently just accepts its injected properties:
import UIKit
class StartUpViewController: UIViewController
{
var image: ImageService? {
willSet {
guard _image == nil else { return }
_image = newValue
}
}
var signals: SignalsService? {
willSet {
guard _signals == nil else { return }
_signals = newValue
}
}
var stopwatch: StopwatchProtocol? {
willSet {
guard _stopwatch == nil else { return }
_stopwatch = newValue
print( "StartUpViewController key: \( _stopwatch.key )" )
}
}
internal var
_image: ImageService!
, _signals: SignalsService!
, _stopwatch: Stopwatch!
}
And finally getPrivateKey()
is simply a global static, returning unique Ints:
private var _primaryKey = UInt16( 0 )
func getPrimaryKey () -> UInt16
{
_primaryKey += 1
return _primaryKey
}
Now as I understand it, the way I have registered Stopwatch.swift
in MySwinjectStoryboard.swift
means that each time an instance is injected, it will be a new, discrete instance. However, both ImageService.swift
and StartUpViewController.swift
are being injected with the same instance:
StartUpViewController key: Stopwatch_2.Complete
ImageService key: Stopwatch_2.Complete
ImageService
's key ought to be:
ImageService key: Stopwatch_3.Complete
Does anyone know why this is happening, please? Thank you.
Default scope for services is .Graph
. From documentation:
With ObjectScope.Graph, an instance is always created, as in ObjectScope.None, if you directly call resolve method of a container, but instances resolved in factory closures are shared during the resolution of the root instance to construct the object graph.
If you want a unique instance to be created for each reference even during object graph resolution, you should use object scope .None
, i.e.
defaultContainer.register(Stopwatch.self) { resolver in
Stopwatch(signals: resolver.resolve(SignalsService.self)!)
}.inObjectScope(.None)