Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Usage of protocols as array types and function parameters in swift

People also ask

What are protocols used for in Swift?

Protocol is a very powerful feature of the Swift programming language. Protocols are used to define a “blueprint of methods, properties, and other requirements that suit a particular task or piece of functionality.”

What is the use of array in Swift function?

Swift Array provides different methods to add elements to an array. Here, append() adds 32 at the end of the array. Here, we are adding all elements of evenNumbers to primeNumbers . Note: We must use contentOf with append() if we want to add all elements from one array to another.

What is array type in Swift?

Specifically, you use the Array type to hold elements of a single type, the array's Element type. An array can store any kind of elements—from integers to strings to classes. Swift makes it easy to create arrays in your code using an array literal: simply surround a comma-separated list of values with square brackets.

Can array have different data types in Swift?

Swift Array – With Elements of Different TypesIn Swift, we can define an array that can store elements of any type. These are also called Heterogenous Collections. To define an array that can store elements of any type, specify the type of array variable as [Any].


You've hit a variant of a problem with protocols in Swift for which no good solution exists yet.

See also Extending Array to check if it is sorted in Swift?, it contains suggestions on how to work around it that may be suitable for your specific problem (your question is very generic, maybe you can find a workaround using these answers).


You want to create a generic class, with a type constraint that requires the classes used with it conform to SomeProtocol, like this:

class SomeClass<T: SomeProtocol> {
    typealias ElementType = T
    var protocols = [ElementType]()

    func addElement(element: ElementType) {
        self.protocols.append(element)
    }

    func removeElement(element: ElementType) {
        if let index = find(self.protocols, element) {
            self.protocols.removeAtIndex(index)
        }
    }
}

In Swift there is a special class of protocols which doesn't provide polymorphism over the types which implement it. Such protocols use Self or associatedtype keywords in their definitions (and Equatable is one of them).

In some cases it's possible to use a type-erased wrapper to make your collection homomorphic. Below is an example.

// This protocol doesn't provide polymorphism over the types which implement it.
protocol X: Equatable {
    var x: Int { get }
}

// We can't use such protocols as types, only as generic-constraints.
func ==<T: X>(a: T, b: T) -> Bool {
    return a.x == b.x
}

// A type-erased wrapper can help overcome this limitation in some cases.
struct AnyX {
    private let _x: () -> Int
    var x: Int { return _x() }

    init<T: X>(_ some: T) {
        _x = { some.x }
    }
}

// Usage Example

struct XY: X {
    var x: Int
    var y: Int
}

struct XZ: X {
    var x: Int
    var z: Int
}

let xy = XY(x: 1, y: 2)
let xz = XZ(x: 3, z: 4)

//let xs = [xy, xz] // error
let xs = [AnyX(xy), AnyX(xz)]
xs.forEach { print($0.x) } // 1 3

The limited solution that I found is to mark the protocol as a class-only protocol. This will allow you to compare objects using '===' operator. I understand this won't work for structs, etc., but it was good enough in my case.

protocol SomeProtocol: class {
    func bla()
}

class SomeClass {

    var protocols = [SomeProtocol]()

    func addElement(element: SomeProtocol) {
        self.protocols.append(element)
    }

    func removeElement(element: SomeProtocol) {
        for i in 0...protocols.count {
            if protocols[i] === element {
                protocols.removeAtIndex(i)
                return
            }
        }
    }

}

The solution is pretty simple:

protocol SomeProtocol {
    func bla()
}

class SomeClass {
    init() {}

    var protocols = [SomeProtocol]()

    func addElement<T: SomeProtocol where T: Equatable>(element: T) {
        protocols.append(element)
    }

    func removeElement<T: SomeProtocol where T: Equatable>(element: T) {
        protocols = protocols.filter {
            if let e = $0 as? T where e == element {
                return false
            }
            return true
        }
    }
}