Tips to become a better Swift (iOS) Developer

Swift is friendly to new programmers. It's an industrial-quality programming language that's as expressive and enjoyable as a scripting language. Here the some tips to become a better Swift Developer. You can copy and paste the code snippets into Playground, to make it easier for you to understand -

1. For loop with Half-open range

The half-open range operator (a ..< b) defines a range that runs from a to b, but doesn’t include b. The half-open range operator also has a one-sided form that’s written with only its final value.

1.1 With Half-open range operator (or 😎 Code)

let names = ["Anna", "Alex", "Brian", "Jack"]
for name in names[..<2] {
    print(name)
}
/* prints -
Anna
Alex */

for name in names[1..<3] {
    print(name)
}
/* prints -
 Alex
 Brian
 */

1.2 Without Half-open range operator (or 💩 Code)

let names = ["Anna", "Alex", "Brian", "Jack"]
for (index, name) in names.enumerated(){
    if index < 2 {
        print(name)
    }
}
/* prints -
 Anna
 Alex */

or with while loop (still 💩 Code)

let names = ["Anna", "Alex", "Brian", "Jack"]
var index = 0
while index < 2 {
    print(names[index])
    index += 1
}
/* prints -
 Anna
 Alex */

2. For loop with Closed range

The closed range operator (a...b) defines a range that runs from a to b, and includes the values a and b. The value of a must not be greater than b. The closed range operator has an alternative form for ranges that continue as far as possible in one direction.

2.1 With Closed range operator (or ❤️ Code)

let names = ["Anna", "Alex", "Brian", "Jack"]
for name in names[...2] {
    print(name)
}
/* prints -
 Anna
 Alex
 Brian
 */

for name in names[1...2] {
    print(name)
}
/* prints -
 Alex
 Brian
 */

2.2 Without Closed range operator (or 💩 Code)

let names = ["Anna", "Alex", "Brian", "Jack"]
for (index, name) in names.enumerated(){
    if index >= 1 && index <= 2 {
        print(name)
    }
}
/* prints -
 Alex
 Brian
 */

3. Subscripts

Classes, structures, and enumerations can define subscripts, which are shortcuts for accessing the member elements of a collection, list, or sequence. You use subscripts to set and retrieve values by index without needing separate methods for setting and retrieval. For example, you access elements in an Array instance as someArray[index].

The following example defines a Matrix structure, which represents a two-dimensional matrix of Double values with and without subscripts :

3.1 Without Subscripts (or Okay Version)

struct Matrix {
    let rows: Int, columns: Int
    var grid: [Double]
    init(rows: Int, columns: Int) {
        self.rows = rows
        self.columns = columns
        self.grid = Array(repeatElement(0.0, count: rows * columns))
    }
    
    func getValue(row: Int, column: Int) -> Double{
        return grid[(row * columns) + column]
    }
    
    mutating func setValue(row: Int, column: Int, value: Double){
        grid[(row * columns) + column] = value
    }
}

var matrix = Matrix(rows: 2, columns: 2)
matrix.setValue(row: 0, column: 0, value: 1.0)
matrix.setValue(row: 0, column: 1, value: 2.0)
matrix.setValue(row: 1, column: 0, value: 3.0)
matrix.setValue(row: 1, column: 1, value: 4.0)

print(matrix.getValue(row: 0, column: 0)) //prints "1.0"
print(matrix.getValue(row: 0, column: 1)) //prints "2.0"
print(matrix.getValue(row: 1, column: 0)) //prints "3.0"
print(matrix.getValue(row: 1, column: 1)) //prints "4.0"

3.2 With Subscripts (or Better Version)

struct Matrix {
    let rows: Int, columns: Int
    var grid: [Double]
    init(rows: Int, columns: Int) {
        self.rows = rows
        self.columns = columns
        self.grid = Array(repeatElement(0.0, count: rows * columns))
    }
    subscript(row: Int, column: Int) -> Double {
        get {
            return grid[(row * columns) + column]
        }
        set {
            grid[(row * columns) + column] = newValue
        }
    }
}

var matrix = Matrix(rows: 2, columns: 2)

matrix[0,0] = 1.0
matrix[0,1] = 2.0
matrix[1,0] = 3.0
matrix[1,1] = 4.0

print(matrix[0,0]) //prints "1.0"
print(matrix[0,1]) //prints "2.0"
print(matrix[1,0]) //prints "3.0"
print(matrix[1,1]) //prints "4.0"

4. Function vs Computed Property

4.1 Function (or 💩 Code)

As you know, functions are self-contained chunks of code that perform a specific task. Here's an example of a function implementation, this example find the diameter of a circle from circle radius or visa-versa :

func getDiameter(radius: Double) -> Double {
    return radius * 2
}

func getRadius(diameter: Double) -> Double {
    return diameter / 2
}

print(getDiameter(radius: 100)) //prints "200"
print(getRadius(diameter: 100)) //prints "50"

