override var navigationItem: UINavigationItem fail in iOS 14

The following code fails since iOS14.x Is this a bug? No depreciation statements found in the SDK. Is there a workaround?

import UIKit

class ViewController: UIViewController {
    
    private lazy var barButtonItem: UIBarButtonItem = {
        UIBarButtonItem(image: .add, style: .plain, target: self, action: #selector(doSomething))
    }()

    override var navigationItem: UINavigationItem {
        let item = super.navigationItem
        item.rightBarButtonItem = barButtonItem // <- Thread 1: EXC_BAD_ACCESS (code=2, address=...)
        item.title = "TestCase"
        return item
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
    }
    
    @objc private func doSomething() {
        print(">>doSomething")
    }
}

We are running a complex software since iOS4 and cannot simply migrate to SwiftUI.

https://stackoverflow.com/questions/64270033/override-var-navigationitem-uinavigationitem-fail-in-ios-14

Post not yet marked as solved Up vote post of pkiman Down vote post of pkiman
1.5k views
  • Apple' software depreciation is quite annoying. Apple banned pre-iOS14 apps on the App Store. <string>SDK Version Issue. This app was built with the iOS 13.2 SDK. All iOS apps submitted to the App Store must be built with the iOS 14 SDK or later, included in Xcode 12 or later.</string>

Add a Comment

Replies

Setting a property in UINavigationItem within the navigationItem getter is not supported. This code is effectively running into an infinite loop that is constantly re-querying the UINavigationItem whenever you set rightBarButtonItem.

The recommended way to do this is to set your properties in the UIViewController's initializer (or in the viewDidLoad() method) so that it only happens once, rather than to be set every time it is fetched.

import UIKit

class ViewController: UIViewController {
    private lazy var barButtonItem: UIBarButtonItem = {
        UIBarButtonItem(image: .add, style: .plain, target: self, action: #selector(doSomething))
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        navigationItem.rightBarButtonItem = barButtonItem
        navigationItem.title = "TestCase"
    }
    
    @objc private func doSomething() {
        print(">>doSomething")
    }
}

@andyl_ From Apple's own documentation on UIViewController.navigationItem property (https://developer.apple.com/documentation/uikit/uiviewcontroller/1621851-navigationitem):

  1. "... To ensure the navigation item is configured, you can either override this property and add code to create the bar button items when first accessed or create the items in your view controller's initialization code. ..."
  2. "... Avoid tying the creation of bar button items in your navigation item to the creation of your view controller's view.  ..."

So I really don't understand what is wrong with @pkiman's code above. Although I'm also encountering an infinite loop sometimes, but on other projects similar code works just fine. Can't get what is going on there to be honest...