Why does UILabel.text result in “fatal error: unexpectedly found nil while unwrapping an Optional value”?
You might want to follow best practices and avoid the use of !
as much as possible.
The error you've got happens when you try to access a value or reference stored in an optional that does not contain any value (i.e., it is nil
).
In your case, if usernameLbl
is nil
, usernameLbl.text
will crash your app (Think "null pointer exception" in Java, perhaps?).
It is very common to define outlests as:
@IBOutlet weak var label: UILabel!
...instead of the safer:
@IBOutlet weak var label: UILabel?
...because the first one allows you to access its properties without using the ?
(i.e. label.text
vs. label?.text
). But there's an implicit assumption that the label is not nil: the !
by itself does nothing to prevent this (it only silences the compiler, telling it "I know what I'm doing, trust me!").
That shouldn't be a problem as long as you only access it after viewDidLoad()
and your outlets are proerly connected in Interface Builder/storyboard, because in that case it will be guaranteed to not be nil
.
My guess is that you forgot to hook up the outlet to your label.
Here is a tutorial on storyboards in case it helps.
The whole reason outlets need to be defined as optionals (implicitly unwrapped !
or "standard" ?
) is that in Swift, all properties must have a value by the time the instance is initialized (actually, even before calling the super class initializer if that applies).
For view controllers, instance initialization happens before the subviews can be initialized and assigned to the properties/outlets. That happens in the method loadView()
, which is called lazily (only just before the view is actually needed for display). So by making all subviews optional (and variable, not constant), they can have the temporary "value" of nil
by the time the initializer completes execution (thus satisfying Swift's rules).
Edit: If your outlet is still nil
at runtime even though it is connected in interface builder, you can at least try to intercept any code resetting it and see what's going on, with a property observer:
@IBOutlet weak var label: UILabel! {
didSet {
if label == nil {
print("Label set to nil!")
// ^ SET A BREAKPOINT IN THIS LINE
}
}
}
Seeing an @IBOutlet
on a UITabBarController
is very suspicious. You generally have a tab bar controller which presents child UIViewController
subclasses, and put labels on those child view controllers, not the tab bar controller. A tab bar controller does not generally have IBOutlet
references. The child view controllers would have them.
Double check to which class you've connected that @IBOutlet
and confirm whether it's a subclass of UIViewController
or UITabBarController
.
Think of the !
operator as the "crash if nil" operator. It's actually called the "force unwrap" operator, and is used to "unwrap" an Optional. If the optional contains a nil, it triggers the same error as trying to dereference a null pointer in other languages.
You should avoid the !
operator until you really understand what it does.
IB (Interface Builder) sets up outlets as force-unwrapped so that you can reference them without having to constantly check them for nil. The idea is that your outlets should never be nil, and if they are it's a problem and you WANT to crash. Think of it as a fuse. It triggers an obvious, early failure that's easy to find and fix.
Having a broken outlet (or missing IBAction) is one of the easiest mistakes to make. With an outlet declared as
@IBOutlet var someOutlet: UILabel!
The compiler lets you reference it without unwrapping it, but you will crash if the outlet is nil.
You can avoid the force-unwrap by adding a ?
after the outlet name, or using and if let
or a guard statement.
However, with outlets, the thing to do is to realize you've got a broken outlet and just fix it. Then your code works fine.
To fix a broken outlet, control-drag from IB to the @IBoutlet
declaration in your code. Think of it as hooking up a cable in your entertainment system.
When you declare an outlet in your code the editor puts a little open circle in the margin to the left of the @IBOutlet
declaration. When the outlet is connected, the editor shows a filled-in circle, so it's easy to see if an outlet is connected or not. (Same with @IBAction
methods.)