4.2 Computed Property (or ❤️ Code)

Computed property provide a getter and an optional setter to retrieve and set other properties and values indirectly. Computed properties do not actually store a value. Here's the same example of with computed property, this example find the diameter of a circle from circle radius or visa-versa :

var radius: Double = 100
var diameter: Double {
    get {
        return radius * 2
    }
    set {
        radius = newValue / 2
    }
}

print(diameter) //prints "200.0"

diameter = 100
print(radius) //prints "50.0"

5. Extensions

Extensions add new functionality to an existing class, structure, enumeration, or protocol type. This includes the ability to extend types for which you do not have access to the original source code. Extensions are similar to categories in Objective-C.

Here's an example of a extension implementation, this example adds four computed instance properties to Swift's built-in Double type, to provide basic support for working with distance units (default is meters):

5.1 With Extensions (or Awesome Code)

extension Double {
    var m: Double { return self }
    var km: Double { return self * 1000.0 }
    var cm: Double { return self / 100.0 }
    var mm: Double { return self / 1000.0 }
}

let thousandCentimeter = 1000.cm
print("Thousand centimeter is \(thousandCentimeter) meters")
// Prints "Thousand centimeter is 10.0 meters"

let threeKilometer  = 3.km
print("Three km is \(threeKilometer) meters")
// Prints "Three km is 3000.0 meters"

5.2 Without Extensions (or Bad Code)

func centimeterToMeter(value: Double) -> Double{
    return value / 100.0
}

func kilometerToMeter(value: Double) -> Double{
    return value * 1000.0;
}

let thousandCentimeter = 1000.0
print("Thousand centimeter is \(centimeterToMeter(value: thousandCentimeter)) meters")
// Prints "Thousand centimeter is 10.0 meters"

let threeKilometer  = 3.0
print("Three km is \(kilometerToMeter(value: threeKilometer)) meters")
// Prints "Three km is 3000.0 meters"

6. Ternary conditional

The ternary conditional operator is a special operator with three parts, which takes the form question ? answer1 : answer2. It’s a shortcut for evaluating one of two expressions based on whether question is true or false. If question is true, it evaluates answer1 and returns its value; otherwise, it evaluates answer2 and returns its value.

Here's an example of ternary conditional operator, which change the value of rowHeight based on either that row has a header or not.

6.1 With ternary coditional operator (or ❤️ Code)

let contentHeight = 40
let hasHeader = true
let rowHeight = contentHeight + (hasHeader ? 50 : 20)
print(rowHeight)

//prints "90"

6.2 Without ternary coditional operator (or 💩 Code)

let contentHeight = 40
let hasHeader = true
var rowHeight: Int
if hasHeader == true {
    rowHeight = contentHeight + 50
} else {
    rowHeight = contentHeight + 20
}
print(rowHeight) //prints "90"

7. Nil coalescing

The nil-coalescing operator (a ?? b) unwraps an optional a if it contains a value, or returns a default value b if a is nil. The expression a is always of an optional type. The expression b must match the type that is stored inside a.

Here's an example of Nil coalescing operator, in which if userDefinedColorName returns nil, choose defaultColorName (red). If not, choose userDefinedColorName.

7.1 With Nil coalescing (or 😎 Code)

let defaultColorName = "red"
var userDefinedColorName: String?   // defaults to nil

var colorNameToUse = userDefinedColorName ?? defaultColorName
print(colorNameToUse)

//prints "red"
// userDefinedColorName is nil, so colorNameToUse is set to the default of "red"

userDefinedColorName = "green"
colorNameToUse = userDefinedColorName ?? defaultColorName
print(colorNameToUse)

//prints "green"
// Now userDefinedColorName is "green", so colorNameToUse is set to the value of userDefinedColorName of "green"

7.2 Without Nil coalescing (or 💩 Code)

let defaultColorName = "red"
var userDefinedColorName: String?   // defaults to nil

var colorNameToUse: String!
if let color = userDefinedColorName {
    colorNameToUse = color
} else {
    colorNameToUse = defaultColorName
}
print(colorNameToUse)

//prints "green"
//userDefinedColorName is "green", so colorNameToUse is set to the value of userDefinedColorName of "green"

8. Optional Unwrapping (if let vs guard let)

As you know, the if let allows us to unwrap optional values safely only when there is a value, and if not, the code block will not run. On other hand, a guard statement is used to transfer program control out of a scope if one or more conditions aren't met. The guard statement makes our code more readable. Readability and easy maintenance are extremely important, especially when working with other programmers.

Here's an example for creating a new user:

let emailField = UITextField()
emailField.text = "abcd@mail.com"
let usernameField = UITextField()
usernameField.text = "vineet"
let passwordField = UITextField()
passwordField.text = "123456"
let conifrmPasswordField = UITextField()
conifrmPasswordField.text = "123456"

8.1 if let (or Bad Code)

