Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to properly implement lazy instantiation with an @IBOutlet property in Swift

I am Learning iOS Development with Big Nerd Ranch's latest iOS book. I have chosen to implement their apps in Swift. In one of their apps, they have the following code in Objective C:

- (UIView *)headerView
{
    // If you have not loaded the header view yet...
    if (!_headerView) {

        // Load HeaderView.xib
        [[NSBundle mainBundle] loadNibNamed:@"HeaderView" owner:self options:nil]
    }

    return _headerView;
}

Apple's Swift guide on "@IBOutlet":

When you declare an outlet in Swift, the compiler automatically converts the type to a weak implicitly unwrapped optional and assigns it an initial value of nil. In effect, the compiler replaces @IBOutlet var name: Type with @IBOutlet weak var name: Type! = nil.

As it was pointed out in Lazy loading Properties in swift, there are a couple of different options. None of them in that post explicitly mention lazy initialization with @IBOutlet, so I've done by best to implement their suggestions, and would like to know what would be considered best practices.

Attempt #1(failed): following a similar pattern, as the example from AppDelegate.swift. This brings the issue "'IBOutlet' attribute requires property to be mutable"

@IBOutlet var headerView : UIView  {
    // If the HeaderView has not been loaded yet...
    if !_headerView {
        // Load HeaderView.xib
        NSBundle.mainBundle().loadNibNamed("HeaderView", owner: self, options: nil)
    }
        return _headerView!
}
var _headerView : UIView? = nil

Attempt #2(failed): using any variation of "@lazy" with "@IBOutlet" didn't worked because "@lazy" needs an initializer, but if a closure is used, then "@IBOutlet" has the same issue as from Attempt #1

Attempt #3(successful?): this is the only way I was able to get this to work. I got the idea from a somewhat different question, Lazy property initialization in Swift. My understanding of what is happening is headerView is actually declared as "@IBOutlet weak var headerView : UIView! = nil", will only be initialized once with the TableViewController subclass I have, and that initialization will be "lazy" in that it only occurs when the TableViewController needs to be loaded.

@IBOutlet var headerView : UIView

func loadHeaderView() {
    // If the HeaderView has not been loaded yet...
    if !headerView {
        // Load HeaderView.xib
        println("loaded HeaderView")
        NSBundle.mainBundle().loadNibNamed("HeaderView", owner: self, options: nil)
    }
}

override func viewDidLoad() {
    super.viewDidLoad()
    loadHeaderView()
    tableView.tableHeaderView = headerView
}

So, how can this be improved?

Is viewDidLoad() the correct function to use?

Thanks

like image 778
SchoonSauce Avatar asked Dec 01 '22 16:12

SchoonSauce


1 Answers

A lazily loaded outlet makes no sense- if it's an outlet, it's populated when loading the nib, not from code. If you're loading it from code, it doesn't need to be an outlet, so you can use @lazy.

like image 144
jrturton Avatar answered Dec 06 '22 23:12

jrturton