shreddish shreddish - 1 year ago 130
Swift Question

Swift error unwrapping nil from Nib in viewDidLoad

Getting some weird results, I am loading a nib file that I created as a custom alert view. However it is failing in the viewDidLoad method when it tries to change the text of the UILabel that is in the custom alert view. (gets unwrapping nil error)

When I go to the nib file all of my outlets are connected (shows the highlighted dots next to them). The file's owner is also set to the correct class name.

However when the code fails and pulls up the debugger all of the outlets are empty. This code is occurring in viewDidAppear so shouldn't the outlets and all of the UI be loaded at that point?

Side information:
The other weird part, is that I am testing this on two different devices. One is running iOS 8.4 and the other is running iOS 9.1(beta). The iOS 9.1 device functions normally and does not ever throw this nil error. The iOS 8.4 device is the one that throws the error. Running Xcode 6.4 swift 1.2. In order to get the code to run on my iOS 9.1 I open Xcode 7 with my device connected to the computer then close out and reopen Xcode 6.4 and it allows me to set my target iOS to 9.1.


Image of where code is failing
enter image description here

Nib file view controller:

@IBOutlet var messageLabel: UILabel!
@IBOutlet var notificationHeightConstraint: NSLayoutConstraint!
@IBOutlet var notificationWidthConstraint: NSLayoutConstraint!
@IBOutlet var singleButton: UIButton!
@IBOutlet var rightButton: UIButton!
@IBOutlet var leftButton: UIButton!
@IBOutlet var middleButtonSpacerView: UIView!

var buttonDelegate: NotificationButtonDelegate?
var grayView: UIView!
var buttonTitles = [String]()
var notificationWidth: CGFloat!
var notificationHeight: CGFloat = 200.0
var hideProgressView = false
var hideNotificationView = false
var currentProgress: CGFloat = 0.0
var messageLabelText = ""

required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)

override init(nibName nibNameOrNil: String!, bundle nibBundleOrNil: NSBundle!) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)

override func viewDidLoad() {
override func viewDidAppear(animated: Bool) {
messageLabel.text = messageLabelText

if buttonTitles.count > 0 {
if buttonTitles.count == 1 {
rightButton.hidden = true
leftButton.hidden = true
middleButtonSpacerView.hidden = true
singleButton.setTitle("\(buttonTitles[0])", forState: UIControlState.Normal)
} else {
leftButton.setTitle("\(buttonTitles[0])", forState: UIControlState.Normal)
rightButton.setTitle("\(buttonTitles[1])", forState: UIControlState.Normal)

if self.hideProgressView {
self.progressView.hidden = true
if messageLabel.bounds.height >= 90 {
notificationHeightConstraint.constant = 225
} else {
notificationHeightConstraint.constant = 200.0

if self.hideNotificationView {
self.notificationView.hidden = true
notificationHeightConstraint.constant = 160

self.notificationWidthConstraint.constant = notificationWidth

// Do any additional setup after loading the view.


Custom Alert View class initialization in main view controller

var notificationPopup: NotificationPopupViewController!
var progressPopup: NotificationPopupViewController!

override func viewDidLoad() {
self.hasError = false
notificationPopup = NotificationPopupViewController()
progressPopup = NotificationPopupViewController()

Call in main viewController to present the custom alert view

@IBAction func configureSystemButtonTapped(sender: AnyObject) {

var prefs: NSUserDefaults = NSUserDefaults.standardUserDefaults()
var hostedSSID = prefs.valueForKey("HostedSSID") as? String
var currentSSID = sureVue.getUserSSID()

if currentSSID != hostedSSID || currentSSID == "" {
self.notificationPopup.showInView(self.view, withMessage: "You are not connected to the device's wifi.\nFollow instructions on launch page to connect.", animated: false, buttonLabels: ["Ok"])

method in nib viewController that sets up the custom alert view

func showInView(aView: UIView!, withMessage message: String!, animated: Bool, buttonLabels:[String]) {
self.hideNotificationView = false
self.hideProgressView = true
self.buttonTitles = buttonLabels
notificationWidth = aView.bounds.width * 0.925
grayView = UIView(frame: aView.frame)
grayView.backgroundColor = UIColor(red: 170/255.0, green: 170/255.0, blue: 170/255.0, alpha: 0.75) = CGPoint(x: aView.bounds.width/2, y: aView.bounds.height/2)
grayView.addSubview(self.view) = CGPoint(x: aView.bounds.width/2, y: aView.bounds.height/2)
messageLabelText = message

if animated

Answer Source

As per comments, I think the problem is in the way self.notificationPopup is initialised. You need to use init(nibName: ...), rather than notificationPopup = NotificationPopupViewController() in order to get the instance to load from the nib and set its outlets.

The existing code works OK in iOS 9 because Apple have changed the default nibNames that are used if you do not specify one when initialising a UIViewController. See the notes under UIKit in the iOS9 release notes.