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.
- You want
configure
to do nothing whenT
is neitherSubTypeAOfBaseClass
norSubTypeBOfBaseClass
- you only want
MyClass<SubTypeAOfBaseClass>
andMyClass<SubTypeBOfBaseClass>
to be valid types -MyClass<BaseClass>
andMyClass<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
How to Store Data from a Picker View in a Variable When a Button Is Pressed
Applescript Used in My Cocoa MAC App, Stopped Working in Osx 10.14
Swift - Rotate Gesture and Rotation Increments of 90 Degrees
How to Put a View with Loadmore Button in UItableview After The Cell
Include Inheritance Constraint in Swift Generic Types
How to Decorate Siesta Request with an Asynchronous Task
How to Show an Alert in Swift UIalertview Not Working
Firebase - Swift 4: Message Sent But Not Received
Swiftui Tabview with List Not Refreshing After Objected Deleted From/Added to Core Data
Verifying The Purchase (Receipt) of Another Application (Mac App Store)
Calling Asynchronous Method Inside For-Loop
Cache That Can Purge Unused Objects by Demand
Uislider Handle Changes Width on Different Size Ipads