Swift Generics and Protocol Extensions

Can you have protocol extension for a specific generics type in Swift?

You can do this in Swift 4.1

extension Array: Printable where Element: Printable {
// ...
}

Extension of constructed generic type in Swift

Swift 5.x:

extension Array where Element == Int {

var sum: Int {
reduce(0, +)
}
}

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)
}
}

Conform class extension to generic protocol function

It looks like you're conflating associated types with generic functions.

Generic functions allow you to provide a type to replace a given generic placeholder at the call site (i.e when you call the function).

Associated types allow types conforming to protocols to provide their own type to replace a given placeholder type in the protocol requirements. This is done per type, not at the call site of any function. If you wish to enforce a conformance requirement for an associatedtype, you should do so directly on its declaration (i.e associatedtype PageItemType : Pageable).

If I understand your requirements correctly, your itemAt(index:) function should be non-generic (otherwise the associatedtype in your protocol is completely redundant). The type that it returns is defined by the implementation of the type that conforms to Page, rather than the caller of the function. For example, your People class defines that the PageItemType associated type should be a Person – and that is what itemAt(index:) should return.

protocol Pageable {/* ... */}

protocol Page {

// any conforming type to Page will need to define a
// concrete type for PageItemType, that conforms to Pageable
associatedtype PageItemType : Pageable

// returns the type that the implementation of the protocol defines
// to be PageItemType (it is merely a placeholder in the protocol decleration)
func itemAt(index: Int) -> PageItemType
}

class Person : Pageable {/* ... */}

class People {
var people: [Person]?
}

extension People : Page {

// explicitly satisfies the Page associatedtype requirement.
// this can be done implicitly be the itemAt(index:) method,
// so could be deleted (and annotate the return type of itemAt(index:) as Person)
typealias PageItemType = Person

// the itemAt(index:) method on People now returns a Person
func itemAt(index: Int) -> PageItemType {

// I would advise against force unwrapping here.
// Your people array most likely should be non-optional,
// with an initial value of an empty array
// (do you really need to distinguish between an empty array and no array?)
let person = self.people![index]
return person
}
}

In regards to your implementation of a PagedCollection – as the PageItemType associated type in your Page protocol conforms to Pageable, I see no need for an ItemType generic parameter. This can simply be accessed through the associated type of the given PageType generic parameter.

As an example:

class PagedCollection<PageType:Page> {

// PageType's associatedtype, which will conform to Pageable.
// In the case of the PageType generic parameter being People,
// PageType.PageItemType would be of type Person
var foo : PageType.PageItemType

init(foo: PageType.PageItemType) {
self.foo = foo
}
}

// p.foo is of type Person, and is guarenteed to conform to Pageable
let p = PagedCollection<People>(foo: Person())

Swift Generic Classes and Extensions with Conditional Generics

Consider the case when T is BaseClass, or when T is AnotherSubclass that I defined as

class AnotherSubclass : BaseClass {

}

What would happen? You haven't declared a conformance to Configure when T is AnotherSubclass!

There's really only two (not bad) choices here.

  1. You want configure to do nothing when T is neither SubTypeAOfBaseClass nor SubTypeBOfBaseClass
  2. you only want MyClass<SubTypeAOfBaseClass> and MyClass<SubTypeBOfBaseClass> to be valid types - MyClass<BaseClass> and MyClass<AnotherSubclass> would give compiler errors.

Choice 2 is not possible in Swift. That would require something similar to the sealed types in Java or Kotlin.

Choice 1 can be done like this:

class BaseClass {
...
func configure() {

}
}

class SubTypeAOfBaseClass: BaseClass {
...
override func configure() {
print("Configuring SubTypeAOfBaseClass")
doSomethingA()
}
}

class SubTypeBOfBaseClass: BaseClass {
...
override func configure() {
print("Configuring SubTypeAOfBaseClass")
doSomethingB()
}
}
class MyClass<T: BaseClass> {

let aThing = T()

func someMethod() {
aThing.configure()
}
}

You might notice that the each implementation of configure has been moved to the base classes. If you want to implement them all in MyClass, you must check the type by hand:

class MyClass<T: BaseClass> {

let aThing = T()

func someMethod() {
if let selfA = self as? MyClass<SubTypeAOfBaseClass> {
selfA.configure()
} else if let selfB = self as? MyClass<SubTypeBOfBaseClass> {
selfB.configure()
}
}
}

extension MyClass where T == SubTypeAOfBaseClass {
func configure() {
print("Configuring SubTypeAOfBaseClass")
aThing.doSomethingA()
}
}
extension MyClass where T == SubTypeBOfBaseClass {
func configure() {
print("Configuring SubTypeBOfBaseClass")
aThing.doSomethingB()
}
}

This is because of the second problem in your code - different parameterisations of a generic type, MyClass<SubTypeAOfBaseClass> and MyClass<SubTypeBOfBaseClass>, can't conform to a protocol differently. This is a limitation of Swift, unfortunately. See here for more info.

Swift Generics and Protocol Extensions

That's an interesting problem. Your code seems like it should work; you might want to file an enhancement request.

Here's a workaround that seems to work correctly:

class SomeGenericClass<CellType: Cell> {
func someFunction() {
let reuseIdentifier = (CellType.self as Reusable.Type).reuseId()
}
}

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

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.



Related Topics



Leave a reply



Submit