'Swift: Extend Optional Array

I'd like to extend Optional, where Wrapped is an array (with elements of any type). In other words, I want to add a function to [Any]?. I'm just not sure how to declare that. I've tried:

1.

extension Optional where Wrapped: Array

results in:

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

2.

extension Optional where Wrapped: Array<Any>

results in:

type 'Wrapped' constrained to non-protocol type 'Array<Any>'

and various other similar declarations. Help is appreciated.



Solution 1:[1]

I am kinda late, but maybe I can help.

In swift 4, you can do this

extension Optional where Wrapped: Collection {

If you need to access the Element type of the array you can do:

Wrapped.Iterator.Element

Solution 2:[2]

Yes the problem here is that in this line,

extension Optional where Wrapped: Array<Any>

the 'Array' should be a protocol, in a more general way :

extension Type where Element: Protocol 

The solution I got is ugly but works, consider this:

protocol ArrayOfAny {}
struct ArrayAny<Element: Any>: ArrayOfAny {
    let array: [Element]
    init(_ array: [Element]) {
        self.array = array
    }
}    

Because this:

extension Array: ArrayOfAny where Element: Any {}

is not supported...

then you can just do this:

extension Optional where Wrapped: ArrayOfAny

Hope it helps :)

Solution 3:[3]

A bit late to the party but it still might help future readers. Using Wrapped RangeReplaceableCollection to support strings as well:


extension Optional where Wrapped: RangeReplaceableCollection {
    mutating func append(_ element: Wrapped.Element) {
        self = (self ?? .init()) + CollectionOfOne(element)
    }
    mutating func append<S: Sequence>(contentsOf sequence: S) where S.Element == Wrapped.Element {
        self = (self ?? .init()) + sequence
    }
}

var optionalString: String?
optionalString.append("a")
optionalString  //  "a"
optionalString.append(contentsOf: "bc")
optionalString  //  "abc"
optionalString.append(contentsOf: ["d", "e"])
optionalString  //  "abcde"
var optionalArray: [Int]?
optionalArray.append(1)
optionalArray  //  [1]
optionalArray.append(contentsOf: [2,3])
optionalArray  //  [2,3]

Solution 4:[4]

A better solution for this problem is using generic functions on Optional like this (here you are only adding functions to Optional if it really is an Array):

extension Optional {
    mutating func appendAndSetIfNil<E>(_ element: Wrapped.Element) where Wrapped == [E] {
        self = (self ?? []) + [element]
    }

    mutating func appendAndSetIfNil<S>(contentsOf newElements: S) where S: Sequence, Wrapped == [S.Element] {
        self = (self ?? []) + newElements
    }
}

This is just an example where the append function basically has been added conditionally to Optional if its Wrapped Type is an Array. In this case here the value is changed from nil to [] before appending.

But you can do anything here!

Be aware that this approach does not work in all circumstances, but in this one, it works!

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Tiago Almeida
Solution 2
Solution 3
Solution 4 Fab1n