5

I realize that static variables are implicitly lazy, which is really great. Doing the below will not create the instance until it's first called:

static var test = Test()

However, assigning a new instance to the static variable initializes the original, then assigns the new instance which is troubling for me:

SomeType.test = AnotherTest() //Initializes Test then AnotherTest type

To give more context on what I'm trying to do, I'm trying to setup a pure Swift dependency injection using this article. It's not working so well when swapping the types out in my unit tests because the original type always gets initialized when assigning the mock type.

Here's a more fuller, playground sample:

protocol MyProtocol { }

class MyClass: MyProtocol {
    init() { print("MyClass.init") }
}

////

struct MyMap {
    static var prop1: MyProtocol = MyClass()
}

protocol MyInject {

}

extension MyInject {
    var prop1: MyProtocol { return MyMap.prop1 }
}

////

class MyMock: MyProtocol {
    init() { print("MyMock.init") }
}

// Swapping types underneath first initializes
// original type, then mock type :(
MyMap.prop1 = MyMock()

prints: MyClass.init
prints: MyMock.init

How can I make MyMap.prop1 = MyMock() not first initialize the original MyClass first?

1 Answer 1

3

You need lazy loading. Try this:

struct MyMap {
    private static var _prop1: MyProtocol?
    static var prop1: MyProtocol {
        get { return _prop1 ?? MyClass() }
        set(value) { _prop1 = value }
    }
}

Or this:

struct MyMap {
    private static var _prop1: MyProtocol?
    static var prop1: MyProtocol {
        get {
            if _prop1 == nil {
                _prop1 = MyClass()
            }
            return _prop1!
        }
        set(value) { _prop1 = value }
    }
}
Sign up to request clarification or add additional context in comments.

7 Comments

Whoa, why does this work??!.. Is this a bug in Swift or was this intentional?
Its as intended by swift. A var needs a value as long as it exists. This value may be nil or a concrete one. Your var can not be nil by your definition, therefore you have to specify a value (or compile throw an error). It doesn't matter if you call the get or set. Swift has to ensure that your var has a value before working with it. Thats what apple call "swift is save".
@codealchimist But static stored properties are lazily loaded – it shouldn't have to call the property initialiser if it's first assigned to before being loaded. But that's just currently how Swift behaves.
It's also worth noting that static stored property initialisers are thread safe, whereas lazily loading like in your second example is not thread safe.
@Hamish True both comments. There was a bug report for it too, maybe it will change in future swift versions and static isn't lazy anymore or it works as expected from a lazy var. Think it will be the first one for compatibility reasons. We will see..
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.