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 alllazy
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 (notstatic
) property belongs to the instance. So its initializer's value cannot refer toself
becauseself
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 propertylazy
(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 variablelet
: used to create a constantstatic
: used to create type properties with eitherlet
orvar
. 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 constantstatic var cnt = 0
: type property that is a variablelet 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 var
for 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
Is There a Preferred Technique to Prohibit Pasting into a Uitextfield
How to Cast an Any Value with Nil in It to a Any
Compare Three Values for Equality
Why Is Swift's Ternary Operator So Picky About Whitespace
Calling a Function Defined in Assembly from Swift
Get Lat and Long from Tapped Overlay in Google Maps
Subtle Cast Warning When Using SQLite.Swift ... Binding? to Any
In Swift,There's No Way to Get the Returned Function's Argument Names
How Are Hash Collisions Handled
Swift Combine How to Skip an Event
Swift: Using "/" Slash in Filename with Createdirectoryatpath
Index Out of Range in Swift with Removeatindex
Why Can't I Mutate a Variable Initially Set to a Certain Parameter When the Func Was Called
How to Assign an Array to a Class Property by Reference Rather Than a Copy
Any Way to Chain == and || Operands