Swift Variable Declaration and Initialize
This one:
let x: Int = 4
creates a non-optional variable x
and initialises it to 4
. x
can be used without issue.
This one:
let x: Int
// Cannot do anything with x yet
x = 4
creates a non-optional variable x
with no defined value. It cannot be used without first assigning it to a value, either directly (as in your example) or by the result of some other statement. If you do try and use it, you'll get a compile-time error.
Swift Variables Initialization
Actually you have 5 ways to initialize properties.
There is no correct way, the way depends on the needs.
Basically declare objects like UILabel
always – if possible – as constant (let
).
The 5 ways are:
Initialization in the declaration line
let label = UILabel(frame:...
Initialization in the
init
method, you don't have to declare the property as implicit unwrapped optional.let label: UILabel
init() { ... label = UILabel(frame:...) ... }
The first two ways are practically identical.
Initialization in a method like
viewDidLoad
, in this case you have to declare the property as (implicit unwrapped) optional and also asvar
var label: UILabel!
on viewDidLoad()
...
label = UILabel(frame:...)
}Initialization using a closure to assign a default (computed) value. The closure is called once when the class is initialized and it is not possible to use other properties of the class in the closure.
let label: UILabel = {
let lbl = UILabel(frame:...)
lbl.text = "Foo"
return lbl
}()Lazy initialization using a closure. The closure is called (once) when the property is accessed the first time and you can use other properties of the class.
The property must be declared asvar
let labelText = "Bar"
lazy var label: UILabel = {
let lbl = UILabel(frame:...)
lbl.text = "Foo" + self.labelText
return lbl
}()
How properly declare a variable in Swift?
Method 1 is a standard variable declaration for a String. It has a setter and a getter
var dogName: String = "Charlie"
print(dogName) -> "Charlie"
dogName = "Rex" // Valid
Method 2 is a computed property of type String and is read-only
var dogName: String {
return "Charlie"
}
print(dogName) -> "Charlie"
dogName = "Rex" // Invalid as property is read-only
Method 3 is a read-only property of type () -> String, so basically a lambda function.
let dogName = {
return "Charlie"
}
print(dogName) -> "(Function)"
print(dogName()) -> "Charlie"
dogName = "Rex" // Invalid as property is read-only
Method 4 is a closure that will be executed when the containing object is initialised. As it is a var
you can replace it with another value
var dogName: String = {
return "Charlie"
}()
print(dogName) -> "Charlie"
dogName = "Rex" // Valid
That being said, as Method 4 is a closure, you can execute other commands in it. Here is an example where you could use this construct to initialise a UILabel:
var dogNameLabel: UILabel = {
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 10, height: 10))
label.text = "Charlie"
return label
}()
How to initialize a variable, but once done, its value is final
Based on Babul Prabhakar answer, but a bit more clean.
Approach 1:
var test: String? { didSet { test = oldValue ?? test } }
test = "Initial string"
test = "Some other string"
test = "Lets try one last time"
print(test) // "Initial string"
Edit: made it shorter
Approach 2:
let value: String
value = "Initial string"
I guess the second approach is the "built-in" option of Swift to declare a variable once. This is only possible when the variable is assigned right after the declaration though.
Initialize a swift variable without knowing the type
You should use definitely use an enum with associated values here. As Schottky said in their answer, you'll need a custom Codable
implementation. Here's an example, implementing just 4 of the cases, assuming that User
, Badge
, Message
, Debate
are all Codable
.
enum UserNotification: Codable {
case getBadge(Badge)
case groupReply(Message, Debate, Message)
case levelUnlock(User)
case userFollowLevelUnlock
var notifyType: String {
switch self {
case .getBadge:
return "get_badge"
case .groupReply:
return "group_reply"
case .levelUnlock:
return "level_unlock"
case .userFollowLevelUnlock:
return "user_follow_level_unlock"
}
}
enum CodingKeys: String, CodingKey {
/*
When decoding/encoding, use convertFromSnakeCase/convertToSnakeCase to convert the key names to/from snake case
No need to hard code them here
e.g.
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
*/
case notifyType
case target
case secondTarget
case thirdTarget
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(notifyType, forKey: .notifyType)
switch self {
case .getBadge(let badge):
// you can also encode the unused targets here if your backend needs them
try container.encode(badge, forKey: .secondTarget)
case .groupReply(let msg1, let debate, let msg2):
try container.encode(msg1, forKey: .target)
try container.encode(debate, forKey: .secondTarget)
try container.encode(msg2, forKey: .thirdTarget)
case .levelUnlock(let user):
try container.encode(user, forKey: .target)
case .userFollowLevelUnlock:
break // nothing more to encode in this case
}
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
switch try container.decode(String.self, forKey: .notifyType) {
case "get_badge":
self = .getBadge(
try container.decode(Badge.self, forKey: .secondTarget)
)
case "group_reply":
self = .groupReply(
try container.decode(Message.self, forKey: .target),
try container.decode(Debate.self, forKey: .secondTarget),
try container.decode(Message.self, forKey: .thirdTarget)
)
case "level_unlock":
self = .levelUnlock(
try container.decode(User.self, forKey: .target)
)
case "user_follow_level_unlock":
self = .userFollowLevelUnlock
default:
throw DecodingError.dataCorruptedError(forKey: .notifyType, in: container, debugDescription: "Unknown notifyType")
}
}
}
This way, the notification
property can just be:
private var notification: UserNotification
and setResource
is trivial.
Can I Declare and initialize multiple variables in one line with a tuple?
func getImagesTuple(from array: Array<String>) -> (UIImage, UIImage, UIImage) {
(UIImage(named: array[0])!, UIImage(named: array[1])!, UIImage(named: array[2])!)
}
let (imageOne, imageTwo, imageThree) = getImagesTuple(from: ["imageOne", "imageTwo", "imageThree"])
initialize Swift Class variables in declaration or with init?
Unless you are providing the initial value as an initializer parameter, for which you have for obvious reasons do that in the initializer, you can use any of the 2 ways.
My rules are:
- if there are several initializers, and the property is initialized with the same value in all cases, I prefer inline initialization
- if the property is (or should be) immutable, I prefer inline initialization
- if the property can change during the instance lifetime, I prefer constructor initialization
but besides the first one, the other 2 are just based on personal preference.
How to declare but not initialize a property in a Swift class
Create an implicitly unwrapped optional - this will act like a normal variable but it does not need to be initialised - its initial value is nil
. Just ensure the value is set before it is used otherwise you will get a fatal error for unwrapping nil.
class car {
var oneWheel: Wheel!
func run(inputWheel: Wheel) {
wheel = inputWheel
}
}
Related Topics
Take Screenshot of Host App Using iOS Share/Action Extensions
Querying in Firebase by Child of Child
Testing If a Decimal Is a Whole Number in Swift
Swift Nsusernotification Doesn't Show While App Is Active
Get the String Up to a Specific Character
Swift. Combine. How to Call a Publisher Block More Than Once When Retry
Swift Programmatically Create Function for Button with a Closure
How to Add Documentation to Enum Associated Values in Swift
No Designated Init for Skshapenode(Circleofradius: Radius)
Table View Cellforrowatindexpath Warning
Operation Not Permitted When Executing 'Killall' with Swift
How to Open to a Specific View Using Home Quick Actions
How to Reference the View's Window in Swift 3.X Using Storyboards/Cocoa
[_Nscftimer Copywithzone:]: Unrecognized Selector Sent to Instance