How to define static constant in a class in swift

Swift

Swift Problem Overview


I have these definition in my function which work

class MyClass {
    func myFunc() {
        let testStr = "test"
        let testStrLen = countElements(testStr)
    }
}

But if I move 'testStr' and 'testStrLen' to the class level, it won't compile. It said 'MyClass.Type does not have a member named 'testStr'.

class MyClass {
    let testStr = "test"
    let testStrLen = countElements(testStr)
    
    func myFunc() {
        
    }
}

How can I fix this? I don't want to pay the penalty for counting len of a constant 'test' everytime.

Based on my understanding of the comments below, I need to do this:

class MyClass {
    let testStr = "test"
    let testStrLen = countElements("test")
    
    func myFunc() {
        
    }
}

Is there a way I don't need to type/enter "test" twice? Thanks.

Swift Solutions


Solution 1 - Swift

Perhaps a nice idiom for declaring constants for a class in Swift is to just use a struct named MyClassConstants like the following.

struct MyClassConstants{
    static let testStr = "test"
    static let testStrLength = countElements(testStr)

    static let arrayOfTests: [String] = ["foo", "bar", testStr]
}

In this way your constants will be scoped within a declared construct instead of floating around globally.

Update

I've added a static array constant, in response to a comment asking about static array initialization. See Array Literals in "The Swift Programming Language".

Notice that both string literals and the string constant can be used to initialize the array. However, since the array type is known the integer constant testStrLength cannot be used in the array initializer.

Solution 2 - Swift

Adding to @Martin's answer...

If anyone planning to keep an application level constant file, you can group the constant based on their type or nature

struct Constants {
    struct MixpanelConstants {
        static let activeScreen = "Active Screen";
    }
    struct CrashlyticsConstants {
        static let userType = "User Type";
    }
}

Call : Constants.MixpanelConstants.activeScreen

UPDATE 5/5/2019 (kinda off topic but 路‍♂️)

After reading some code guidelines & from personal experiences it seems structs are not the best approach for storing global constants for a couple of reasons. Especially the above code doesn't prevent initialization of the struct. We can achieve it by adding some boilerplate code but there is a better approach

ENUMS

The same can be achieved using an enum with a more secure & clear representation

enum Constants {
    enum MixpanelConstants: String {
        case activeScreen = "Active Screen";
    }
    enum CrashlyticsConstants: String {
        case userType = "User Type";
    }
}

print(Constants.MixpanelConstants.activeScreen.rawValue)

Solution 3 - Swift

If I understand your question correctly, you are asking how you can create class level constants (static - in C++ parlance) such that you don't a) replicate the overhead in every instance, and b have to recompute what is otherwise constant.

The language has evolved - as every reader knows, but as I test this in Xcode 6.3.1, the solution is:

import Swift

class MyClass {
	static let testStr = "test"
	static let testStrLen = count(testStr)
	
	init() {
		println("There are \(MyClass.testStrLen) characters in \(MyClass.testStr)")
	}
}

let a = MyClass()

// -> There are 4 characters in test

I don't know if the static is strictly necessary as the compiler surely only adds only one entry per const variable into the static section of the binary, but it does affect syntax and access. By using static, you can refer to it even when you don't have an instance: MyClass.testStrLen.

Solution 4 - Swift

If you actually want a static property of your class, that isn't currently supported in Swift. The current advice is to get around that by using global constants:

let testStr = "test"
let testStrLen = countElements(testStr)

class MyClass {
    func myFunc() {
    }
}

If you want these to be instance properties instead, you can use a lazy stored property for the length -- it will only get evaluated the first time it is accessed, so you won't be computing it over and over.

class MyClass {
    let testStr: String = "test"
    lazy var testStrLen: Int = countElements(self.testStr)

    func myFunc() {
    }
}

Solution 5 - Swift

Some might want certain class constants public while others private.

private keyword can be used to limit the scope of constants within the same swift file.

class MyClass {

struct Constants {
    
    static let testStr = "test"
    static let testStrLen = testStr.characters.count
    
    //testInt will not be accessable by other classes in different swift files
    private static let testInt = 1
}

func ownFunction()
{
   
    var newInt = Constants.testInt + 1
    
    print("Print testStr=\(Constants.testStr)")
}

}

Other classes will be able to access your class constants like below

class MyClass2
{

func accessOtherConstants()
{
    print("MyClass's testStr=\(MyClass.Constants.testStr)")
}

} 

Solution 6 - Swift

What about using computed properties?

class MyClass {
  class var myConstant: String { return "What is Love? Baby don't hurt me" }
}

MyClass.myConstant

Solution 7 - Swift

Tried on Playground


class MyClass {



struct Constants {
    
    static let testStr = "test"
    static let testStrLen = testStr.characters.count
    
    //testInt will not be accessable by other classes in different swift files
    private static let testInt = 1
    
    static func singletonFunction()
    {
        //accessable
        print("Print singletonFunction testInt=\(testInt)")
        
        var newInt = testStrLen
        newInt = newInt + 1
        print("Print singletonFunction testStr=\(testStr)")
    }
}

func ownFunction() {
    //not accessable
    //var newInt1 = Constants.testInt + 1
    
    var newInt2 = Constants.testStrLen
    newInt2 = newInt2 + 1
    print("Print ownFunction testStr=\(Constants.testStr)")
    print("Print ownFunction newInt2=\(newInt2)")
}
}


let newInt = MyClass.Constants.testStrLen
print("Print testStr=\(MyClass.Constants.testStr)")
print("Print testInt=\(newInt)")


let myClass = MyClass()
myClass.ownFunction()
MyClass.Constants.singletonFunction()


Solution 8 - Swift

testStr is evaluated before initialization, testStrLen too, so bad testStr is not available unless initialization is finished. Dead end.

Two solutions, the first tells that testStrLen can be called only after self availability:

let testStr = "test"
var testStrLen: Int  {
    get {
        countElements(testStr)
    }
}

(usage for myClassInstance.testStrLen is absolutely the same way)

the second solution is smarter:

let testStr: String // don't set here; note: the type needs to be specified
let testStrLen: Int // don't set here; note: ditto

init() {
    // now we can
    testStr = "test"
    testStrLen = countElements(testStr)
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionn179911View Question on Stackoverflow
Solution 1 - SwiftMartin WoolstenhulmeView Answer on Stackoverflow
Solution 2 - SwiftClement PremView Answer on Stackoverflow
Solution 3 - SwiftChris ConoverView Answer on Stackoverflow
Solution 4 - SwiftNate CookView Answer on Stackoverflow
Solution 5 - SwiftChinLoongView Answer on Stackoverflow
Solution 6 - SwiftEmin Bugra SaralView Answer on Stackoverflow
Solution 7 - SwiftKevin6View Answer on Stackoverflow
Solution 8 - SwifttontonCDView Answer on Stackoverflow