Reputation: 11577
I have an enumeration with values:
enum Types {
case A
case B
case C
case D
}
var tableViewDataSource: [Types] = [.A, .B, .C, .D]
I want to implement the following conditions:
let pickerSelectingFields: [Types] = [.A, .B, .C, .D]
let indexes = pickerSelectingFields.map { tableViewDataSource.firstIndex(of: $0) }
if indexes.contains(textField.tag) {
// Working
}
When I try to make the whole things in a single line, as below it shows error:
Anonymous closure argument not contained in a closure
Code is below:
if ([.A, .B, .C, .D] as? [Types]).map { tableViewDataSource.firstIndex(of: $0) }
.contains(textField.tag)
What am I doing wrong here?
Upvotes: 0
Views: 105
Reputation: 154691
if ([.A, .B, .C, .D] as? [Types]).map { tableViewDataSource.firstIndex(of: $0) }
.contains(textField.tag)
What am I doing wrong here?
There are two issues.
First, you used as?
instead of as
. When you use the conditional cast as?
, the result is [Types]?
: an optional [Types]
. Then Swift uses the optional version of map
and you're majorly headed off in the wrong direction.
You needed to use as [Types]
because you are simply telling Swift to interpret [.A, .B, .C, .D]
as [Types]
.
The second issue is that since you're doing this in a single line, you need some extra parentheses (
, )
around the closure for map
because Swift doesn't like multiple {
after an if
. Without clarifying parens, it will interpret the first {
which belongs to the closure for map
as the start of then block for the if
.
So:
if ([.A, .B, .C, .D] as [Types]).map({ tableViewDataSource.firstIndex(of: $0) }).contains(textField.tag) {
// do something
}
will work.
You can also just explicitly type one of the entries of the array, and Swift will interpret the entire array to be [Types]
like so:
if [Types.A, .B, .C, .D].map({ tableViewDataSource.firstIndex(of: $0) }).contains(textField.tag) {
// do something
}
Note:
It is common Swift convention to start class
, struct
, and enum
type names with uppercase letters, and to start variables, methods, and enum values with lowercase letters.
So your enum
could be written as:
enum Types {
case a, b, c, d
}
Upvotes: 2
Reputation: 86671
Anonymous closure argument not contained in a closure
This error occurs because you cannot use trailing block syntax in the condition of an if statement. When the compiler sees the opening brace {
, it assumes it has found the block to be executed when the if
condition is true. $0
makes no sense in that context.
To fix it, you must use the non trailing block syntax i.e. put the parentheses for the map
function call in.
if ([.A, .B, .C, .D] as? [Types]).map ({ tableViewDataSource.firstIndex(of: $0)}).contains(textField.tag)
// ^- here ^-- and here
{
// Do stuff
}
That will leave you with this error:
type of expression is ambiguous without more context
That's because of the as?
. By putting the question mark there, you tell the compiler you are not sure that the array can be converted to an array of Types
and it therefore assumes it does not have enough information to infer the type. Take out the question mark and the compiler knows it has to be an array of Types
and can therefore infer the type of the expression correctly.
In fact, if you only have one enum with those members, you might find you don't need the cast:
if [.A, .B, .C, .D].map ({ tableViewDataSource.firstIndex(of: $0) }).contains(1)
{
// do something
}
compiles and runs fine in a playground.
Upvotes: 0
Reputation: 16784
The problem in second line is implicit type which Swift complier can not determine. In short doing ([.A, .B, .C, .D] as? [Types])
does nothing good, you need to be explicit in every type: ([Types.A, Types.B, Types.C, Types.D])
. Now Swift compiler can interpret this as [Types]
.
You don't need it in the first example because you used explicit type:
let pickerSelectingFields: [Types] = [.A, .B, .C, .D]
But you would get the same issue by doing it implicitly:
let pickerSelectingFields: = [.A, .B, .C, .D]
And you could again fix it by giving a hint to compiler:
let pickerSelectingFields: = [Types.A, Types.B, Types.C, Types.D]
It might be a bit hard to explain/understand but I hope this clears a few things.
Also a bracket is missing so the final result should be:
if (([Types.A, Types.B, Types.C, Types.D]).map { tableViewDataSource.firstIndex(of: $0) }).contains(textField.tag) {
}
Upvotes: 1
Reputation: 52033
One solution is to make the enum implement CaseIterable
enum Types: CaseIterable {
case A
case B
case C
case D
}
Then you can check for the presence of the tag in the array like this
if textField.tag >= 0 && textField.tag < Types.allCases.count { //maybe not needed
if tableViewDataSource.contains(Types.allCases[textField.tag]) {
//do stuff
}
}
Another option is to make the enum of type Int
enum Types: Int{
case A = 1
case B
case C
case D
}
And then check directly using the tag
if let type = Types(rawValue: textField.tag) {
if tableViewDataSource.contains(type) {
//do stuff
}
}
Upvotes: 1