How to initialize a variable when IBOutlet is initialized?

Viewed 927

It happened for me to write following code snippet in one of my UIViewControllers in my new iOS Swift Application.

var starButtonsCount = starButtons.count
@IBOutlet var starButtons: [UIButton]!

Then straight next to the starButtonsCount variable declaration following error appeared in red.

Error: Cannot use instance member ‘starButtons’ within property initializer; property initializers run before ‘self’ is available.

So I found out that by declaring the starButtonCount variable as lazy we can resolve this error (temporary in the long run of the iOS App development process).

I'm curious to find out what are the other methods to solve this?

Is there a way to trigger the initialization for starButtonCount when the starButtons IBOutlets get initialized?

4 Answers

Another way

var starButtonsCount:Int! 
@IBOutlet var starButtons: [UIButton]! { 
    didSet { 
         starButtonsCount = starButtons.count
    }
}

awakeFromNib is a method called when every object in a .xib have been deserialized and the graph of all outlets connected. Documentation says:

Declaration

func awakeFromNib()

Discussion

message to each object recreated from a nib archive, but only after all the objects in the archive have been loaded and initialized. When an object receives an awakeFromNib message, it is guaranteed to have all its outlet and action connections already established.The nib-loading infrastructure sends an awakeFromNib

You must call the super implementation of awakeFromNib to give parent classes the opportunity to perform any additional initialization they require. Although the default implementation of this method does nothing, many UIKit classes provide non-empty implementations. You may call the super implementation at any point during your own awakeFromNib method.

As in:

class MyUIViewController : UIViewController {
    var starButtonsCount = 0
    @IBOutlet var starButtons: [UIButton]!
    override func awakeFromNib() {
        super.awakeFromNib()
        starButtonsCount = startButtons.count
    }
}

Also note there is no need for starButtonsCount to be a stored variable:

var starButtonsCount: Int {
   return starButtons.count
}

In this case it would be probably better to use starButtons.count directly since the variable does not improve the code in no way.

In a general case, there is no need to store derived state unless storing it provides some performance boost (e.g. caching calculations).

Simple solution is a constant

let starButtonsCount = 8 // or whatever the number of buttons is

IBOutlets are connected at build time so the number of buttons won't change at runtime.

You could add an assert line in viewDidLoad to get informed if the number of buttons changed in a future version of the app

assert(starButtons.count == starButtonsCount, "number of buttons has changed, adjust starButtonsCount") 
Related