Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Initializing class constants in Swift

Tags:

swift

I was trying to do something like this (it is a contrived example for demonstration purposes only):

class Test {
  let hello = "hello"
  let world = "world"
  let phrase: String {
    return self.hello + self.world
  }
}

but you can't use let for computed properties in Swift. Is there a way to do this without having to write an init() method? Thanks!

like image 795
RobertJoseph Avatar asked Feb 07 '23 01:02

RobertJoseph


2 Answers

The reason let doesn't work on a read-only calculated property is because it's used to state that the property's actual value will never change after being set – not that the property is read-only. As the Apple docs say (emphasis mine):

You must declare computed properties — including read-only computed properties — as variable properties with the var keyword, because their value is not fixed. The let keyword is only used for constant properties, to indicate that their values cannot be changed once they are set as part of instance initialization.

You therefore need to use var in order to reflect the fact that a calculated property's value could change at any time, as you're creating it on the fly when accessing it. Although in your code, this can't happen – as your hello and world properties are let constants themselves. However, Swift is unable to infer this, so you still have to use var.

For example:

class Test {
    let hello = "hello"
    let world = "world"
    var phrase: String {
        return self.hello + self.world
    }
}

(This doesn't change the readability of the property – as because you haven't provided it with a setter, it's still read-only)

However in your case, you might want to consider using a lazy property instead, as your hello and world properties are constants. A lazy property is created when it's first accessed, and keeps its value for the rest of its lifetime – meaning you won't have to keep on concatenating two constants together every time you access it.

For example:

class Test {
    let hello = "hello"
    let world = "world"
    lazy var phrase: String = {
        return self.hello + self.world
    }()
}

Another characteristic of let properties is that their value should always be known before initialisation. Because the value of a lazy property might not be known before then, you also need to define it as a var.


If you're still adamant on wanting a let property for this, then as far as I can see, you have two options.

The first is the neatest (although you've said you don't want to do it) – you can assign your phrase property in the initialiser. As long as you do this before the super.init call, you don't have to deal with optionals. For example:

class Test {
    let hello = "hello"
    let world = "world"
    let phrase: String

    init() {
        phrase = hello+world
    }
}

You simply cannot do it inline, as self at that scope refers to the static class, not an instance of the class. Therefore you cannot access the instance members, and have to use init() or a lazy/calculated property.

The second option is pretty hacky – you can mirror your hello and world properties at class level, so you can therefore access them inline in your phrase declaration. For example:

class Test {
    static let hello = "hello"
    static let world = "world"

    // for some reason, Swift has trouble inferring the type
    // of the static mirrored versions of these properties
    let hello:String = Test.hello
    let world:String = Test.world

    let phrase = hello+world
}

If you don't actually need your hello or world properties as instance properties, then you can just make them static – which will solve your problem.

like image 144
Hamish Avatar answered Feb 19 '23 22:02

Hamish


Yes to make it work as computed properties, replace let to var. Like,

class Test {
  let hello = "hello"
  let world = "world"
  var phrase: String {
     return self.hello + self.world
  }
}

This way you can use it without init()

like image 45
Jigar Tarsariya Avatar answered Feb 19 '23 21:02

Jigar Tarsariya