Generic Within a Generic in Swift

Generics of generics in Swift 3

If I'm understanding what you want to do correctly, this can be achieved with a protocol with an associated type. You can make your Simple class conform to it, and then constrain the type parameter on Complex to be its conforming types, and finally, place qualifications on the associated type. Should be something like this:

protocol SimpleType {
associatedtype Assoc
}

class Simple<T: Equatable>: SimpleType {
typealias Assoc = T
}

class Complex<U: SimpleType> where U.Assoc: Equatable {
init() {

}
}

let c = Complex<Simple<Date>>()

Swift, use generic property with different specific types - Reference to generic type requires arguments in

You can specify a protocol for providing Stage types like so:

protocol StageProvider {

associatedtype T: Stage

func getType() -> T.Type

}

Then make your SomethingThatKnowsAboutTheStages or any other one conform this protocol:

class SomethingThatKnowsAboutTheStages: StageProvider {

typealias T = SecondStage

func getType() -> T.Type {
SecondStage.self
}

}

Add an initializer for your FooBarController:

class FooBarController<StageType: Stage>: UIViewController {

convenience init(stage: StageType.Type) {
self.init()
}

}

And finally use all these:

func fooBarScreen<T: StageProvider>(boop: T) {
let controller = FooBarController(stage: boop.getType())
}

Generic of Generics ? class ATU?

Try declaring a protocol SectionDataType that requires an associated type. This means that any conforming type (like Section below) must implement a typealias. Then, in TableViewViewModel you can access the type which you were calling U through that typealias.

protocol SectionDataType {
associatedtype RowDataType
}

struct Section<U>: SectionDataType {
typealias RowDataType = U
}

class TableViewViewModel<T: SectionDataType> {
typealias RowDataType = T.RowDataType

var sections = [T]()
}

How to get a class with generic type accept an array of different by same generic types?

It's good that you want to experiment with generics, but this isn't the occasion for it. You say:

The idea ... is to support other items in the future in case I want to expand the app...not just coffee.

But you don't need a generic for that. The only requirement for managing an order is that the order's item be Priceable. Priceable is already a type; you don't need to add a generic type to the mix.

struct Order {
var name: String
var item: Priceable
}

class OrderManager {
private var orders: [Order]

init(orders: [Order]) {
self.orders = orders
}

func add(_ order: Order) {
self.orders.append(order)
}
}

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.

Using Any instead of generics

In swift, you can assign value to a variable ONLY if the type of the variable is same with the value. So imagine you have this:

var a = 1
var b = 2

func swapTwoValues(_ a: inout Any, _ b: inout Any) {
let temporaryA = a
a = b
b = temporaryA
}

swapTwoValues(&a, &b) // <- Cannot pass immutable value as inout argument: implicit conversion from 'Int' to 'Any' requires a temporary

So compiler forces you to implicitly assign Any for the type of the variables. This is not what you want I think. So you have to tell the compiler that it doesn't matter the type of the arguments. The only thing matters is they both have same type to fulfill compilers need. So to achieve this, you can use generic or some kind of protocol.

func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
let temporaryA = a
a = b
b = temporaryA
}

swapTwoValues(&a, &b) // Done

Tip: You can swap values with tuple in single line of code without using temporaryVar like this: (Even without function and generic and etc.)

(a, b) = (b, a)

Generic type not preserved when called within another generic function

You're trying to reinvent class inheritance with generics. That is not what generics are for, and they don't work that way. Generic methods are statically dispatched, which means that the code is chosen at compile-time, not runtime. An overload should never change the behavior of the function (which is what you're trying to do here). Overrides in where clauses can be used to improve performance, but they cannot be used to create dynamic (runtime) dispatch.

If you must use inheritance, then you must use classes. That said, the problem you've described is better solved with a generic Task rather than a protocol. For example:

struct Task<Result> {
let execute: () throws -> Result
}

enum TaskRunner {
static func run<Result>(task: Task<Result>) throws -> Result {
try task.execute()
}
}

let specificTask = Task(execute: { "Some Result" })

print(try TaskRunner.run(task: specificTask)) // Prints "Some Result"

Notice how this eliminates the "task not supported" case. Rather than being a runtime error, it is now a compile-time error. You can no longer call this incorrectly, so you don't have to check for that case.

If you really want dynamic dispatch, it is possible, but you must implement it as dynamic dispatch, not overloads.

enum TaskRunner<T: Task> {
static func run(task: T) throws -> T.Result {

switch task {
case is SpecificTask:
// execute a SpecificTask
return "Some Result" as! T.Result // <=== This is very fragile
default:
throw SomeError.error
}
}
}

This is fragile because of the as! T.Result. If you change the result type of SpecificTask to something other than String, it'll crash. But the important point is the case is SpecificTask, which is determined at runtime (dynamic dispatch). If you need task, and I assume you do, you'd swap that with if let task = task as? SpecificTask.

Before going down that road, I'd reconsider the design and see how this will really be called. Since the Result type is generic, you can't call arbitrary Tasks in a loop (since all the return values have to match). So it makes me wonder what kind of code can actually call run.



Related Topics



Leave a reply



Submit