Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Protocol conformance with implicitly unwrapped optionals

I am trying to make a Swift protocol that I can use on UILabel, UITextField, and UITextView that incorporate their text, attributedText, and font properties.

However, unfortunately these three classes are inconsistent with whether they use optional types for these properties or implicitly unwrapped optionals.

For example, if I create this protocol:

protocol MyProtocol: class {
    var font: UIFont? { get set }
}

And I apply it:

extension UILabel: MyProtocol { }
extension UITextField: MyProtocol { }
extension UITextView: MyProtocol { }

It works fine for UITextField and UITextView but UILabel's font property is UIFont! and so the compiler says UILabel doesn't conform to MyProtocol.

Additionally text and attributedText are optional (String?) for UILabel and UITextField but implicitly unwrapped for UITextView (String!). So it's not even consistent which ones use optionals and which ones use implicitly unwrapped optionals for all three properties.

So then I've had to rename font in the protocol to eg. uiFont as essentially an alias for font with the following implementation in each of the extensions above:

extension UILabel: MyProtocol {
    var uiFont: UIFont? {
        get { font }
        set { font = newValue }
    }
} 
// … and similarly for UITextField and UITextView

This is a bit annoying as it takes away from the simplicity of the protocol.

I found this post on the Swift forum that seems to be the same issue and the discussion seems to say this is not how it's supposed to behave in Swift 4.2, but I am using Swift 5 and still getting this. There was even a proposal to abolish IUOs that got merged.

Note I am using Xcode 11.7 with iOS 13.7 on macOS Catalina 10.15.6 (19G2021).

Is there some way to avoid this problem altogether, or perhaps to make the code a bit cleaner so I don't need to have as much redundancy?

Thanks

like image 591
shim Avatar asked Sep 03 '20 23:09

shim


People also ask

What are implicitly unwrapped optionals?

Checking an optionals value is called “unwrapping”, because we're looking inside the optional box to see what it contains. Implicitly unwrapping that optional means that it's still optional and might be nil, but Swift eliminates the need for unwrapping.

Why are outlets defined as implicitly unwrapped optionals?

It's only after the view controller is initialized that it loads its view. This also means that any outlets declared in the view controller class don't have a value immediately after the view controller's initialization. That's why an outlet is always declared as an (implicitly unwrapped) optional.

What is forced unwrapping in Swift?

In these cases, Swift lets you force unwrap the optional: convert it from an optional type to a non-optional type. That makes num an optional Int because you might have tried to convert a string like “Fish” rather than “5”.


1 Answers

Although it seems like a bug in the Swift, you can extend the protocol itself to make it work:

extension UILabel: MyProtocol { }
extension MyProtocol where Self: UILabel {
    var font: UIFont? {
        get { self.font ?? nil }
        set { self.font = newValue }
    }
}
like image 69
Mojtaba Hosseini Avatar answered Nov 15 '22 22:11

Mojtaba Hosseini