119

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.

Ky -
  • 30,724
  • 51
  • 192
  • 308
n179911
  • 19,547
  • 46
  • 120
  • 162
  • 3
    possible duplicate of [ViewControl.Type does not have a member named](http://stackoverflow.com/questions/25855137/viewcontrol-type-does-not-have-a-member-named) (The initial value of a property cannot depend on another property.) – Martin R Sep 18 '14 at 17:19
  • 1
    possible duplicate of [Change the X and Y in a CGRectMake](http://stackoverflow.com/questions/25854300/change-the-x-and-y-in-a-cgrectmake) (with a nice solution using a lazy property) – Martin R Sep 18 '14 at 17:25
  • *"Is there a way I don't need to type/enter "test" twice?"* - Yes. Move the initialialization of testStrLen into an init method (as suggested in an answer to the first possible duplicate), or use a lazy initialialization (as suggested in an answer to the second possible duplicate). – Martin R Sep 18 '14 at 17:38

8 Answers8

191

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.

Community
  • 1
  • 1
Martin Woolstenhulme
  • 3,968
  • 4
  • 24
  • 25
  • 3
    Could you not instead just declare the constants as static private? Wouldn't they then no longer be global? – sethfri Apr 22 '15 at 07:57
  • 4
    beats using .rawValue as I was using enum – DogCoffee Apr 23 '15 at 07:48
  • @sethfri You can't declare the constants as static private in the way other answers have declared global constants because they are scoped outside of a class or structure. Xcode will give an error that says "Static properties may only be declared on a type." See [Type Properties](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Properties.html#//apple_ref/doc/uid/TP40014097-CH14-ID254). The keyword `static` is used to declare type properties. – Martin Woolstenhulme Apr 30 '15 at 00:56
  • This appears best answer ..... if however, there were more static let members in the struct MyClassConstants and the members were all the same type ie say string .... is there an easy way to create an array of the static let members for use elsewhere in your code – lozflan May 26 '15 at 23:50
  • How about just declaring "private let myConstantVar = 100" on the top of the file outside the Type declaration ? – the Reverend Jun 24 '15 at 20:48
  • You can declare `private let myConstantVar = 100` in the file, but that's not really a static constant because `static` takes on a different meaning in Swift as opposed to other languages. It is strictly used to declare properties on types in Swift. Also, I'm not sure the scoping rules for declaring a var or constant in Swift as they apply to a file, so the intent of a static value may not be captured with file-scoped declarations. – Martin Woolstenhulme Jun 24 '15 at 21:01
  • 2
    This won't be the best solution if the code is to be used from Objective C as well, since Swift structs are not translated into Objective C – mbpro Oct 22 '15 at 08:40
  • Does this play nice when you inherit the class that wraps the struct? – eugene Dec 29 '15 at 08:17
  • 1
    What the difference between declaring `static` constants in `Struct` and in `Class`es? – Jauzee Mar 07 '16 at 12:31
  • @Jauzee As Swift has evolved it has added features that early on it didn't permit. "Stored Type Properties" on classes, which is the concept most similar to the static constants asked about in this question, is one such feature. This restriction is no longer in place, so you may declare static properties for both classes and structs. See [Swift 2.2 Properties](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Properties.html#//apple_ref/doc/uid/TP40014097-CH14-ID254) documentation from Apple. – Martin Woolstenhulme Mar 07 '16 at 17:48
  • Why wouldn't you use `enum MyClassConstants{}` instead of struct? If you simply need a constant container `enum` is more straightforward than `struct` as `enum` won't be able to initialized in this case but `struct` will be if one does `let constants = MyClassConstants()`. – YOUNG Aug 29 '16 at 08:59
  • 1
    @YOUNG [Swift 2.2 Enumerations](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Enumerations.html#//apple_ref/doc/uid/TP40014097-CH12-ID145) could work, but generally enums having a stronger semantic association than just grouping constants in general. In the link, Swift enums feature both associated values and raw values. You could use raw values, but the syntax is `MyClassConstants.testStr.rawValue` to access the value, which isn't as syntactically friendly as just `MyClassConstants.testStr`. – Martin Woolstenhulme Aug 29 '16 at 18:24
  • Should `MyClassConstants` go inside of `MyClass` or outside of it? – Octocat Jan 06 '18 at 17:03
  • @Octocat At the time I answered this it had to go outside. Swift has evolved significantly and it now supports nested types, see [Swift 4.0.3](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/NestedTypes.html), although I don't know when nested types were introduced. – Martin Woolstenhulme Jan 13 '18 at 18:35
81

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)
Clement Prem
  • 3,112
  • 2
  • 23
  • 43
  • 2
    Thank you. I like this, provides an elegant way of managing constants through struct. – Abhijeet Sep 21 '15 at 07:11
  • 1
    With more examples I have added a better approach [here](https://www.youtube.com/watch?v=wAEc6NbtNWc&index=1&list=LL1VZu0skZTr7WU4eoqYQSuw) – LC 웃 Feb 26 '17 at 01:55
  • Still prefer structs, esp for constants on for the scope of the class. They save having to prefix the constant with the class name. Instead you can make a child struct called "Const" or whatever. – Hari Honor Apr 05 '21 at 10:57
17

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.

Chris Conover
  • 8,889
  • 5
  • 52
  • 68
11

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() {
    }
}
Nate Cook
  • 92,417
  • 32
  • 217
  • 178
  • 1
    +1 Too bad there still seems not better way than these global variables. – Drux Oct 27 '14 at 07:10
  • 1
    I know this is a language constraint at the moment, but I would rather avoid using globals at all costs. I think using struct containers w/ something like ClassNameConstants would be a better approach for now. – Zorayr Jan 26 '15 at 06:41
9

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)")
}

} 
ChinLoong
  • 1,735
  • 24
  • 26
6

What about using computed properties?

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

MyClass.myConstant
Emin Bugra Saral
  • 3,756
  • 1
  • 18
  • 25
3

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()
Kevin6
  • 41
  • 1
0

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)
}
tontonCD
  • 320
  • 2
  • 6