meaning-matters meaning-matters - 2 months ago 5x
Swift Question

Confusing error: Optionality of map's variable changes it from single object to array

I have:

private var wrappedObjects: [WrapperClass]?

var objects: [SomeClass]?
self.wrappedObjects ={ WrapperClass($0) }

This results in the following error:

`Cannot convert value of type '[SomeClass]' to expected argument type 'SomeClass'`

However when I just change one line to:

var objects: [SomeClass] = []

the error is gone.

Why does the optionality of
is either a single
or an array


The problem here is that there are two map(_:) functions. One for sequences:

extension SequenceType {

    // ...

    /// Returns an `Array` containing the results of mapping `transform`
    /// over `self`.
    /// - Complexity: O(N).
    public func map<T>(@noescape transform: (Self.Generator.Element) throws -> T) rethrows -> [T]

One for optionals:

public enum Optional<Wrapped> : _Reflectable, NilLiteralConvertible {

    // ...

    /// If `self == nil`, returns `nil`.  Otherwise, returns `f(self!)`.
    public func map<U>(@noescape f: (Wrapped) throws -> U) rethrows -> U?

Therefore when you call map on a [SomeClass]?, the second map function will be used, where the transformation function parameter will be of type [SomeClass], as map will unwrap it for you, and apply a given transformation to it.

However, when you call map on a [SomeClass], the first map function will be used, where the elements will be iterated through – applying the transformation function to each of them. Therefore the parameter type of the transformation function will be SomeClass.

One amusing solution therefore would be to use map twice – once to unwrap, once to apply a transform to the elements:

self.wrappedObjects ={ ${ WrapperClass($0) } }

However, this is absolutely ridiculous, you should definitely use optional chaining as Rob suggests.