Swift protocol with where Self clause
The ability to put superclass constraints on protocols declarations (that is, being able to define protocol P where Self : C
where C
is the type of a class) was a premature consequence of
SE-0156, and the syntax should have been rejected in Swift 4.x until the feature was implemented. Attempting to use this feature in Swift 4.x can cause miscompilation and crashes, so I would avoid using it until Swift 5.
In Swift 5 (Xcode 10.2) the feature has now been implemented. From the release notes:
Protocols can now constrain their conforming types to those that
subclass a given class. Two equivalent forms are supported:protocol MyView: UIView { /*...*/ }
protocol MyView where Self: UIView { /*...*/ }
Swift 4.2 accepted the second form, but it wasn’t fully implemented
and could sometimes crash at compile time or runtime. (SR-5581)
(38077232)
This syntax places a superclass constraint on MyView
which restricts conforming types to those inheriting from (or being) UIView
. In addition, the usage of MyView
is semantically equivalent to a class existential (e.g UIView & MyView
) in that you can access both members of the class and requirements of the protocol on the value.
For example, expanding upon the release notes' example:
protocol MyView : UIView {
var foo: Int { get }
}
class C : MyView {} // error: 'P' requires that 'C' inherit from 'UIView'
class CustomView : UIView, MyView {
var foo: Int = 0
}
// ...
let myView: MyView = CustomView(frame: .zero)
// We can access both `UIView` members on a `MyView` value
print(myView.backgroundColor as Any)
// ... and `MyView` members as usual.
print(myView.foo)
Swift protocol extension with specific Self type
Is there a way to specify a default value for the parameter for specific conforming structs when doing a protocol extension?
You have highlighted the problems with this approach in the question already.
How can I solve it in a different way?
UnsignedInteger
inherits fromBinaryInteger
that can provide youbitWidth
information (UInt8
=>8
,UInt16
=>16
and so on).
extension UnsignedInteger {
func hex(uppercase: Bool = true) -> String {
let fieldWidth = self.bitWidth / 4
return String(format: "%0\(fieldWidth)\(uppercase ? "X" : "x")", self as! CVarArg)
}
}
Above makes it work for UInt
, UInt8
, UInt16
, UInt32
& UInt64
.
Taking it one step further, you can do this with FixedWidthInteger
& now it will work for all signed and unsigned integers.
Swift overriding protocol extension keeping the extension behaviour
There is a simple solution: Just add a defaultShow
method to the extension.
extension ShowAlert where Self: UIViewController {
func defaultShow(){
let alert = UIAlertController(title: self.titleForAlert, message: self.messageForAlert(), preferredStyle: .Alert)
alert.addAction(UIAlertAction(title: "Ok", style: .Cancel, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
func show() {
defaultShow()
}
}
So in your code you can just call defaultShow
:
extension ViewController: ShowAlert {
// ...
func show() {
self.defaultShow()
print("Good day sir!")
}
}
There is also another solution where you can call .show()
instead of .defaultShow()
. However it uses casting and breaks encapsulation. If you want to see it let me know.
Forced to cast, even if protocol requires given type
Adopting the protocol Fooable
tells the compiler that this particular UIViewController
responds to foo()
, no less no more.
In reverse conclusion Fooable
does not become UIViewController
necessarily.
The constraint Self: UIViewController
is just another information for the compiler to complain at compile time if the affected class is not UIViewController
In your case when annotating SampleViewController
to Fooable
the compiler knows only that SampleViewController
responds to foo()
. It does not know that the type is actually a subclass of UIViewController
.
So do not annotate a concrete class to a protocol if you want to access properties of the concrete class.
However you could add the show
method and other common properties / methods to the protocol
protocol Fooable: class where Self: UIViewController {
func foo()
func show(_ vc: Fooable, sender: Any?)
}
then you can use Fooable
because the compiler knows that the type adopting the protocol responds to the method.
A suitable practice to annotate a type to a protocol is for example when you are going to create a heterogenous but restricted collection type
let array : [CustomStringConvertible] = ["Foo", 1, false]
array.forEach{ print("\($0)")}
The code prints the three items using the description
property which all items respond to. The compiler recognizes the three items as types which have a description
property, not as String
, Int
and Bool
.
Update:
In Swift 5 support of superclass constrained protocols is implemented.
Related Topics
Get the Type of Anyobject Dynamically in Swift
How to Set the iOS13 Uisegmentedcontrol Backgroundcolor to White
How to Use Case Enum Comparison as a Boolean Expression
Ios: Ambiguous Use of Init(Cgimage)
Saving a Codable Struct to Userdefaults with Swift
Swiftui View and Uihostingcontroller in Uiscrollview Breaks Scrolling
Swift Build Time Too Long When the Configuration Is 'Release'
Write into Settings Bundle in Swift
Dynamically Call a Function in Swift
Changing Tab Bar Color (Swift)
Background Upload with Share Extension
Toolbar Is Deleting My Back Button in the Navigationview
Is .Playground a Swift File? Who Can 'See' It
Swift Error with Generic Array
Xcode11 Error "Open(_:Options:Completionhandler:) Is Unavailable in Application Extensions"