Have you been programming in Swift for a while and aiming to be better at it? You're at the right place! These tips, if followed wholeheartedly, shall take you a step ahead in Swift Programming.
Feel free to copy and paste the code snippets into Playground. To make it easier for you to understand, I have included several YouTube video links as part of the explanations.
1. For Loop vs. While Loop
Let’s take the below code which prints “Hello” 5 times as an example.
var i = 0
while 5 > i {
print("Hello")
i += 1
}
You initialized the variable “i” and incremented it once after each iteration to ensure that the text is printed the exact number of times you want it to. What if I say you are better without it?
for _ in 1...5 {
print("Hello")
}
The above code does the same task but without any variables.
Rule of Thumb: The greater the number of variables your code has, the higher the chances of code bugs! Never forget the Butterfly effect. It is always better to follow the KISS Methodology (Keep It Simple, Stupid!).
YouTube Life Vest: For Loop Tutorial
2. Functional Programming
Functional programming can make you look smarter in the programming world. Have a look at the snippet below.
var newEvens = [Int]()
for i in 1...10 {
if i % 2 == 0 {
newEvens.append(i)
}
}
print(newEvens) // [2, 4, 6, 8, 10]
It is a simple program printing all the even numbers between 1 and 10: an age-old imperative approach to coding. Want to see the beauty of functional programming? Here it is:
var evens = Array(1...10).filter {
$0 % 2 == 0
}
print(evens) // [2, 4, 6, 8, 10]
That’s way simpler and better
3. Enum Type Safe
The very first tip said the lesser you write, the easier your life. When it comes to switching statements, for example, introducing enums can be a life saver. For example:
switch person {
case "Adult":
print("Pay $7")
case "Child":
print("Pay $3")
case "Senior":
print("Pay $4")
default:
print("Bad Choice!")
}
Here, you are hard coding the values for each case — Adult, Child, and Senior. Let's now see how enums can add beauty to your code.
enum People {
case adult, child, senior
}
var person = People.adult
switch person {
case .adult:
print("Pay $7")
case .child:
print("Pay $3")
case .senior:
print("Pay $4")
}
The chances of you making a mistake are highly reduced with enums.
YouTube Life Vests:
4. Optional Unwrapping
Nested statements can be a nightmare — an endless staircase of complex code! This is where the guard statement comes to the rescue. It can check one or more condition in a single statement, and if any condition is not satisfied, an else block will be called which transfers the control of the program.
Have a look at the clutter that the nested statement causes.
var myUsername: Double ?
var myPassword: Double ?
func userLogIn() {
if let username = myUsername {
if let password = myPassword {
print("Welcome, \(username)"!)
}
}
}
Guard statement, on the other hand, makes it a lot cleaner:
func userLogIn() {
guard
let username = myUsername,
let password = myPassword
else {
return
}
print("Welcome, \(username)!")
}
Pretty, isn’t it?
5. Generics
Code reusability is the key to be a better programmer. Generics come in handy when avoiding duplication and providing reusable pieces of code. Let’s go through the below code fragments to understand the difference between a redundant code and a code with no duplication.
var stringArray = ["Bob", "Bobby", "SangJoon"]
var intArray = [1, 3, 4, 5, 6]
var doubleArray = [1.0, 2.0, 3.0]
func printStringArray(a: [String]) {
for s in a {
print(s)
}
}
func printIntArray(a: [Int]) {
for i in a {
print(i)
}
}
func printDoubleArray(a: [Double]) {
for d in a {
print(d)
}
}
Example 1
func printElementFromArray < T > (a: [T]) {
for element in a {
print(element)
}
}
Example 2
func printElementFromArray < T > (a: [T]) {
for element in a {
print(element)
}
}
Example 2 shows you how simply can you write a program to perform a certain action without unnecessarily introducing so many lines of duplicate code, as evident in Example 1.
YouTube Life Vest: Generic Tutorial
6. Extension
Suppose you were writing a program to print the square of a number. The chances are high that you end up with something which looks like :
func square(x: Int) - > Int {
return x * x
}
var squaredOFFive = square(x: 5)
square(x: squaredOFFive) // 625
One simple rule to making your life easier: lesser typing equals lesser errors, equals working code, equals your happiness!
Get rid of the unnecessary variables as much as possible:
extension Int {
var squared: Int {
return self * self
}
}
5. squared // 25
5. squared.squared // 625
Short and simple!
YouTube Life Vest: Extension tutorial
7. Computed Property vs. Function
The more connected your code is, the lesser typing, typo mistakes, and errors you will have to remedy. If your code has parts which can be interdependent, do not introduce unnecessary mutually exclusive lines of code. Keep it as much connected as possible.
func getDiameter(radius: Double) - > Double {
return radius * 2
}
func getRadius(diameter: Double) - > Double {
return diameter / 2
}
getDiameter(radius: 10) // return 20
getRadius(diameter: 200) // return 100
getRadius(diameter: 600) // return 300
Here, radius and diameter can be interdependent but still have two mutually exclusive getters for no reason at all! Let’s look at a better version:
var radius: Double = 10
var diameter: Double {
get {
return radius * 2
}
set {
radius = newValue / 2
}
}
radius // 10
diameter // 20
diameter = 1000
radius // 500
Life sorted!
8. Defer
A defer block contains code which will be called only when all the other code in the current scope has ended execution. It “defers” the code execution to a certain point. This is a cleaner approach and helps reduce redundancy.
For example :
func process(fileName: String) throws {
if exists(fileName) {
let file = open(fileName)
defer {
close(file)
}
let userName = getUserName()
guard userName != ""
else {
return
}
file.write(userName)
let userAge = getUserName()
guard userAge >= 18
else {
return
}
file.write(userAge)
// close(file) is called here, at the end of the scope.
}
}
9. Nil Coalescing
The Nil Coalescing operator is a ternary operator that you can use in your code while working with arbitrary values.
For instance, the user choosing a theme color for Twitter:
var userChosenColor: String ?
var defaultColor = "Red"
var colorToUse = ""
if let Color = userChosenColor {
colorToUse = Color
} else {
colorToUse = defaultColor
}
Always dread long code! They will kill your happiness.
Here is how Nil Coalescing works:
var colorToUse = userChosenColor ? ? defaultColor
If the userChosenColor is nil, it assigns the
default color.
10. Closure vs. Function
Function name, variables, return types, etc, etc… Too much to handle, right? But imagine this — with Closures, you would only have to remember one thing.
A program with a normal function definition would look similar to the one below:
func sum(x: Int, y: Int) - > Int {
return x + y
}
var result = sum(x: 5, y: 6) // 11
And this is how it would look like with Closures:
var sumUsingClosure: (Int, Int) - > (Int) = {
$0 + $1
}
sumUsingClosure(5, 6) // 11
You just need to remember the variable name. Pretty awesome!
YouTube Life Vest: Closure Tutorial
11. Conditional Coalescing
You can very well use the nil coalescing ternary operator for condition based programming as well.
The below code fragments will make it easier for you to understand.
var currentHeight = 185
var hasSpikyHair = true
var finalHeight = 0
if hasSpikyHair {
finalHeight = currentHeight + 5
} else {
finalHeight = currentHeight
}
Unwanted clutter! Let’s clean it up :
finalHeight = currentHeight + (hasSpikyHair ? 5 : 0)
If hasSpikyHair will be true, 5 will be added to the currentHeight else 0 will be added. Simply and tidy.
Do you think you have some hooks which can uplift the skills of other Swift Developers? Why not share it in the comments section below!