TL;DR - It doesn't work because an array of Attribute
s cannot be assigned to an array of String
s, they are both mismatched types, and Swift does not do automatic conversion between types, and an explict conversion needs to be specified.
In Swift, when you initialise an array using an array literal, the following happens under the hood:
let words = ["hello", "world"]
- The compiler recognises that an array literal is being assigned to a variable named
words
. Since we have not specified the type of the words
, implicitly an array is assumed. The type of the elements underlying the array is determined based on the contents of the array literal.
- In this case, the array literal is a collection of
String
types; this is easily understood by the compiler
- Since the LHS type is an array, the RHS structure is an array literal, and since the LHS type (
Array
) conforms to a pre-defined protocol called ExpressibleByArrayLiteral
which has an associated type constraint to match Element
, the compiler will actually be converting our line to the following
Example:
let words = [String].init(arrayLiteral: ["hello", "world"]) // we do not call this init directly
This is how initialising with array literals work. In the above example, since we did not specify the type of array, the implicit type setting will work. If we specified a mismatched type, the assignment will fail, since ExpressibleByArrayLiteral
requires the associated Element
type of the array literal and the actual array you are assigning to to match.
So the following fails:
let words:[String] = [1, 2] // array literal has Element=Int, array has Element=String
This also shows that there is no implicit type conversion between Int
and String
, even though Int
conform to CustomStringConvertible
.
In your case, you are trying to assign an array literal consisting of Attributes
to an array of String
. This is a type mismatch. This is the reason it fails.
If you state protocol conformance, the following line will work:
var attributesList: [CustomStringConvertible] {
return [
Attributes.eventDate,
Attributes.eventName,
Attributes.eventType,
Attributes.country
]
}
// note that we have an array of CustomStringConvertible protocol,
// each element here is still of Attributes type
// a type conforming to a protocol can be cast to an instance
// of that protocol automatically
// The above initialisation works simply because the following
// also works without any further action required
// let a:CustomStringConvertible = Attributes.country
If you really want a list of string values, you need to map this to a string explicitly:
var attributesList1: [String] {
return [
Attributes.eventDate,
Attributes.eventName,
Attributes.eventType,
Attributes.country
].map { $0.description }
}
var attributesList2: [String] {
return [
Attributes.eventDate.description,
Attributes.eventName.description,
Attributes.eventType.description,
Attributes.country.description
]
}