Legacy API and Value Extractions
Oftentimes, when you get data from an external source, like a library,
or an API, it is not only good practice but usually even required that
you check the data for consistency before interpreting it. You need to
make sure that all keys exists or that the data is of the correct type,
or the arrays have the required length. Not doing so can lead from buggy
behaviour (missing key) to crash of the app (indexing non-existent array
items). The classic way to do this is by nesting if
statements.
Let's imagine an API that returns a user. However, there're two types of users: System users - like the administrator, or the postmaster - and local users - like "John B", "Bill Gates", etc. Due to the way the system was designed and grew, there're a couple of nuisances that API consumers have to deal with:
system
andlocal
users come via the same API call.- the
department
key may not exist, since early versions of the db did not have that field and early employees never had to fill it out. - the
name
array contains either 4 items (username, middlename, lastname, firstname) or 2 items (full name, username) depending on when the user was created. - the
age
is an Integer with the age of the user
Our system needs to create user accounts for all system users from this API with only the following information: username, department. We only need users born before 1980. If no department is given, "Corp" is assumed.
func legacyAPI(id: Int) -> [String: Any] {
return [\"type\": \"system\", \"department\": \"Dark Arts\", \"age\": 57,
\"name\": [\"voldemort\", \"Tom\", \"Marvolo\", \"Riddle\"]]
}
Given these constraints, let's develop a pattern match for it:
let item = legacyAPI(4)
switch (item[\"type\"], item[\"department\"], item[\"age\"], item[\"name\"]) {
case let (sys as String, dep as String, age as Int, name as [String]) where
age < 1980 &&
sys == \"system\":
createSystemUser(name.count == 2 ? name.last! : name.first!, dep: dep ?? \"Corp\")
default:()
}
// returns (\"voldemort\", \"Dark Arts\")
Note that this code makes one dangerous assumption, which is that if the name array does not have 2 items, it must have 4 items. If that case doesn't hold, and we get a zero item name array, this would crash.
Other than that, it is a nice example of how pattern matching even with just one case can help you write cleaner code and simplify value extractions.
Also, see how we're writing let
at the beginning right after the
case, and don't have to repeat it for each assignment within the case.