Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift Array extension with generic items

I am using Swift 3.1 and have some generic struct:

struct Section<InfoT: Equatable, ItemsT: Equatable> {
    let info: InfoT?
    let items: [ItemsT]
}

And want array extension with custom method for elements of this generic type:

extension Array where Element == Section<Equatable, Equatable> {
    // Just dummy example function:
    func debugDummy() {
        for section in self {
            let info = section.info
            print("section info: \(info).")
            for item in section.items {
                print("item: \(item).")
            }
        }
    }
}

This gives me compilation errors:

error: using 'Equatable' as a concrete type conforming to protocol 'Equatable' is not supported
extension Array where Element == Section<Equatable, Equatable> {
                                 ^

error: GenericsListPlayground.playground:6:24: error: value of type 'Element' has no member 'info'
            let info = section.info
                       ^~~~~~~ ~~~~

error: GenericsListPlayground.playground:8:25: error: value of type 'Element' has no member 'items'
            for item in section.items {
                        ^~~~~~~ ~~~~~

How to properly declare such extension? I have tried few variations for declaring this extension, like:

extension Array where Element == Section (no arguments)

produces:

error: reference to generic type 'Section' requires arguments in <...>

etc... none of them want to compile.

like image 995
Lukasz Avatar asked Apr 21 '17 10:04

Lukasz


People also ask

When to use extension in Swift?

Extensions in Swift are super powerful, because they help you organize your code better. You use an extension to add new functionality to an existing class. And they're especially useful if you don't have access to the original code of that class!

What are extensions in Swift?

Extensions add new functionality to an existing class, structure, enumeration, or protocol type. This includes the ability to extend types for which you don't have access to the original source code (known as retroactive modeling). Extensions are similar to categories in Objective-C.


2 Answers

Try this:

import Foundation

protocol Section {
    associatedtype InfoT: Equatable
    associatedtype ItemsT: Equatable
    var info: InfoT? { get }
    var items: [ItemsT] { get }
}

extension Array where Element: Section {
    // Just dummy example function:
    func debugDummy() {
        for section in self {
            let info = section.info
            print("section info: \(String(describing: info)).")
            for item in section.items {
                print("item: \(item).")
            }
        }
    }
}
like image 57
Eppilo Avatar answered Oct 03 '22 05:10

Eppilo


Derivative from @Eppilo`s answer:

While the Section protocol is all good, it's important to remember the different ways it can be implemented.

Here's an example that shows two use-cases, copy and paste into playground for Swift 3.1 to check it out :)

protocol Section {
    associatedtype InfoType: Equatable
    associatedtype ItemsType: Equatable
    var info: InfoType? { get set }
    var items: [ItemsType] { get set }
}

// This struct will try to infer the types for info and items when instantiating an object, any type that conforms to Equatable can be used while instantiating.
struct InferredSection<Info: Equatable, Items: Equatable> : Section {
    var info: Info?
    var items: [Items]
}

// This struct has explicit types, which means you can ONLY instantiate an object from this by using the specified types within the struct.
struct ExplicitSection : Section {

    typealias InfoType = String
    typealias ItemsType = Int

    var info: InfoType?
    var items: [ItemsType]
}

extension Array where Element: Section  {
    // Just dummy example function:
    func debugDummy() {
        for section in self {
            let info = section.info
            print("section info: \(String(describing: info)).")
            for item in section.items {
                print("item: \(item).")
            }
        }
    }
}

let explicit = ExplicitSection(info: "This section has explicit set types", items: [13, 37])
let inferred = InferredSection(info: "This section has inferred types", items: ["Foo", "Bar"])

let explicitTypeArray = [explicit, explicit]
let inferredTypeArray = [inferred, inferred]

explicitTypeArray.debugDummy()
inferredTypeArray.debugDummy()

// The Section protocol can only be used as a generic constraint since it has associatedType requirements, which means you can't mix different types that conforms to Section to the same array without changing the the type to Any
let all: [Section] = []

let mixedArray = [explicit, inferred] // Not allowed unless cast to [Any]
like image 29
Laffen Avatar answered Oct 03 '22 06:10

Laffen