Why does the author use forced unwrapping in Example 2 but not in example one? - swift

I know the concept of optionals and forced unwrapping but just to quote an example from iOS 8 Swift Programming Cookbook, I don't understand why var imageView: UIImageView is used
in example 1 but forced unwrapping var imageView: UIImageView! in example 2. Hopefully, someone tells me what I am missing here so I know what to read up on.
Example 1:
class ViewController: UIViewController {
let image = UIImage(named: "Safari")
var imageView: UIImageView
required init(coder aDecoder: NSCoder) {
imageView = UIImageView(image: image)
super.init(coder: aDecoder)
}
override func viewDidLoad() {
super.viewDidLoad()
imageView.center = view.center
view.addSubview(imageView)
}
}
Example 2:
import UIKit
class ViewController: UIViewController {
let image = UIImage(named: "Safari")
var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
imageView = UIImageView(frame: view.bounds)
imageView.image = image
imageView.center = view.center
view.addSubview(imageView)
}
}

As others have noted, the purpose of these two examples is to illustrate the difference between a non-optional variable that is set during the initialization process and an optional (in this case, an implicitly unwrapped one) which you may instantiate at a later point.
It's worth noting that while this example has pedagogic value, from a practical perspective, when dealing with view controllers, you would use the latter pattern with optional variable set in viewDidLoad (or for programmatically created views, you might use a third pattern, the loadView method), not the first pattern with the non-optional instantiated in init.
I just wanted to make sure you didn't walk away from this example concluding that in the case of view controllers that you should freely use either of these two patterns. There are reasons, unrelated to the Swift language but rather related to details of the life cycle of view controllers, views, etc., that you would favor the optional variable set in viewDidload when dealing with view controllers.
That notwithstanding, it is important to understand in what cases one would use an optional in Swift and in what cases one would not (the key point being that you must use optional if the variable may not be set during the initialization process of the object), so from that perspective, this may be an illuminating example. But just remember, that in the specific case of view controllers, one would generally adopt the viewDidLoad pattern.

In example 1, the variable value is set in init. In example 2, the variable is initialized later and after initialization (until viewDidLoad is called) its value is nil.
Since in example 1 the variable is never nil, it can be a non-optional. However, in example 2 the variable is nil for a long time, so it must be an optional variable.

In swift all non optional class and struct properties must be initialized in a constructor, and there's no way to avoid that (unless it's initialized inline, along with its declaration).
In some cases a non optional property cannot be initialized at instantiation time, because it depends from other properties of the same class, or just it must be initialized at a later stage in the instance lifetime. As such, these properties must be declared as optional.
A workaround consists of declaring all there properties as implicitly unwrapped - internally they are optionals, but they are accessed as non optionals (i.e. without the ? operator).
That explains the differences between example 1 and 2: in the former, the property is initialized in the initializer, so it is declared as non optional. In the latter case instead it is initialized in the viewDidLoad method, and so it's been declared as implicitly unwrapped.
This pattern is widely used in view and view controller outlets - you will notice that when you create an outlet in IB, the corresponding property is an implicitly unwrapped - that's because the variable is assigned after initialization.

Example 1's only initializer is the required init(coder aDecoder: NSCoder), which always assignes a value to imageView. Therefore imageView will always have a value after initilization has taken place.
Example 2 doesn't have or need an initilizer, since both stored properties are given an initial value in their declaration:
let image = UIImage(named: "Safari") // has an initial value of whatever UIImage(named: "Safari") returns
var imageView: UIImageView! // has an initial value of nil
You can therefore construct a ViewController, by calling the initializer which swift adds automatically: ViewController()
This will result in imageView having a value of nil.

Related

Modify IBOutlet property from outside of viewDidLoad - Xcode Storyboards

