Justin Justin - 3 months ago 28
Swift Question

Swift 2.2 - Updating UILabel throws "unexpectedly found nil while unwrapping an optional value"

I am new to iOS development so forgive me if I'm missing something obvious. I have a view controller that contains a subview in which I've created a numpad, and for the time being I want to give the numpad view its own UIView subclass because I want to do a few different things with it. Right now the numpad is just creating a string from the keys that get pressed, and I've set up a delegate to pass that string anywhere else I want to use it (though I've also tried accessing the raw input directly in the view controller with let a = subview(); label.text = a.rawInput).

Whenever I try to set the text of the UILabel in the view controller to the subview's raw input, whether by delegation or directly, the UILabel is found to be nil and throws the error in the title.

Things I've tried:

  • Setting the text inside a viewDidLoad override, and outside of it

  • Setting a variable (testInput) inside the view controller to adopt the subview's raw input and setting the label text to that (I've confirmed that the variable inside the view controller gets properly set, so no delegation issues)

  • Using didSet on the testInput variable both to set label text to testInput and to try calling viewDidLoad and set the label text in there (printing testInput inside this didSet does print the right string, FWIW)

  • Deleting and relinking the IBOutlet for my label

  • Strong and weak storage for the IBOutlet variable

  • Trying to do the same thing in another subview within the view controller, in case for some reason it was the view controller's own fault

  • Searching everywhere for a solution that works

I'm stumped. Here is my relevant numpad code:

import UIKit

protocol NumpadDelegate {
func updateInput(input: String)

class Numpad: UIView {

// MARK: UI outlets
@IBOutlet weak var decButton: UIButton!

// MARK: Properties
var rawInput: String = ""
var visibleInput: String = ""
var calcInput: String = ""
var operandReady = 1
var percentWatcher = 0

var delegate: NumpadDelegate? = BudgetViewController()

// MARK: Functions
func handleRawInput(str: String) {
rawInput += str
print("numpad input is \(rawInput)")


And here is the view controller code:

import UIKit

class BudgetViewController: UIViewController, NumpadDelegate {

// MARK: Properties
//@IBOutlet weak var transactionValueField: UITextField!
@IBOutlet weak var remainingCashForIntervalLabel: UILabel!
@IBOutlet weak var intervalDenoterLabel: UILabel!
@IBOutlet weak var currencyDenoterLabel: UILabel!
@IBOutlet weak var mainDisplayView: TransactionType!
@IBOutlet weak var inactiveInputView: InactiveInput!
@IBOutlet weak var numpadView: Numpad!
@IBOutlet weak var rawInputLabel: UILabel!

var remainingCashForInterval = 40

let display = TransactionType()
var testInput = "" {
didSet {

override func viewDidLoad() {
// let numpad = Numpad()
// numpad.delegate = self
// print("\(numpad.delegate)")
self.rawInputLabel.text = testInput


func updateInput(input: String) {
print("view controller now has \(input)")
display.mainInput = input
testInput = input

As a side note, in case you noticed that my protocol isn't a class type, for some reason adding : class to it and declaring my delegate as a weak variable prevents the delegation from working. Any suggestions there?

Rob Rob

You assigned the delegate like so:

var delegate: NumpadDelegate? = BudgetViewController()

That doesn't reference the view controller whose scene was presented, but rather a new blank one. And that's why when you used weak, why it was deallocated (because that orphaned instance of the view controller has no strong references to it).

You should define the protocol to be a class protocol again, and define delegate to be:

weak var delegate: NumpadDelegate?

And then, in the view controller's viewDidLoad, uncomment the line that sets that delegate:

numpadView.delegate = self

But, do not uncomment the line that says numpad = Numpad(); that is incorrect as that creates yet another Numpad instance. But you do want to set the delegate of the existing Numpad, though.