Let's say I have two non-generic 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
}
}
Problem
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 }
}
Solution
There are two solutions that I can come up for this scenario right now:
1. Generic Approach
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 }
}
2. StringValue approach
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 }
}
Output
Both solutions give the same output:
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()
Related
I've created the following extension:
import Foundation
extension Collection {
/// Returns `nil` if empty
var nonEmptyValue: Self? {
isEmpty ? nil : self
}
}
Now I'd like to make it a property wrapper so I could use it like this:
final class MyClass {
#NonEmpty
var string: String? = "test"
}
The idea is that whenever an empty string is assigned to the property, it gets replaced with nil.
Is it even possible to create such a property wrapper (since String? and String are of different type) and how would I go about it?
I'm using your extension:
import Foundation
#propertyWrapper
struct NonEmpty<T: Collection> {
var wrappedValue: T? {
didSet {
self.wrappedValue = wrappedValue?.nonEmptyValue
}
}
init(wrappedValue: T?) {
self.wrappedValue = wrappedValue?.nonEmptyValue
}
}
extension Collection {
/// Returns `nil` if empty
var nonEmptyValue: Self? {
isEmpty ? nil : self
}
}
and the result is just like image below:
I have the following piece of code. The protocols and extensions I've added don't seem to be doing quite what I expect. What am I missing?
struct foo {
var getFoo: String
var getBar: bar
struct bar {
var getBar: String
}
}
protocol FooProtocol {
var getFoo: String { get }
var getBar: BarProtocol { get }
}
extension foo: FooProtocol {} // Type 'foo' does not conform to protocol 'FooProtocol'
protocol BarProtocol {
var getBar: String { get }
}
extension foo.bar: BarProtocol {}
The problem in this code is the line extension foo: FooProtocol {}, where we're trying to say that, in effect foo.getBar.getBar should be a valid property. However, Xcode throws up a compilation error saying "Type 'foo' does not conform to protocol 'FooProtocol'.
I can fix this by changing the following:
protocol FooProtocol {
var getFoo: String { get }
var getBar: foo.bar { get } // instead of var getBar: BarProtocol { get }
}
However, it seems the line extension foo.bar: BarProtocol {} should mean FooProtocol doesn't care if we give it foo.bar or BarProtocol. What am I missing here?
#Drobs answer was correct. Here's a bonus question.
BONUS
Now suppose I need to implement another struct that conforms to FooProtocol. Neither of these approaches work. What's the fix?
struct FooImplementation: FooProtocol { // Type 'FooImplementation' does not conform to protocol 'FooProtocol'
var getFoo: String
var getBar: BarProtocol
}
Do I need to use some sort of typealias to achieve this?
You want to use an associatedtype in the protocol. In that case it essentially says give me a type that implements the protocol BarProtocol.
protocol FooProtocol {
associatedtype BarType: BarProtocol
var getFoo: String { get }
var getBar: BarType { get }
}
protocol BarProtocol {
var getBar: String { get }
}
struct Foo: FooProtocol {
var getFoo: String
var getBar: Bar
struct Bar: BarProtocol {
var getBar: String
}
}
I know that you can give a default value with a protocol extension like this
protocol SomeProtocol {
var prop: String { get }
}
extension SomeProtocol {
var prop: String {
return "defaultValue"
}
}
struct SomeA: SomeProtocol {}
struct SomeB: SomeProtocol {}
let a = SomeA()
let b = SomeB()
debugPrint(a.prop) // prints defaultValue
debugPrint(b.prop) // prints defaultValue
but is there a way to have different default value for different implementations of the protocol like this without implementing the property for every class or struct that conforms to this protocol?
debugPrint(a.prop) // prints defaultValue
debugPrint(b.prop) // prints differentDefaultValue
or some similar pattern for doing something like this?
Protocol inheritance.
protocol 😺: SomeProtocol { }
extension 😺 {
var prop: String { "😺" }
}
struct SomeA: SomeProtocol { }
struct SomeB: 😺 { }
struct SomeC: 😺 { }
SomeA().prop // "defaultValue"
SomeB().prop // "😺"
SomeC().prop // "😺"
I have the following piece of code, the protocol MyDisplayable has three optional Strings, and I have a default implementation of the protocol via extension. My question is, since I'm sure the extension returns the three strings, is there a way I can use them as non-optional and is there any risk if some other implementation overwrites it? (see the question points 1 and 2 in code below)
Thanks a lot!
protocol MyDisplayable {
var displayName: String? { get }
var shortDescription: String? { get }
var longDescription: String? { get }
}
protocol MyObject : MyDisplayable, CustomStringConvertible {
}
extension MyObject {
var displayName: String? {
return "noname"
}
var shortDescription: String? {
return "something can't be described"
}
var longDescription: String? {
return "no way to describe it further"
}
var description: String {
// **1. is there a way to use the strings as if they are non-optional?**
// **2. is it a problem if another class implements the protocol and returns `nil` for any of the strings, but here they are force unwrapped?**
return "\(displayName!): \(shortDescription!)\n\(longDescription!)"
}
}
class Something : MyObject {
}
let something = Something()
print("Something: \(something)")
Unfortunately, it's not possible to treat a declared optional as a non-optional.
You have declared those strings as optional in your protocol, thus when you implement that protocol they stay optional.
However, you can use getter-setter to ensure that your variables always store some value even when they are declared as optional.
I'll elaborate with some code :
protocol MyDisplayable {
var displayName: String? { get set }
var shortDescription: String? { get set }
var longDescription: String? { get set }
}
protocol MyObject : MyDisplayable, CustomStringConvertible {
}
extension MyObject {
var displayName: String? {
get {
return "noname"
}
set {
newValue ?? ""
}
}
var shortDescription: String? {
get {
return "something can't be described"
}
set {
newValue ?? ""
}
}
var longDescription: String? {
get {
return "no way to describe it further"
}
set {
newValue ?? ""
}
}
var description: String {
// **1. is there a way to use the strings as if they are non-optional?**
// **2. is it a problem if another class implements the protocol and returns `nil` for any of the strings, but here they are force unwrapped?**
return "\(displayName!): \(shortDescription!)\n\(longDescription!)"
}
}
class Something : MyObject {
}
let something = Something()
print("Something: \(something)")
Now even if some other class overwrites a nil value to those strings they will return empty string "".
They will still be optional as they are declared optional, but now they will always have a non-nil value.
How about conditional unwrapping in your default implementation?
return "\(displayName ?? "" ): \(shortDescription ?? "" )\n\(longDescription ?? "")"
I'm looking for a way to add a default initializer to a protocol via protocol extensions.
My protocol is:
protocol TestProtocol {
var myVar : Double { get set }
init(value: Double)
init(existingStruct : TestProtocol)
}
I've implemented a struct using this protocol as:
struct TestStruct : TestProtocol {
var myVar : Double
init(value : Double) {
myVar = value
}
init (existingStruct : TestProtocol) {
myVar = existingStruct.myVar
}
}
However if I try via extension to make a default initializer for this protocol I run into self issues:
extension TestProtocol {
init(value : Double) {
myVar = value
}
init(existingStruct : TestProtocol) {
myVar = existingStruct.myVar
}
}
Where both assignment lines issue the error
Variable 'self' passed by reference before being initialized
Is there a way to make this work - or am i limited to using classes?
Your question is almost the same as in this post I answered yesterday.
Here is the trick to solve this :)
protocol TestProtocol {
var myVar : Double { get set }
init() // designated initializer which will ensure that your class or structer type will instantiate correctly
}
struct TestStruct : TestProtocol {
var myVar : Double
init() {
myVar = 0
}
}
extension TestProtocol {
init(value : Double) {
self.init()
myVar = value
}
init(existingStruct : TestProtocol) {
self.init()
myVar = existingStruct.myVar
}
}
Have a good day. :) Protocol extension is so nice.