I have a separate class which when called upon updates the ToolTip (a text property) for an NSButton in a pistonViewController via its IBOutlet.
However, whenever I try to perform the action, I get the error
"Unexpectedly found nil while implicitly unwrapping an Optional value"
since pistonViewController.piston.tooltip didn't work, I created an instance above the class:
let pistonView = pistonViewController();
and then from within the separate class called pistonView.set_piston();
func set_piston(index: Int) {
piston1.toolTip = "yay it worked!";
}
I get the same error: found nil.
How to get the correct instance of the pistonViewController (the one that appears on viewDidLoad) so that piston1 will not be nil?
There is this solution, but it looks needlessly complex. This one appears to only work on iOS, using a storyboard.InstantiateViewController command that does not work on MacOS. This MacOS solution is poorly explained and does not appear to work.
"[How do I] Modify IBOutlet property from outside of viewDidLoad"
(But what you're really asking is how you modify a view controller's views from outside of the view controller.)
The short answer is "Don't do that." It violates the principle of encapsulation. You should treat a view controller's view properties as private, and only modify them inside the view controller's code.
(To misquote Groucho Marx: "Doc, it crashes when I do this". "Then don't do that!")
Instead, add a public property (pistonToolTip) in your PistonViewController (Class names should begin with upper-case letters).
class PistonViewController: UIViewController {
var pistonToolTip: String {
didSet {
piston?.tooltip = pistonToolTip
}
}
}
And in case you set pistonToolTip before your PistonViewController has loaded its views, add this line to viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
piston?.tooltip = pistonToolTip
// The rest of your viewDidLoad code
}
Ultimately I just set it up in viewDidLoad, with a timer waiting for the other program to get the variables that will then be assigned to the pistons.
The lack of effective pointers to instances of View Controllers makes anything else not possible or perhaps just arcane and difficult.

Implicit Unwrapping and Initializers

I have a subclass of UIView, which instantiates several sublayers. For convenience, I want a direct reference to one in particular, and because it has the same lifetime as the view, it seems reasonable to make it a let constant. The problem is that I want the sublayer to have a similarly immutable reference back to its owner/parent view, so I try to pass this to the layer’s initializer:
class MyView: UIView{
let myLayer: MyLayer!
required init?(coder aDecoder: NSCoder){
super.init(coder: aDecoder)
self.myLayer = MyLayer(owner: self) // <- The problem line.
self.layer.addSublayer(self.myLayer)
}
}
class MyLayer: CAShapeLayer {
unowned let owner: MyView
init(owner: MyView) {
self.owner = owner
super.init ()
}
}
If I put the call to the layer initializer before the call to MyView’s super.init, I get the error “'self' used before 'super.init' call.” I take this to mean it’s permissible (preferred I always thought?) to use self in a property initialization, but it doesn’t yet have a value that can be passed as a parameter?
But if I place the line after super.init as shown, I get two errors: “Property 'self.myLayer' not initialized at super.init call” and “Immutable value 'self.myLayer' may only be initialized once.” This puzzles me, as I thought one point of an implicitly unwrapped optional was that it had a valid nil value from its declaration, and that this did not count as the one and only assignment a let statement permits. (It also sounds a trifle contradictory: something wasn’t initialized, but after an attempted initialization, it wound up initialized twice, nevertheless?)
I know I can get around the problem by dropping my obsession with immutables, but is there a way to do this properly? I’m all in favor of Swift’s safety checking, but is there an actual danger here? I’d think the implicit unwrap would signal the compiler that the programmer is aware of a possible issue and is looking out for consequences.
I thought one point of an implicitly unwrapped optional was that it had a valid nil value from its declaration, and that this did not count as the one and only assignment a let statement permits.
Unfortunately, it counts. This behavior is fixed into current style after this update:
Xcode Release Notes > Xcode 6.3 > Swift Language Changes
You may have found some workarounds, but I think this would be very near to what you want:
class MyView: UIView {
private(set) var myLayer: MyLayer!
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.myLayer = MyLayer(owner: self)
self.layer.addSublayer(self.myLayer) }
}
You could use a lazy var for this. It’s basically a let, and only gets initialized once, at the first use site.
class MyView: UIView{
lazy var myLayer: MyLayer = {
return MyLayer(owner: self)
}()
required init?(coder aDecoder: NSCoder){
super.init(coder: aDecoder)
self.layer.addSublayer(self.myLayer)
}
}

#IBInspectable fatal error: unexpectedly found nil while unwrapping an Optional value

I got fatal error: unexpectedly found nil while unwrapping an Optional value when running my app it hangs for the following code
This is happening because the Inspectable is triggering your hasBottomLine property to be set, and therefore the didSet property observer to be called before your main view is loaded.
I'm going to guess that your lineHeightConstraint is probably defined as an implicitly unwrapped optional, something like:
#IBOutlet weak var lineHeightConstraint: NSLayoutConstraint!
You'll have to unwrap the optional before using it, for example:
lineHeightConstraint?.constant = hasBottomLine ? 3 : 0
There may be a chance that you still need to set this constraint after the view is loaded, so you might want to check if the optional contains nil, if so set a flag, and perform this line later in your viewDidLoad method. (or perhaps you can just call it regardless in your viewDidLoad)
Updated answer, with extra info that this code is in a subclassed UIView rather than UIViewController.
Rather than updating the constraint constant in your viewDidLoad method you could do so in the UIView's layoutSubviews method. For example:
override func layoutSubviews() {
super.layoutSubviews()
lineHeightConstraint?.constant = hasBottomLine ? 3 : 0
}

