Let's say I have two non-generi c protocols (1)
protocol StringValue {
var asString: String {get}
}
protocol StringProvider {
var value: StringValue {get}
}
I want to have a generic version of the second one (2)
protocol TypedStringProvider: StringProvider { // inherits from StringProvider
associatedtype TypedStringValue: StringValue
var typedValue: TypedStringValue { get }
}
And extension with default implementation of non-generic version to have a code free conformance to StringProvider
( doesn't work , pls read below) (3)
extension TypedStringProvider {
var value: TypedStringValue { return typedValue }
}
Now I want several classes to conform both generic TypedStringProvider
and non-generic StringProvider
protocols (4)
extension UIView: TypedStringProvider {
typealias TypedStringValue = String
var typedValue: String { return "Some String" }
}
extension Double: TypedStringProvider {
typealias TypedStringValue = String
var typedValue: String { return String(self) }
}
extension String: StringValue {
var asString: String { return self }
}
And get compiler error: Type 'UIView' does not conform to protocol 'StringProvider'
.
Seemingly extension (3) doesn't work like I want because TypedStringValue
is not a StringValue
in despite of this constraint associatedtype TypedStringValue: StringValue
from (2)
The question is how to conform to non-generic StringProvider
while keeping value
typed
Example :
0.5.value.lowercased()
Of course StringValue
doesn't have lowercased
method from String
so it won't compile.
What I have tried:
First is to add untyped property to extension (3)
extension TypedStringProvider {
var value: TypedStringValue { return typedValue }
var value: StringValue { return typedValue }
}
Doesn't work because of Invalid redeclaration of 'value'
error
Second is to extend my classes and add untyped property there (5)
extension UIView {
var value: StringValue { return typedValue }
}
extension Double {
var value: StringValue { return typedValue }
}
It works without compiler errors but
No autocompletion for lowercased
in our example.
With extensions (5) I need to write a lot of code for every class conforming StringProvider
and every property this protocol has
Any ideas?
value
is defined as type StringValue
, so this is the type you should specify in your extension of TypedStringProvider
in order to complete the protocol conformance:
extension TypedStringProvider {
var value: StringValue {
return typedValue
}
}
Found solution
extension StringProvider where Self: TypedStringProvider {
var value: StringValue { return typedValue }
}
With this extension there's no need to write similar extensions to every class and autocompletion works too.
Full code:
protocol StringValue {
var asString: String {get}
}
protocol StringProvider {
var value: StringValue {get}
}
protocol TypedStringProvider: StringProvider {
associatedtype TypedStringValue: StringValue
var typedValue: TypedStringValue { get }
}
extension StringProvider where Self: TypedStringProvider {
var value: StringValue { return typedValue }
}
extension TypedStringProvider {
var value: TypedStringValue { return typedValue }
}
extension UIView: TypedStringProvider {
typealias TypedStringValue = String
var typedValue: String { return "some string" }
}
extension Double: TypedStringProvider {
typealias TypedStringValue = String
var typedValue: String { return String(self) }
}
extension String: StringValue {
var asString: String { return self }
}
let doubleValue = 0.5.value.lowercased()
On StringProvider
you are defining value
to a StringValue
:
protocol StringProvider {
var value: StringValue { get }
}
but here you are defining the type to TypedStringValue
which is not the same as StringValue
. (underlying value can be TypedStringValue
, however it needs to be typecasted from StringValue
to TypedStringValue
when using it)
extension TypedStringProvider {
var value: TypedStringValue { return typedValue }
}
There are two solutions that I can come up for this scenario right now:
If you want to make value
generic and change the type based on TypedStringProvider
you can:
protocol StringProvider {
associatedtype StringProviderValue: StringValue
var value: StringProviderValue { get }
}
Conform to the protocol by defining StringProviderValue
to TypedStringValue
extension TypedStringProvider {
var value: TypedStringValue { return typedValue }
}
Keep the StringProvider
as it is:
protocol StringProvider {
var value: StringValue { get }
}
Conform to the protocol by using the correct StringValue
type, and inside it return typedValue: TypedStringValue
which can be downcasted to StringValue
extension TypedStringProvider {
var value: StringValue { return typedValue }
}
Both solutions give the same output:
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.