0

We all know that as soon we drop an outlet inside a View or its ViewController, it is being marked as unwrapped and we all know that Swift wants to initialise all the properties within the initialisation phase and this is the sentence we give to whoever asks us for the first time why an outlet is always in company with the exclamation mark.

Today I was trying to understand why an object that comes from a XIB, cannot be initialised within the initWithCoder: method.

As far as I know, a XIB file just contains all the information about the objects drawn internally at the XIB using an XML file structure. So what we see inside the XIB file is gonna be archived and stored into a file.

When we will call the UINib loadNibNamed:owner:options: class method, it will unarchive the object created previously, look up for all the properties, set them and send the message awakeFromNib to the object...

but due to that exclamation mark that says "During the init phase I'm not able to init you" what I've said above should be incorrect.. But why? Can someone tell me why Nib cannot be init and should be marked as optional?

Here I have some docs from Apple that didn't help me https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/LoadingResources/CocoaNibs/CocoaNibs.html

ndPPPhz
  • 315
  • 1
  • 15

1 Answers1

3

Your @IBOutlet properties are introduced by your UIViewController subclass.

Saying that "Swift wants to initialise all the properties within the initialisation phase" is oversimplifying a bit.

The Swift initialisation rules state that all properties introduced by a subclass must be initialised before calling the superclass initialiser, and the Swift compiler must be able to "see" this initialisation; there must be an explicit assignment. This is "Safety Check 1":-

Safety check 1

A designated initializer must ensure that all of the properties introduced by its class are initialized before it delegates up to a superclass initializer.

In almost all cases where you are using a XIB or storyboard scene you don't override init(coder:), so the compiler can determine that you haven't explicitly assigned values to these properties.

If you did override the initialisers and assign values (or even if you simply assigned default values when you declared the properties) then you could make them normal properties rather than implicitly unwrapped optionals, but that would be a bit pointless since you would almost immediately overwrite those values when the XIB is loaded.

An implicitly unwrapped optional doesn't say "During the init phase I'm not able to init you"; It is more like "I know it looks like this isn't being initialised, but at runtime it will be. Trust me" (Strictly speaking it is just declaring an optional, which is allowed to be nil, so the compiler doesn't complain that it isn't initialised, but implicitly force-unwraps the property whenever it is referenced - hence the name, "implicitly unwrapped optional").

This works for @IBOutlets because the loading process uses Key Value Coding to assign values at runtime.

This is the reason why, if you remove an @IBOutlet but forget to update the XIB/Storyboard you get a runtime exception stating that your class is "not key/value compliant for xxx".

The use of implicitly unwrapped optionals in this way is generally considered acceptable as you will pretty quickly find out if you have a connection problem during your testing (because your app will crash with an "unexpectedly nil") and it saves a lot of conditional unwrapping.

Community
  • 1
  • 1
Paulw11
  • 108,386
  • 14
  • 159
  • 186
  • First of all, thank you for your answer. Yes I know that implicitly optional doesn't mean "During the init phase I'm not able to init you" but it was just to summarise and also the KVC-Error handling when you are trying to set a value for a non-existing key. BTW, I think that the first part of your answer got the point of my question. In most of the case, we don't override `initWithCoder:` so the subclass where we are introducing some outlets, isn't assigning values to these properties. – ndPPPhz Sep 06 '18 at 21:04
  • That's correct. It isn't good enough that a value is assigned somewhere during the initialisation process; Swift needs to see a value assigned in the subclass. – Paulw11 Sep 06 '18 at 21:06
  • My thought now is why Apple never automated this process. I mean: why does not Apple automatically add the override init and a line into it with the decoding instruction to avoid using optional? So we can easily set up all the objects during the init – ndPPPhz Sep 06 '18 at 21:10
  • Firstly because Swift has been grafted on top of the Objective-C based UIKit. The initialisation safety checks don't exist in Obj-C, but you can get effectively the same bugs (ie not KV-compliant crash), or arguably worse ones; not assigning to an Obj-C outlet just results in nothing happening (because you can send a message to `nil`) rather than the explicit crash you get in Swift. And secondly because it would be arguably much worse; you would have many lines of boilerplate in each VC and you would need to add a new assignment line each time you added an outlet – Paulw11 Sep 06 '18 at 21:19