unwrapping optionals in swift

I am still trying to wrap my head around optionals in swift. Can someone explain why when we created the model object we put an '!' after and also when creating the quizCountries array of strings we also put the '!' and set it equal to nil. I don't really understand why we would want something to ever be nil in the program.
// QuizViewController.swift
import UIKit
class QuizViewController: UIViewController, ModelDelegate {
#IBOutlet weak var flagImageView: UIImageView!
#IBOutlet weak var questionNumberLabel: UILabel!
#IBOutlet weak var answerLabel: UILabel!
#IBOutlet var segmentedControls: [UISegmentedControl]!
private var model: Model! // reference to the model object
private let correctColor = UIColor(red: 0.0, green: 0.75, blue: 0.0, alpha: 1.0)
private let incorrectColor = UIColor.redColor()
private var quizCountries: [String]! = nil // countries in quiz
private var enabledCountries: [String]! = nil // countries for guesses
private var correctAnswer: String! = nil
private var correctGuesses = 0
private var totalGuesses = 0
Exclamation mark after the property name indicates that this is an implicitly unwrapped optional.
Implicitly unwrapped optionals are useful when an optional’s value is confirmed to exist immediately after the optional is first defined and can definitely be assumed to exist at every point thereafter. The primary use of implicitly unwrapped optionals in Swift is during class initialization, as described in Unowned References and Implicitly Unwrapped Optional Properties.
These are useful when referenced object, e.g. model in your case, is not yet known at the moment of initialisation of QuizViewController but still is guaranteed to be available and assigned to the model property before its first use. Same goes for quizCountries, we do not know these countries yet when the controller is created but will certainly know them before they are going to be used.
In case if implicitly unwrapped optional, contrary to expectation, is (still) nil at the moment of its use the application will crash.
If you create IBOutlets by dragging from elements in interface builder to the source file they default to being Implicitly Unwrapped Optionals (IUOs) with !'s. You can manually change the them to be proper optionals with ? so that you can handle them carefully and let the compiler check all the cases.
You might be interested in the cases when they could be nil. These are a few that I have thought of although I may have missed some.
Before the view loads (before viewDidLoad has been called) they will be nil.
If you ever explicitly set them to nil.
If the item has been removed from Interface Builder.
If the outlet has been removed from Interface Builder.
If you have multiple views/view controllers in Interface Builder which use the same class and one has the outlets connected and another doesn't.
If you have two Storyboards (e.g. one for iPhone and one for iPad) that both reference the same class but only one has the IBOutlet connected.
If you ever unload the view the outlets are likely to become nil again.
Given all these possibilities I prefer to use proper optionals. I blogged about how I deal with optionals and also gave a talk on the topic.

Swift: UIImageView - Unexpectedly found nil while unwrapping an Optional value

class DisplayImageVC: BasePageView { //BasePageView inherits from UIViewController
#IBOutlet weak var displayImage: UIImageView!
override func viewDidLoad() {
self.displayImage.backgroundColor = UIColor.yellowColor()
}
....
in another class I try this and get a fatal error:
var displayImageView = self._pageContent[1] as DisplayImageVC
displayImageView.displayImage.image = UIImage(named: ("displayChecklane"))
Log:
fatal error: unexpectedly found nil while unwrapping an Optional value
Images.xcassets
var displayImageView = self._pageContent[1] as DisplayImageVC
displayImageView.displayImage.image = UIImage(named: ("displayChecklane"))
You are crashing on the second line. You know this is because something is nil, but you do not know what is nil here. You are assuming it is the image. That is a false assumption; if it were correct, there would be no crash, as you are not force-unwrapping it, and it is legal to assign nil to an image view's image.
Instead, consider this: maybe displayImageView.displayImage is nil. That is, what is nil is the UIImageView you are trying to assign to. This would make sense since this is an outlet. If the DisplayImageVC's view has not loaded, its outlets have not been filled - they are still nil.
How you solve this depends on what you want to do. Personally I think your approach is bogus from the start; you have no business setting another view controller's outlet or a property of its outlet, or any aspect of its interface. You should have an image property in DisplayImageVC, and here, you should be setting that property; that way, when the view controller shows its view, it can configure its own interface.