Swift is one of the fastest-growing languages in history, due to its elegance, simplicity, and “safety by design”. In fact, Swift’s official mantra is “to make programming simple things easy, and difficult things possible”. In this post, you’ll learn how to use Swift to its fullest by refactoring your code.
While a lot of the code optimization is implicit and obviously inherent in the language’s design, there are certain refactoring strategies that can make your code more readable, more reliable, and better performing. In this article, you will learn eight ways to refactor your code with Swift 4.
Objectives of This Article
In this article, you will learn some ways to better optimize and refactor your code with Swift 4. We’ll cover the following:
- handling duplicate keys in dictionaries elegantly with
zip
- setting default values for dictionaries
- merging dictionaries into one
- filtering data dictionary values directly into another dictionary object
- using Codable to save custom objects into JSON
- swapping values in mutable arrays
- handling multi-line literals
- finding random elements in a collection
1. Duplicate Keys in Dictionaries
First up, Swift 4 further enhances dictionaries with an elegant way to handle duplicate keys using the generic function zip
. zip
works on more than just dictionaries, and in fact it will let you build own sequence type from two underlying collections that conform to Sequence.
For example, say you have an array with the following values, taking note of two elements containing the same key:
let locations = [ "Springfield", "Chicago", "Springfield", "Jackson"] var dictionary: Dictionary<String, String>
By using zip
, you are able to create a sequence of unique pairs:
let locations = [ "Springfield", "Chicago", "Springfield", "Jackson"] var dictionary = Dictionary(zip(locations, repeatElement(1, count: locations.count)), uniquingKeysWith: +) print(dictionary) // ["Jackson": 1, "Chicago": 1, "Springfield": 2]
The uniquingKeysWith
elements in the code above allow you to create unique values through the use of a mathematical operator. In this case, we use +
to increment the value whenever a duplicate entry is found. Of course, you can also decide to employ your own custom increment logic.
2. Default Values for Dictionaries
Another powerful feature in Swift 4 is in the ability to set default values for dictionaries, using a newly added subscript. In the following code, when you access the value of a key in the dictionary, the value returned is an optional
value, which will return nil if the key doesn’t exist:
let locationsAndPopulations = [ "Springfield": 115715, "Chicago": 2705000, "Aurora": 201110] print(locationsAndPopulations["Springfield"]) //value is optional print(locationsAndPopulations["New York"]) //this key doesn't exist, returns nil
Normally you should handle the possibility of nil
in optional values, but Swift 4 makes it a bit more convenient for you, through a new subscript which allows you to add default parameters, rather than forcing you to guard or unwrap optionals.
print(locationsAndPopulations["New York", default: 0])
In this case, since we don’t have an entry for New York in our initial array, it will return the default parameter of 0. You can also inject a dynamic value as opposed to a static literal value if you need to, which certainly makes for a much more powerful subscript feature.
3. Merging Dictionaries
Swift 4 also makes it easier to merge two dictionaries into one through the use of merge(_:uniquingKeysWith:)
. In the following example, we are merging the second dictionary into the first, and through the use of the newly learned parameter uniquingKeysWith
, we can ensure any duplicates will be handled should they occur:
var locationsAndPopulationsIL = [ “Springfield”: 115715, “Chicago”: 2705000, “Aurora”: 201110] let location2 = [ “Rockford”: 152871, “Naperville”: 141853, “Champaign”: 81055] locationsAndPopulationsIL.merge(location2, uniquingKeysWith: +) print(locationsAndPopulationsIL)
In the absence of this function, you would normally have to manually traverse all the values of a dictionary and implement custom logic in merging two dictionaries into one.
4. Filtering Dictionary Values Into Another Dictionary
In addition to merging two dictionaries, you can also dynamically filter a dictionary with the results directed back into another dictionary of the same type. In this next snippet of code, we filter the dictionary of locations by a specific value, returned as a dictionary:
let locationsAndPopulationsIL = [ "Springfield": 115715, "Chicago": 2705000, "Aurora": 201110] let filteredmillionPlusPopulation = locationsAndPopulationsIL.filter{ $0.value > 1000000 } print(filteredmillionPlusPopulation) //["Chicago": 2705000]
So, beyond simple filtering, you can also make use of the filter
closure to provide custom filters that will culminate in a new dictionary result.
5. Save Custom Objects to JSON
If you’ve ever serialized (and deserialized) data before, it can be quite involved, having to subclass classes with NSObject
, as well as implementing NSCoding
. With Swift 4, you can more efficiently serialize your classes through the use of Codable
. This is especially useful when you want to persist by serializing a custom object of yours into a JSON object, either to pass to an API or even to persist locally using UserDefaults
:
// a class to serialize class Location: Codable{ var name: String var population: Int init(name: String, population: Int){ self.name = name self.population = population } } //create an instance let chicago = Location(name: "chicago", population: 2705000) //serialize to JSON let jsonData = try? JSONEncoder().encode(chicago) //deserialize and output if let data = jsonData{ let jsonString = String(data: data, encoding: .utf8) print(jsonString!) //{"name":"chicago","population":2705000} }
As you can see, by setting your class or struct to Codable
, you can easily serialize your data to JSON, persist the data somewhere, and deserialize back.
6. Swapping Values in Mutable Arrays
Switching over to arrays, another welcome feature in Swift 4 is the ability to swap two values directly in mutable arrays, by using swapAt(_:_:)
. This would be most useful for functions such as sorting algorithms, and it’s extremely straightforward to use:
var locations = [ "Springfield", "Chicago", "Champaign", "Aurora"] locations.swapAt(0, 2) print(locations) //["Champaign", "Chicago", "Springfield", "Aurora"]
Previously, you would have had to make use of temporary variables to swap between two element locations, but with this method, you can more concisely sort your arrays.
7. Handling Multi-Line Literals
Another cool addition to Swift 4 is the ability to store multiple-line string literals in your values, making it extremely easy to break out your literal content to be more presentable. Through the use of the notation """
to open and close the text block, you can create multi-line content and even reference dynamic variables, as shown below when we dynamically add a date.
let illinoisIntro = """ Illinois is a state in the Midwestern United States. It is the 5th most populous and 25th largest state, and is often noted as a microcosm of the country. With Chicago and its suburbs in the northeast, small industrial cities and great agricultural productivity in central and northern Illinois, and natural resources like coal, timber, and petroleum in the south, Illinois has a diverse economic base and is a major transportation hub. (Source: Wikipedia - Dated (Date()) """ print(illinoisIntro)
8. Picking Random Elements in a Collection
New in Swift 4.2 is the ability to pick random elements in a collection with the randomElement
function. In fact, not just arrays but any object that conforms to the Collection
protocol can make use of this nifty function. The following example uses our location array, and it will print out a random city from that array:
let locations = [ "Springfield", "Chicago", "Springfield", "Jackson"] let randomElement = locations.randomElement() print(randomElement!)
Conclusion
In this article, you learned some useful techniques that Swift 4 brings to help create more compact, focused, and optimized code. In its fourth iteration, Swift has certainly made strides in helping you refactor your code to be more readable and reliable. Swift 4.x isn’t done yet, and I encourage you to follow the official Swift evolution page to take note of all the new proposals in the pipeline to be discussed.