In swift, why is UIlabel optional? - ios

Looking at this,
#IBOutlet weak var label1 : UILabel!
Why is UILabel an optional? The fact that I established a IBOutlet connection to that UILabel means that the UILabel DEFINATELY exists, and wouldn't be nil. So why is it an optional UIlabel? and not UILabel?

It's becouse non-optional variables have to be initialized in constructor and leave all lifecicle.
IBOutlets are weak also, what means it can become nil while e.g. view contoller are still alive.

This is about how The View Controller life cycle actually works into play.
Let's be known of Model, View, and Controllers, before proceeding.
The View Controller object itself gets instantiated before the view is loaded i.e. your label. During this time, since view is not even loaded, there is no reference to the label, as it still needs to be created from storyboard.
For this reason, the object becomes nil for a pretty short unnoticable moment. And, when the view is loaded: ViewController, viewDidLoad(), then only the labels reference is assigned to the variable.
This outlines a short description on why optionals are really used in iOS.

Related

Why are the outlets by default declared as weak?

I looked up this topic on google but have not got an understandable answer yet, the problem is that I know that when two classes are coupled together by instantiating an object in the first class from the second class and declaring another object in the second class from the first class this will cause a retain cycle that should be broken by using the keyword weak or unowned , yet I can not apply this way of thinking on the IBOutlets being declared as weak
for example
class SignUpViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBOutlet weak var signUpBttn: UIButton!
}
this is an outlet in my viewController class, why the outlet is declared as weak ? as per what I understand is that to have a retain cycle, the uibutton class should have an object from the viewController class so that the two classes (viewController and uibutton) become coupled together
can anybody clarify what is happening under the hood?
all ui element in a view controller are part of the viewController view. so view is a UIView class and outlets are reference in the viewController to UIView class elements, so if the view is removed form the view hierarchy all cross element should be weak to avoid cross reference. thew eid problem about this is the reference from apple MVC, where a ViewController is a Controller but have a bunch o related code of the view part. all you outlets should be placed in the UIView class of you ViewController.
TLDR - in my opinion it's not a good decision.
A strong reference denotes ownership. The view or view controller is the owner of the views therefore the logical choice should be strong.
However, in most situations this does not really matter because the views are also strongly referenced by their parent in the view hierarchy.
When does it matter? It matters in situations when you are dynamically updating the view hierarchy by removing views/constraints etc. Once you remove a view/constraint from the hierarchy and you don't have a strong reference to it, it will get removed from memory.
Also note that the combination of weak and ! is a bit dangerous because ! denotes a reference that you expect never to be nil.
This can lead to errors, for example:
#IBOutlet weak var constraint: NSLayoutConstraint!
...
constraint.isActive = false // removes constraint from hierarchy, assigns `nil` to constraint
...
constraint.isActive = true // crashes the app
Personally, I always make outlets strong. For any weak references I always use ? and not !.
Note that weak in this case doesn't have anything to do with protection against reference cycles. It was just a personal decision by Xcode developers.
Historically, there might be a connection with UIViewController.viewDidUnload. However, that method is never called since iOS 6.
Apple recommends that an #IBOutlet be declared as strong. Many discussions / articles can be found about this.
If you take a look at this video from Apple's 2015 WWDC, right around the 32:30 mark: https://developer.apple.com/videos/play/wwdc2015/407/
He states:
In general you should make your outlet strong, especially if you are connecting an outlet to a sub view or to a constraint that's not always going to be retained by the view hierarchy. The only time you really need to make an outlet weak is if you have a custom view that references something back up the view hierarchy and in general that's not recommended.

Why can't an #IBOutlet be assigned let instead of var in Swift?

I'm following a tutorial on Swift and I noticed that the author uses var instead of let when declaring an #IBOutlet variable. So I became curious as to why I can't use let instead since an object's properties are still mutable even if the object is constant or is this not the case?
The error Xcode shows when using let is
#IBOutlet attribute requires property to be mutable
but I'm confused because questionLabel is a UILabel object and not necessarily a property of an object. Or is the questionLabel object a property of the current viewController?
import UIKit
class ViewController: UIViewController {
#IBOutlet let questionLabel: UILabel!
}
Thank you in advance if I'm over analyzing.
The #IBOulet marked properties are generally properties of a ViewController that are connected using the interface builder. The view you create in the interface builder has to actually connect the interface elements in it to the properties during your actual application runtime.
For that reason it firstly creates a new ViewController using some init without connecting any interface elements. They only get connected at a later stage. For the runtime to be able to hook the properties up to the view elements after the object creation has completed they cannot be constants, they have to be mutable. Because they do not have a value after the initializer has finished they have to be optionals. And to not make using the properties cumbersome afterwards they are implicitly unwrapped optionals, so that you do not have to write label!.property but label.property suffices.
That is why your code crashes as soon as you try to do something with an IBOutlet variable which you failed to connect and that is also the reason why you cannot use / change / manipulate those fields in the initializer.
Regarding your actual var / let confusion. Yes, the object itself that is referenced using let can be changed, e.g. the text of a UILabel BUT the actual object reference cannot be changed. That would mean that if you do not give the constant a specific value in the initializer it would forever remain nil.
For the simple reason that it is not assigned during initialization (in the initXXX methods) but later, when the view is being loaded.
The compiler actually cannot be even sure that the variable is ever assigned because the view loading is comletely dynamic.
In swift, all vars and lets can be thought of as properties.
A property is immutable (a constant) if it's declared with let. It's mutable (a variable) if it's declared using the var keyword. That is the defining difference between let and var.
Outlets must be mutable because their value does not get set until after the object is initialized. (The view controller gets initialized and it's outlets don't get loaded right away.)
You are right questionLabel is an object of type UILabel but used as a property of your class ViewController. That's why you have #IBOutlet attribute requires property to be mutable. If you use var you are saying that a property is mutable. If you use let you are saying that the property is immutable.
Try to create questionLabel without #IBOutletand see what's going on. Probably, you can put let in front.
First the ViewController is created, then the view tree is built. That means that when the ViewController finished it's init these views don't exist yet. They will be added just before viewDidLoad by parsing the XML-like data of the storyboard or XIB.
I know that it's Apple's default way of doing things but I would always write my outlets like:
#IBOutlet let questionLabel: UILabel?
For the simple reason it's absolutely not proven this label will really exist at run time. For example when reusing ViewControllers over multiple screens, changing the layout after setting the connections and so on this outlet might not be set. If you would use the questionLabel defined as UILabel! and it would be nil your application will crash. I don't think applications in production should ever crash over something silly like that.
For real application safety the only way to know really for sure it exists is to build your UI's in code. But the ease of use of Storyboards for one off screens is really tempting, I still use them a lot.

