How can I use a generic-type constraint extension for a class with a `Codable` type constraint? - swift

Say my class definition is:
class Foo<T: Codable> {
let bar: T
}
I want to extend arrays of this type:
extension Array where Element: Foo<Codable> { /* do something based on `bar` values */ }
This produces the error
Protocol 'Codable' (aka 'Decodable & Encodable') as a type cannot conform to 'Decodable'
I did read in another question that conforming T to Encodable/Decodable isn't possible if T == Encodable/Decodable since a protocol can't conform to itself, but this is only for a certain property so I'm having trouble wrapping my head around it. Is there any way to achieve this?

I think is better to use protocol instead class like this.
protocol FooProtocol {
associatedtype T : Codable
var bar: T {get set}
}
extension Array where Element: FooProtocol {
}

Related

Is it possible to have a property containing generic protocol in Swift?

Let's say I have a protocol
protocol MyProtocol {
associatedtype DoSmthValue
func doSmth(_ value: DoSmthValue) -> String
}
I also have another protocol:
protocol AnotherProtocol {
associatedtype T: MyProtocol
var items: [T] { get set }
}
and finally my class where I want to use it:
class MyClass: AnotherProtocol {
typealias T = MyProtocol // this code returns an error
var items = [T]
}
But this code returns me MyProtocol can only be used as a generic constraint because it has Self or associated type requirements which is fair enough. I would like to use it like typealias T = MyProtocol<String> which is not allowed in Swift. So is there a way?
AnotherProtocol has an associated value which must be some type that conforms to MyProtocol. A protocol cannot conform to protocols, including itself, so that type cannot be MyProtocol; it has to be a type conforming to MyProtocol.
var items, then, has to be an array of whatever that type is.
So, let's say you have a type conforming to MyProtocol which defines the associated type to be String:
struct Foo: MyProtocol {
func doSmth(_ value: String) -> String {
value
}
}
Then you can define MyClass as:
class MyClass: AnotherProtocol {
var items: [Foo]
init(_ items: [Foo]) { self.items = items }
}
Of course, if you don't want it to be specific to Foo, you can make it generic with respect to any type T that conforms to MyProtocol and has an associated type of String:
class MyClass<T: MyProtocol>: AnotherProtocol where T.DoSmthValue == String {
var items: [T]
init(_ items: [T]) { self.items = items }
}
let myClass = MyClass([Foo(), Foo(), Foo()])
New Dev's answer is exactly right about how to fix this, but it's also worth realizing why this set of protocols is requiring something that doesn't make sense.
Let's pretend your MyClass definition were legal. I could then write the following code:
for item in items {
// item is of pseudo-type "MyProtocol"
// (that's illegal, but we're pretending it's not)
item.doSmth(... what would go here? ...)
}
As written, DoSmthValue could require literally any type, and you'd have to provide it. But you don't know what it is (and you can't know what it is). There's no way to make use of this protocol; you can't generate the things it requires. So it's not meaningful for item to be of type MyProtocol. You need to go back to thinking about what you want the calling code to look like, and that will tell you what kinds of protocols you need. Starting with the protocols (especially if you're new to this) will typically get you in trouble.

Multiple constraints on parameter of a generic function

Reading about generic functions in Swift, I see that it is possible to put some constraints on a parameter by requiring, that it is a subclass of a given class C, or that it implements a given protocol P.
But I wonder if there is a way to require both at the same time. I haven't found anything about that yet.
Does anyone know?
Actually You can do that.
If you have seen Codable in swift it is actually Decodable and Encodable
typealias Codable = Decodable & Encodable
So in some function if you are using generic T as Codable
struct StructOfCodable<T:Codable>: Codable {
....
}
Here is example
protocol Test {}
class TClass {
}
typealias Common = Test & TClass
func generic <T:Common>(method:T) {
}
Another way is protocol and class both can have super class. So you can create common protocol
like
protocol CommonInProtocolAndStruct { }
protocol ProtocolUsedAsConstraint:CommonInProtocolAndStruct {}
struct StructUsedAsConstraint:CommonInProtocolAndStruct {}
And any method you can use CommonInProtocolAndStruct as generic constraint
You can add any number of type constraints using a where clause. Examples:
import UIKit
func f<T>(t: T) where T: UIView, T: Encodable {}
class C<T> where T: UIView, T: Encodable {}

var defined in protocol doesn't conform to multiple protocol

I'm struggling with protocols in Swift.
I have defined a protocol like this:
protocol AProtocol {
var property : BProtocol {get set}
}
And I would like to conform to AProtocol in a class with a property that also conform to another protocol. I've tried in these two ways:
class AClass: AProtocol {
var property = BClass()
}
and:
class AClass: AProtocol {
var property: BProtocol & MyBClassType = BProtocol()
}
but none of them seems to work (BClass itself confirm to BProtocol)
This issue is a bit difficult to explain, I hope it was clear.
Is it a limitation of the Swift language?
Do you know a work around to this?
You have two issues: firstly, the property name must match that declared in the protocol, secondly you need to type annotate the variable to be of type BProtocol as Hamish explained in the comment.
protocol AProtocol {
var aProperty : BProtocol {get set}
}
protocol BProtocol {}
class BClass: BProtocol {}
class AClass: AProtocol {
var aProperty: BProtocol = BClass()
}
You should also conform to the Swift naming convention, which is lowerCamelCase for variable names, so I changed AProperty to its correct form, aProperty.

How to make a generic class conform to a protocol for specific type?

Say there exists a generic struct:
public struct Matrix<T> where T: FloatingPoint, T: ExpressibleByFloatLiteral {
// some methods...
}
Is it possible extend the struct to conform to a protocol for constrained T using where clauses? E.g. something like
extension Matrix where T: SpecificClass : SomeProtocol {
// This does not compile :(
}
No, such construct isn't possible (circa Swift 3.1 at least).
For instance:
class SomeClass { }
protocol SomeProtocol { }
extension Matrix: SomeProtocol where T == SomeClass { }
Gives a very clear error message:
Extension of type Matrix with constraints cannot have an inheritance clause.
But all is not lost... as correctly noted by Alexander, there is already a proposal lined up for Swift 4! The feature will be called Conditional Conformances (SE-0143).
A nice example for all protocol-oriented programming hackers out there:
extension Array: Equatable where Element: Equatable {
...
}
If an array contains equatable elements than said array is also equatable.
Update. Swift 4 is out but this feature hasn’t landed yet. We may need to wait until Swift 5 for this...

Protocol extensions on Structs causes compile error 'Self' constrained to non-protocol type

I'm attempting to apply a constrained protocol extension to a struct (Swift 2.0) and receiving the following compiler error:
type 'Self' constrained to non-protocol type 'Foo'
struct Foo: MyProtocol {
let myVar: String
init(myVar: String) {
self.myVar = myVar
}
}
protocol MyProtocol {
func bar()
}
extension MyProtocol where Self: Foo {
func bar() {
print(myVar)
}
}
let foo = Foo(myVar: "Hello, Protocol")
foo.bar()
I can fix this error by changing struct Foo to class Foo but I don't understand why this works. Why can't I do a where Self: constrained protocol a struct?
This is an expected behaviour considering struct are not meant to be inherited which : notation stands for.
The correct way to achieve what you described would be something like equality sign like:
extension MyProtocol where Self == Foo {
func bar() {
print(myVar)
}
}
But this doesn't compile for some stupid reason like:
Same-type requirement makes generic parameter Self non-generic
For what it's worth, you can achieve the same result with the following:
protocol FooProtocol {
var myVar: String { get }
}
struct Foo: FooProtocol, MyProtocol {
let myVar: String
}
protocol MyProtocol {}
extension MyProtocol where Self: FooProtocol {
func bar() {
print(myVar)
}
}
where FooProtocol is fake protocol which only Foo should extend.
Many third-party libraries that try to extend standard library's struct types (eg. Optional) makes use of workaround like the above.
I just ran into this problem too. Although I too would like a better understanding of why this is so, the Swift language reference (the guide says nothing about this) has the following from the Generic Parameters section:
Where Clauses
You can specify additional requirements on type parameters and their
associated types by including a where clause after the generic
parameter list. A where clause consists of the where keyword, followed
by a comma-separated list of one or more requirements.
The requirements in a where clause specify that a type parameter
inherits from a class or conforms to a protocol or protocol
composition. Although the where clause provides syntactic sugar for
expressing simple constraints on type parameters (for instance, T:
Comparable is equivalent to T where T: Comparable and so on), you can
use it to provide more complex constraints on type parameters and
their associated types. For instance, you can express the constraints
that a generic type T inherits from a class C and conforms to a
protocol P as <T where T: C, T: P>.
So 'Self' cannot be a struct or emum it seems, which is a shame. Presumably there is a language design reason for this. The compiler error message could certainly be clearer though.
As Foo is an existing type, you could simply extend it this way:
struct Foo { // <== remove MyProtocol
let myVar: String
init(myVar: String) {
self.myVar = myVar
}
}
// extending the type
extension Foo: MyProtocol {
func bar() {
print(myVar)
}
}
From The Swift Programming Language (Swift 2.2):
If you define an extension to add new functionality to an existing type, the new functionality will be available on all existing instances of that type, even if they were created before the extension was defined.