Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are IBOutlets optionals after swift 5 migration

Tags:

swift

swift5

After migrating the project to swift 5, I'm getting a lot of errors such as

Expression implicitly coerced from 'UIButton?' to 'Any'

I'm not sure what's causing this. One example where this is happening(there are a bunch) is when I'm setting the view.accessibilityElements. The array is supposed to contain : [Any]?... Any idea what's causing this?

Here is an example:

@IBOutlet weak var shareButton: UIButton!
@IBOutlet weak var shareTitleLabel: UILabel!

view.accessibilityElements = [shareButton, shareTitleLabel]

Here is another example:

@IBOutlet weak var titleLabel: UILabel!

let titleConstraints = [
        NSLayoutConstraint(item: titleLabel, attribute: .leading, relatedBy: .equal, toItem: otherView, attribute: .leading, multiplier: 1, constant: horizontalTextInset),
        NSLayoutConstraint(item: titleLabel, attribute: .trailing, relatedBy: .equal, toItem: otherView, attribute: .trailing, multiplier: 1, constant: -horizontalTextInset)
]

When setting the elements above like this, it causes the mentioned error

like image 642
Jay Avatar asked Mar 29 '19 17:03

Jay


1 Answers

A couple of observations:

  1. It’s actually not the migration, itself, that is causing the issue. The issue is simply that you’re now compiling it Swift 5, which now warns you about the ambiguous coercion.

    Since you didn’t share the precise code that produced this warning, consider this example that produces that warning:

    class ViewController: UIViewController {
    
        @IBOutlet var button: UIButton!
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            let foo: Any = button
            print(type(of: foo))
    
            // do something with `foo`
        }
    }
    

    So, glancing at this code, is foo the optional or is it the unwrapped value? In Swift 5, it brings this ambiguity to our attention with this warning

    warning: expression implicitly coerced from 'UIButton?' to 'Any'

    And it will show you three possible auto-fixes to eliminate this ambiguity, namely either:

    • use nil-coalescing operator, ??;
    • force unwrap it, !; or
    • just cast it with as Any to explicitly say that foo will be the optional with no unwrapping.
       

    Bottom line, we want to be able to easily reason about our code, and the Any type just makes this ambiguous. The compiler no longer makes assumptions as to whether you wanted the button to be unwrapped or not and is asking us to make our intentions explicit.

  2. For sake of comparison, consider the following two scenarios, where there is no ambiguity, and thus no warning. For example, considering the same implicitly unwrapped optional, here it knows that the implicit unwrapping should take place:

    let foo: UIButton = button
    

    Whereas here it knows that foo will be the optional:

    let foo: UIButton? = button
    
  3. If you’re wondering why your implicitly unwrapped UIButton! outlet is being treated as UIButton? at all (rather than as an ImplicitlyUnwrappedOptional type or just automatically force unwrapping it even though you’re using Any type), there are interesting discussions related to that in Reimplementation of Implicitly Unwrapped Optionals and the SE-0054 Abolish ImplicitlyUnwrappedOptional type.

like image 199
Rob Avatar answered Sep 20 '22 01:09

Rob