Why does UILabel.text result in “fatal error: unexpectedly found nil while unwrapping an Optional value”?

I've read several answers to this question and have tried all recommendations with no success. I'm fairly new to swift and am building an app with Swift, PHP and MySQL. I'm receiving the error after the user has logged in to the app and the system should be displaying the username via a label using UILabel.text. The error is occurring on setting a value to the UILabel.text variable. My code is included below. I've tried to hardcode values on other pages and am getting this error throughout my project.
import UIKit
class HomeViewController: UITabBarController {
#IBOutlet var usernameLbl: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// set global variables
let username = (user!["username"] as AnyObject).uppercased
// label values
print(usernameLbl ?? username!)
usernameLbl.text = username
}
}
I'm accessing the HomeViewController programmatically. The app uses a tab bar and the first page of it is Home. The code is from a course I'm taking on Udemy. Here is how I'm accessing Home:
// func to pass to home page or to tabBar
func login() {
// refer to our Main.storyboard
let storyboard = UIStoryboard(name: "Main", bundle: nil)
// store our tabBar Object from Main.storyboard in tabBar var
let tabBar = storyboard.instantiateViewController(withIdentifier: "tabBar")
// present tabBar that is storing in tabBar var
window?.rootViewController = tabBar
}
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.)

Why the default type of the label is a forced unwrapped optional?

When I create a label in the view (dragged from the Object library) and make the IBOutlet to connect the label and viewController (Ctrl and drag), The default code generated by Xcode is, for example, #IBOutlet weak var displayColumn: UILabel!
I can delete the exclamation mark and set it as the UILable only. But there must be a good reason the Xcode want it to be a forced unwrapped optional type.
Question: I don't think I understand the reason to use the optional type here. Because I already created a label in my view and connected it to my controller, it is firmly out there, it exists, I created it. Why Xcode want an option as the default type? If it says the text inside the label is an option, that sounds reasonable to me, because the label may be empty at some point in run time.
Many Thanks
You have created a label in Xcode and indicated to Xcode that you want that label connected to that #IBOutlet. But that connection doesn't happen until later.
This is the order of events at runtime.
The viewController is created (instantiated).
The Storyboard or .xib is unarchived. The label is created at this time.
The #IBOutlets and #IBActions are connected.
In Swift, when a class is instantiated, all properties must be initialized. In step 1, the outlets are nil because they haven't been connected yet. In order to allow for this, they must be declared as Optionals.
It is perfectly valid to use a normal Optional, such as:
#IBOutlet weak var displayColumn: UILabel?
but then you'd have to deal with unwrapping it each time you access it. The only time an #IBOutlet will be nil is when step 3 hasn't happened yet (for example, in prepareForSegue) or if you forgot to connect the outlet or renamed it. In those cases, the crash caused by accessing nil alerts you to the problem.

Swift making IBOutlet as strong

IBOutlets are weak by default in Swift. I have an object in the viewController Created in the storyboard which is not in the view hierarchy , So I need it to be a strong reference in ViewController , How can I Change #IBoutlet property to strong.
You can make an IBOutlet strong by either selecting strong when connecting the outlet:
Or just remove the weak keyword from the declaration:
#IBOutlet var label: UILabel!
Change the name of the outlet, it might be a reserved name so you are trying to override it.
As of Xcode 6 beta 2, Swift does not have a way to designate strong outlets. The workaround is to connect the outlet in IB, then remove the #IBOutlet attribute from your source file.
Update: This has been added in Xcode 6 beta 3.
Its now an option when creating the outlet from a drop down.
Here is why we might want to start making them strong from WWDC 2015 Session 407
http://asciiwwdc.com/2015/sessions/407
And the last option I want to point out is the storage type, which can
either be strong or weak.
In general you should make your outlet strong, especially if you are
connecting an outlet to a sub view or to a constraint that's not
always going to be retained by the view hierarchy.
The only time you really need to make an outlet weak is if you have a
custom view that references something back up the view hierarchy and
in general that's not recommended.
So I'm going to choose strong and I will click connect which will
generate my outlet.
The strong keyword is gone again and raises a syntax error in Xcode 6.1.1. It appears like outlets are now strong by default, which previously was the opposite. So simply define the outlet w/o additional declaration.
#IBOutlet var nameOfOutlet: type = Whatever();
Another reason for that error is because a stupid bug. For example when I initialize a UIImageView object called "imageView" in the view controller an error occurred called "cannot override strong property with weak property".
But when I change the object name for example "pictureView", the error is gone.
Best regards...
As of Xcode 6 beta 3, Swift now allows marking #IBOutlets as strong.
From the release notes:
• #IBOutlets may be explicitly marked strong to override their
implicitly-weak behavior. (16954464)

Resources