How to Define Static Constant in a Class in Swift

How to define static constant in a generic class in swift?

You can define global constant with fileprivate or private access level in the same .swift file where your generic class is defined. So it will not be visible outside of this file and will not pollute global (module) namespace.

If you need to access this constant from outside of current file then declare it as internal (default access level) or public and name it like ClassConstant so it will be obvious that it relates to Class.

Read more about access levels in Swift 3.

When to use static constant and variable in Swift?

When you define a static var/let into a class (or struct), that information will be shared among all the instances (or values).

Sharing information

class Animal {
static var nums = 0

init() {
Animal.nums += 1
}
}

let dog = Animal()
Animal.nums // 1
let cat = Animal()
Animal.nums // 2

As you can see here, I created 2 separate instances of Animal but both do share the same static variable nums.

Singleton

Often a static constant is used to adopt the Singleton pattern. In this case we want no more than 1 instance of a class to be allocated.
To do that we save the reference to the shared instance inside a constant and we do hide the initializer.

class Singleton {
static let sharedInstance = Singleton()

private init() { }

func doSomething() { }
}

Now when we need the Singleton instance we write

Singleton.sharedInstance.doSomething()
Singleton.sharedInstance.doSomething()
Singleton.sharedInstance.doSomething()

This approach does allow us to use always the same instance, even in different points of the app.

Why can I define a static constant that depends on another static constant in the same type, but not for properties?

Type and instance.

  • The type MyStruct always exists. static properties belong to the type. So they just sit there and can do anything they like or be related in any way. Okay, yes, it has to come into existence when the program starts, but under the hood the static property initializers are all lazy so it's okay for one to depend on another (not in a circular way of course).

  • An instance of MyStruct is a thing that has to be created, each time you say MyStruct(...). When you say that, the instance properties must be initialized. An instance (not static) property belongs to the instance. So its initializer's value cannot refer to self because self is exactly what we are in the middle of creating, i.e. the instance. This line:

    let myInstancePlusOne = myInstance + 1

    ...really means

    let myInstancePlusOne = self.myInstance + 1

    ...but that is exactly what you are not allowed to say; at the time you are initializing this property there is no self yet, it is what you are initializing. And you can work around this by declaring that property lazy (with other adjustments in the syntax).

static let vs let for declaring class specific constants

Neither. Use an uninhabited (caseless) enum to create a Constant namespace; it reads better.

class CurrencyConverter {
private enum Constant {
static let conversionRate = 1.3
}
func convertToForeign(fromlocal local: Double) -> Double {
return local * Constant.conversionRate
}
}

let c = CurrencyConverter()
print(c.convertToForeign(fromlocal: 5))

What is the purpose of using static on a constant in a Swift structure?

It has multiple applications, including but not limited by the following:

1) To give a constant separate namespace, if constants have same names.

struct A {
static let width: Int = 100
}

struct B {
static let width: Int = 100
}
print(A.width)
print(B.width)

2) Static constants are 'lazy' by design, so if you are about to use lazy-behaved global constant, it might be handy to put it in a structure.

3) To show your coworkers that constant is applicable to specific domain where given structure is used.

4) Organize your configuration in sections:Theme.Layout.itemHeight or Label.Font.avenirNext

Initializing class constants in Swift

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.

What is the use of static keyword if let keyword used to define constants/immutables in swift?

I will break them down for you:

  • var : used to create a variable
  • let : used to create a constant
  • static : used to create type properties with either let or var. These are shared between all objects of a class.

Now you can combine to get the desired out come:

  • static let key = "API_KEY" : type property that is constant
  • static var cnt = 0 : type property that is a variable
  • let id = 0 : constant (can be assigned only once, but can be assigned at run time)
  • var price = 0 : variable

So to sum everything up var and let define mutability while static and lack of define scope. You might use static var to keep track of how many instances you have created, while you might want to use just varfor a price that is different from object to object. Hope this clears things up a bit.

Example Code:

class MyClass{
static let typeProperty = "API_KEY"
static var instancesOfMyClass = 0
var price = 9.99
let id = 5

}

let obj = MyClass()
obj.price // 9.99
obj.id // 5

MyClass.typeProperty // "API_KEY"
MyClass.instancesOfMyClass // 0


Related Topics



Leave a reply



Submit