Eliasz Sawicki

Eliasz Sawicki

iOS developer from Gdansk

Expressible by array literal

- 3 mins

I recently noticed a pretty nice mechanism which may come in handy in some situations ;)

kitty

Let’s imagine we have a Team structure that needs a configuration in order to be initialized. If we want to get some information about the team, we will simply call team.info().

typealias Player = String
struct Team {

    let configuration: [Player]

    init(configuration: [Player]) {
        self.configuration = configuration
    }

    func info() {
        let names = configuration.reduce(into: "") { 
            (result: inout String, player: Player) in
            if result.isEmpty {
                result += player
            } else {
                result += ", \(player)"
            }
        }
        print("The team structure is: \(names)")
    }
}

let team = Team(configuration: ["Foo", "Bar"])
print(team.info()) // The team structure is: Foo, Bar

But at some point it turns out, that we would like to create another team object, however its configuratin is unknown. We probably shouldn’t initialize it with an empty array of players (let team = Team(configuration: [])) as this could be reserved for a team without any players at all. What can we do about it?

ExpressibleByArrayLiteral

As we already have an initializer that we use in many different places which we don’t want to change, we can use a protocol called ExpressibleByArrayLiteral. This will allow us to create an enum like:

enum Configuration: ExpressibleByArrayLiteral {

    case players([Player])
    case unknown

    typealias ArrayLiteralElement = Player

    init(arrayLiteral elements: ArrayLiteralElement...) {
        self = .players(elements)
    }

}

Now we can replace the constructor of a Team object with
init(configuration: Configuration) and use it this way:
let unknownTeam = Team(configuration: .unknown). Keep in mind, that we do not need to change the code that created the old team
let myTeam = Team(configuration: ["Foo", "Bar"]), because an array will be translated to our enum as case players([Player]).

struct Team {

    let configuration: Configuration

    init(configuration: Configuration) {
        self.configuration = configuration
    }

    func info() {
        switch configuration {
        case .players(let players):
            let names = players.reduce(into: "") { 
                (result: inout String, player: Player) in
                result += player + " "
            }
            print("The team structure is: \(names)")
        case .unknown: print("Unknown team configuration")
        }
    }
}

let myTeam = Team(configuration: ["Foo", "Bar"])
let unknownTeam = Team(configuration: .unknown)

print(myTeam.info()) // The team structure is: Foo, Bar
print(unknownTeam.info()) // Unknown team configuration

If you find this interesting, then take a look at the official docs or ExpressibleByStringLiteral, ExpressibleByNilLiteral, ExpressibleByBooleanLiteral, ExpressibleByFloatLiteral and other ExpressibleBy*Literal protocols ;)

comments powered by Disqus
rss facebook twitter github youtube mail spotify lastfm instagram linkedin google google-plus pinterest medium vimeo stackoverflow reddit quora