func loginIfLet(){
    if let email = emailField.text {
        if let username = usernameField.text {
            if let password = passwordField.text {
                if let conifrmPassword = conifrmPasswordField.text {
                    if password == conifrmPassword {
                        print("Email - \(email)")
                        print("Username - \(username)")
                        print("Password - \(password)")
                    } else {
                        print("Password didn't match with conifrm password.")
                    }
                } else {
                    print("Conifrm password is empty.")
                }
            } else {
                print("Password is empty.")
            }
        } else {
            print("Username is empty.")
        }
    } else {
        print("Email is empty.")
    }
}
loginIfLet()

8.2 guard let (or Awesome Code)

func loginGuardLet(){
    guard let email = emailField.text else {
        print("Email is empty.")
        return
    }
    guard let username = usernameField.text else {
        print("Username is empty.")
        return
    }
    guard let password = passwordField.text else {
        print("Password is empty.")
        return
    }
    guard let conifrmPassword = conifrmPasswordField.text else {
        print("Conifrm password is empty.")
        return
    }
    if password == conifrmPassword {
        print("Email - \(email)")
        print("Username - \(username)")
        print("Password - \(password)")
    } else {
        print("Password didn't match with conifrm password.")
    }
}
loginGuardLet()

9. Functional Programming

As you know, functional programming is a programming paradigm—a style of building the structure and elements of computer programs—that treats computation as the evaluation of mathematical functions and avoids changing-state and mutable data.

Here's an example, that print all the even numbers between 1 and 10:

9.1 Old Approach (or Bad Code)

var evenNumbers = [Int]()
for number in 1...10 {
    if number % 2 == 0 {
        evenNumbers.append(number)
    }
}
print(evenNumbers) //prints "[2, 4, 6, 8, 10]"

9.2 Modern Approach (or Good Code)

var evens = Array(1...10).filter { (num) -> Bool in
    return num % 2 == 0
}
print(evens) //prints "[2, 4, 6, 8, 10]"

or

var evens = Array(1...10).filter { $0 % 2 == 0 }
print(evens) //prints "[2, 4, 6, 8, 10]"

10. Generics

Generics are one of the most powerful features of Swift. Generic code enables you to write flexible, reusable functions and types that can work with any type, subject to requirements that you define.

Here's an example of swaping two integer and string values:

10.1 Without Generics (or 💩 Code)

func swapTwoInts(_ a: inout Int, _ b: inout Int) {
    let temporaryA = a
    a = b
    b = temporaryA
}

func swapTwoStrings(_ a: inout String, _ b: inout String) {
    let temporaryA = a
    a = b
    b = temporaryA
}

var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)
print("someInt = \(someInt)")
print("anotherInt = \(anotherInt)")
/* prints
 someInt = 107
 anotherInt = 3
 */
 
var someString = "hello"
var anotherString = "world"
swapTwoStrings(&someString, &anotherString)
print("someString = \(someString)")
print("anotherString = \(anotherString)")
/* prints
 someString = world
 anotherString = hello
 */

10.2 With Generics (or 👊 Code)

func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
    let temporaryA = a
    a = b
    b = temporaryA
}

var someInt = 3
var anotherInt = 107
swapTwoValues(&someInt, &anotherInt)
print("someInt = \(someInt)")
print("anotherInt = \(anotherInt)")
/* prints
 someInt = 107
 anotherInt = 3
 */

var someString = "hello"
var anotherString = "world"
swapTwoValues(&someString, &anotherString)
print("someString = \(someString)")
print("anotherString = \(anotherString)")
/* prints
 someString = world
 anotherString = hello
 */

11. Type safe using Enum (enumeration)

An enumeration defines a common type for a group of related values and enables you to work with those values in a type-safe way within your code.

11.1 Without Enum (or 💩 Code)

let directionToHead = "east"
switch directionToHead {
case "north":
    print("Lots of planets have a north")
case "south":
    print("Watch out for penguins")
case "east":
    print("Where the sun rises")
case "west":
    print("Where the skies are blue")
default:
    print("Unknown direction")
}
//prints "Where the sun rises"

11.2 With Enum (or 👊 Code)

Without Enum, you are hard coding the values for each case — north, south, east, and west. Let's now see how enums can add beauty to your code.

enum CompassPoint {
    case north, south, east, west
}
let direction: CompassPoint = .east

switch direction {
case .north:
    print("Lots of planets have a north")
case .south:
    print("Watch out for penguins")
case .east:
    print("Where the sun rises")
case .west:
    print("Where the skies are blue")
}

Do you think you have some tips and tricks which can improve the skills of other Swift Developers? Why not share them in the comments section below!


You can download the swift playground of all above examples from Here



Discussion

Read Community Guidelines
You've successfully subscribed to Developer Insider
Great! Next, complete checkout for full access to Developer Insider
Welcome back! You've successfully signed in
Success! Your account is fully activated, you now have access to all content.