swift protocol extension default implementation vs actual implementation in class
Having a default implementation for a required protocol function/property means that your conforming types won't have to implement that function/property, they can use the default implementation instead.
However, if a conforming type does implement the function/property, then the compiler will always call the more specific implementation, namely the one in your conforming class and not the default one.
So even if you stored an instance of MyClass2
in a variable of type MyProtocol
, you'd still get the MyClass2
implementation when accessing the property on the variable.
let myClass2: MyProtocol = MyClass2()
type(of: myClass2).name // "Specific name"
The behaviour is different for non-required properties/functions declared and defined in a protocol extension. If you declare a property/function in the protocol extension only, then even if you provide a different implementation for that in a conforming class, you won't be able to access that implementation from a variable whose type is the protocol type rather than the specific conforming type.
protocol MyProtocol {
static var name: String { get }
}
extension MyProtocol {
static var name: String {
return "unnamed"
}
// Optional protocol requirement
static var nonRequired: String {
return "nonRequired"
}
}
// does not specify its own name
class MyClass: MyProtocol { }
//specifies its own name!
class MyClass2: MyProtocol {
static var name: String {
return "Specific name"
}
// Specific implementation
static var nonRequired: String {
return "Specific"
}
}
let myClass = MyClass()
MyClass.name
let myClass2: MyProtocol = MyClass2()
type(of: myClass2).name // "Specific name"
type(of: myClass2).nonRequired // "nonRequired"
MyClass2.nonRequired // "Specific"
override protocol extension default implementation
If you declare the variable as the protocol type, it will always take the default implementation of the protocol method, since the method is declared in an extension of the protocol.
Without adding the method to the protocol declaration itself (which you've stated to be not possible for you), the only way to access the specific implementation of your conforming type is to downcast some
to B
or store it as B
in the first place.
let some: SomeProtocol = B()
some.someMethod() //this prints "protocol implementation"
(some as? B)?.someMethod() // this prints "class implementation"
let someB = B()
someB.someMethod() // this prints "class implementation"
Swift - Protocol default implementation in extension with generic superclass constraint
This is because Printer<A>
and Printer<B>
are different types, even A & B are Printable, so due to possible ambiguity compiler generate error.
You need the following (tested with Xcode 11.4)
extension Press {
func print<P>() where Self: Printer<P>, P: Printable {
// Do Something with self.printable.value
}
}
Extending a Protocol property to provide a default implementation in Swift
A possible solution is to locate the current value in the allCases
collection,
and the return the next element (or wrap around to the first element):
public protocol CycleValue: CaseIterable, Equatable {
var nextValue: Self { get }
}
public extension CycleValue {
var nextValue: Self {
var idx = Self.allCases.index(of: self)!
Self.allCases.formIndex(after: &idx)
return idx == Self.allCases.endIndex ? Self.allCases.first! : Self.allCases[idx]
}
}
(Note that both forced unwraps are safe!)
Example:
public enum AppThemeAttributes: CycleValue {
case classic, darkMode // etc.
}
let a = AppThemeAttributes.classic
print(a) // classic
let b = a.nextValue
print(b) // darkMode
let c = b.nextValue
print(c) // classic
The protocol must conform to Equatable
for this to compile, but that
is not a real restriction: A CaseIterable
protocol cannot have
associated values, so that the compiler can always synthesize theEquatable
conformance.
How the call the default implementation code in protocol in swift?
A protocol is more like a compile-time guarantee that type has certain methods and properties. Default implementation adds another layer of complexity to this by injecting an implementation to the protocol's adopters. I don't have the skills to go through Swift's source code but I think when the adopter provide its own implementation, the default implementation is overshadowed and there's no way to get it back.
A workaround is to add a method with a different name to your protocol, which provides the default implementation and can be called by any adopter:
protocol ImplementedProtocol {
func printInfo()
func defaultPrintInfo()
}
extension ImplementedProtocol where Self: BaseClass {
func printInfo() {
defaultPrintInfo()
}
func defaultPrintInfo() {
print("Hello! This is ImplementedProtocol")
}
}
class SuperClass: BaseClass, ImplementedProtocol {
func printInfo() {
self.defaultPrintInfo()
print("Hello! This is SuperClass")
}
}
How to add default implementation using generic protocol extension?
Maybe this would be simpler.
extension Comparable {
func limit(from minValue: Self, to maxValue: Self) -> Self {
return Swift.max(Swift.min(self, maxValue), minValue)
}
}
Swift protocol default values are not changable
var type1: AssetViewAttribures = Crypto(name: "name", logo: URL(string: "https://pixabay.com/de/illustrations/online-maus-web-internet-weltweit-523234/")!, symbol: "symbol", avgPrice: "123", precision: 2)
type1.avgPrice
This would call the getter declared in the protocol extension, which just returns ""
. This is because Crypto.avgPrice
has no relation to the avgPrice
declared in the protocol extension. You can't "override" a member in an extension, because extensions are dispatched statically. The compiler sees that test
is of type AssetViewAttributes
, finds the default getter you have declared in the extension, and that's what it will call.
To fix this, you need to add avgPrice
as a requirement of the protocol:
protocol AssetViewAttributes {
...
var avgPrice: String { get }
}
This causes Swift to find avgPrice
declared in the protocol, and dispatches it dynamically. If the implementing class happens to implement avgPrice
, that implementation will be called. If not, then the default implementation is called.
Why must Protocol defaults be implemented via Extensions in Swift?
The @optional
directive is an Objective-C only directive and has not been translated to Swift. This doesn't mean that you can't use it in Swift, but that you have to expose your Swift code to Objective-C first with he @objc
attribute.
Note that exposing to Obj-C will only make the protocol available to types that are in both Swift and Obj-C, this excludes for example Structs as they are only available in Swift!
To answer your first question, Protocols are here to define and not implement:
A protocol defines a blueprint of methods, properties, and other requirements [...]
And the implementation should thus be supplied by the class/stuct/enum that conforms to it:
The protocol can then be adopted by a class, structure, or enumeration to provide an actual implementation of those requirements
This definition really applies to Protocols we use in our daily lives as well. Take for example the protocol to write a Paper:
The PaperProtocol defines a paper as a document with the following non-nil variables:
- Introduction
- Chapters
- Conclusion
What the introduction, chapters and conclusion contain are up to the one implementing them (the writer) and not the protocol.
When we look at the definition of Extensions, we can see that they are here to add (implement) new functionalities:
Extensions add new functionality to an existing class, structure, enumeration, or protocol type. This includes the ability to extend types for which you do not have access to the original source code.
So extending a protocol (which is allowed) gives you the possibility to add new functionality and hereby give a default implementation of a defined method. Doing so will work as a Swift only alternative to the @optional
directive discussed above.
UPDATE:
While giving a default implementation to a protocol function in Switch does make it "optional", it is fundamentally not the same as using the @optional
directive in Objective-C.
In Objective-C, an optional method that is not implemented has no implementation at all so calling it would result in a crash as it does not exist. One would thus have to check if it exists before calling it, in opposition to Swift with an extension default where you can call it safely as a default implementation exists.
An optional in Obj-C would be used like this:
NSString *thisSegmentTitle;
// Verify that the optional method has been implemented
if ([self.dataSource respondsToSelector:@selector(titleForSegmentAtIndex:)]) {
// Call it
thisSegmentTitle = [self.dataSource titleForSegmentAtIndex:index];
} else {
// Do something as fallback
}
Where it's Swift counterpart with extension default would be:
let thisSegmentTitle = self.dataSource.titleForSegmentAtIndex(index)
Related Topics
How to Reset Intent Extension Configurations in Widgetkit
Read and Write Permission for User Selected Folder in MAC Os App
Is the Swift Divide "/" Operator Not Working or Have I Missed Something
Using Compiler Variables in Swift
Nested Types in Swift - What Is the Good Practice
Why Does Int(Float(Int.Max)) Give Me an Error
How to Use Trailing Closure in If Condition
Why Is Calendar.Date(From: Datecomponents) Adding Time
Define a Swift Protocol Which Requires a Specific Type of Sequence
Uibarbuttonitem Selector Not Working
In Swift,There's No Way to Get the Returned Function's Argument Names
Label Disappear When Changing Font Size to 25 in Swift
Swift - Force Synchronous Execution of Work and Subsequent UI Rendering
Hide Password with "•••••••" in a Textfield
How to Find Max Value for Double and Float in Swift
Get "Does Not Implement Methodsignatureforselector" When Try to Store Array in Nsuserdefaults,Swift