Generics in Swift

Generics in Swift

The subscript method of collections is defined as

public subscript(position: Self.Index) -> Self.Iterator.Element { get }

which means that your function should take as arguments

  • a collection C, and
  • a value of the associated type C.Iterator.Element

and return an array of C.Index. In addition, the element type
should be Equatable:

func findAll<C: Collection> (_ collection: C, _ element: C.Iterator.Element) -> [C.Index]
where C.Iterator.Element: Equatable
{ ... }

Similar as in your solution for arrays, one can loop over the
collection's indices:

func findAll<C: Collection> (_ collection: C, _ element: C.Iterator.Element) -> [C.Index]
where C.Iterator.Element: Equatable
{
var result: [C.Index] = []

var idx = collection.startIndex
while idx != collection.endIndex {
if collection[idx] == element {
result.append(idx)
}
collection.formIndex(after: &idx)
}

return result
}

One would expect that something like

for idx in collection.startIndex ..< collection.endIndex
// or
for idx in collection.indices

works, but (in Swift 3) this requires an additional constraint
on the associated Indices type:

func findAll<C: Collection> (_ collection: C, _ element: C.Iterator.Element) -> [C.Index]
where C.Iterator.Element: Equatable, C.Indices.Iterator.Element == C.Index
{

var result: [C.Index] = []

for idx in collection.indices {
if collection[idx] == element {
result.append(idx)
}
}

return result
}

This is no longer necessary in Swift 4, see for example
Unable to use indices.contains() in a Collection extension in Swift 3 for a good explanation.

This can now be simplified using filter:

func findAll<C: Collection> (_ collection: C, _ element: C.Iterator.Element) -> [C.Index]
where C.Iterator.Element: Equatable, C.Indices.Iterator.Element == C.Index
{
return collection.indices.filter { collection[$0] == element }
}

Example (a collection of Character):

let chars = "abcdabcdabcd".characters
let indices = findAll(chars, "c")
for idx in indices {
print(chars[idx])
}

Set is a Collection as well, it has an associated Index
type and a subscript method. Example:

let set = Set([1, 2, 3, 4, 5, 6, 7, 8, 9])
let indices = findAll(set, 3)
for idx in indices {
print(set[idx])
}

Finally you might want to define the function as a method
on the Collection type:

extension Collection where Iterator.Element: Equatable, Indices.Iterator.Element == Index {
func allIndices(of element: Iterator.Element) -> [Index] {
return indices.filter { self[$0] == element }
}
}

// Example:
let indices = [1, 2, 3, 1, 2, 3].allIndices(of: 3)

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)

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

What's the difference between using or not using the 'where' clause with generics?

This is clearly stated in the Swift guide:

The requirements in a generic where clause specify that a type
parameter inherits from a class or conforms to a protocol or protocol
composition. Although the generic 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 constrain
the associated types of type parameters to conform to protocols. For
example, <S: Sequence> where S.Iterator.Element: Equatable specifies
that S conforms to the Sequence protocol and that the associated type
S.Iterator.Element conforms to the Equatable protocol. This constraint
ensures that each element of the sequence is equatable.

Simply put, where lets you specify constraints about the associated types of a generic parameter, while in <> you can't do this.

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

Swift generics and Any

I ended up using a generic function and adding a generic value as needed, somethign like this.

 private func invoke<T: Encodable, U: Encodable>(_ methodName: String, _ callBack: Any?, _ callbackState: Any?, expectedArgs: Int,
_ arg1: T? = nil, _ arg2: U? = nil, _ arg3: T? = nil, _ arg4: T? = nil) {...


Related Topics



Leave a reply



Submit