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
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.
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.
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”.
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 }
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With