How to Specify That a Non-Generic Swift Type Should Comply to a Protocol

Swift 5: how to specify a generic type conforming to protocol when declaring a variable

An associated type is used when you want your protocol to work with a variety of types, think a Container protocol that might have several methods all dealing with one contained type.

But your protocol is not that, it doesn't need to know any other types to specify the necessary behavior, so get rid of the associated type.

protocol Pipe {
func await() -> Void
func yield( to: Any, with listener: Selector ) -> Void
}

class Foo {
var imageSource: Pipe & Renderable
}

Using a Generic Type with a Protocol and associated type?

You can't have a property of type Updater. That's abstract. You need to use it to constrain a concrete type (U in this case).

class MyContainer<T, U: Updater> where U.UpdateType == T {
private(set) var object: T
private(set) var updater: U
}

This approach can become tedious however. The where U.UpdateType == T clause often spreads repeatedly through code that uses MyContainer.

Typically the solution is a type-eraser. Rather than a protocol, use a generic with closures:

struct Updater<T> {
var success: ((T) -> Void)?
var failure: ((NSError) -> Void)?
}

class MyContainer<T> {
private(set) var object: T
private(set) var updater: Updater<T>
}

Updater of course could wrap a single update(withSuccess:withFailure:) method instead, but splitting it up this way is often much nicer in practice.

Swift - implement protocol with generic method for different T types

The way you have it written, by conforming to the protocol Client, you have to implement a function get, with a generic, unconstrained argument T. In the example implementations provided, you added a type constraint to the generic parameter T, which does not match the function in the protocol.

There's more than one way you can approach a solution to this problem. Keeping in mind that you said all entities will conform to Mockable, the solution that requires the least change to the code you provided is to use protocol composition to enforce that all parameters T conform to both Decodable and Mockable.

protocol Client {
func get<T: Decodable & Mockable>(_ url: String) -> Promise<T>
}

In your clients, you would implement that function exactly as written.

Now, in your MockClient, you can call T.mock(), and in your real implementations, you can treat T as Decodable as required. Of course, this now requires that even your mock arguments conform to Decodable, but I would assume your mock arguments will be fairly lightweight and thus this wouldn't be a problem.

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

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...



Related Topics



Leave a reply



Submit