I am having some trouble using .map on a collection in Swift 4.2. Here is what is happening:
I have a property declared like this:
var cbPerifList:[CBPeripheral]!
Later in the code when I write:
_ = cbPerifList.map { (perif) -> Void in
print(perif.identifier)
}
The compiler seems happy and does not complain.
But when I write:
_ = cbPerifList.map { (perif) -> Void in
print(perif.identifier)
print("HELLO")
}
The compiler complains by displaying this message.
Value of type '[CBPeripheral]' has no member 'identifier'
on the line of code: print(perif.identifier)
Can someone explain what is going on here?
I don't see why adding this line of code makes a difference.
print("HELLO")
I expect perif to be of type CBPeripheral and not [CBPeripheral] as the error message says.
As mentioned in the other answer and in the comments, using forEach or for ... in would be the proper way of traversing through the array.
But to answer your question and to explain why those seemingly identical code snippets behave differently: That is caused by a combination of several facts:
[CBPeripheral]! is an implicitly unwrapped optional (IUO), and since Swift 3 those are treated like a regular optional if possible. Compare
func map<U>(_ transform: (Wrapped) throws -> U) rethrows -> U?
The bodies of multi-statement closures don't participate in type inference.
In the “single-statement” closure
_ = cbPerifList.map { (perif) -> Void in
print(perif.identifier)
}
the compiler tries to infer the closure type from the print expression, and in order to type check perif.identifier, the IUO cbPerifList must be unwrapped to an array.
In the “multi-statement” closure
_ = cbPerifList.map { (perif) -> Void in
print(perif.identifier)
print("HELLO")
}
the compiler does not try to infer the closure type from the context. Therefore cbPerifList is treated like a regular optional, and map() is the above-mentioned Optional.map method. Inside the closure, perif has the unwrapped type [CBPeripheral]. That is why perif.identifier does not compile with the error message
Value of type '[CBPeripheral]' has no member 'identifier'
You can “help” the compiler with an explicit type annotation
_ = cbPerifList.map { (perif: CBPeripheral) -> Void in
print(perif.identifier)
print("HELLO")
}
but – as already said – in this particular case there are better solutions.
The way map works in Swift (and elsewhere) is that it can map (change) input from one type to another. So in your case, you will call map on your [CBPeripheral] and for each element, the map function will return another value. So, if the identifier is a String, you will end out with an array of Strings.
That is how it should work.
However :)
In your case, you have defined your mapping function like so
(perif) -> Void
Meaning that you will run through your array and call map, with a function that returns nothing. Meaning that the outcome will be an array filled with Void values.
Therefore, as others are suggesting in the comments, if you just want to examine the content of your cbPerifList, you are better of using a for in loop like so
for cbPerif in cbPerifList {
print(cbPerif.identifier)
}
If however, you're interested in getting an array of the names of your CBPeripherals you can do something like this
let names = cbPerifList.map { $0.identifier }
A comment to the above: To read more about how we can end up just writing { $0.name } have a look at the chapter about Closures in the Swift documentation, or this answer
Hope that helps you.