Tips to become a better Swift developer

Swift provide the features that helps to developers code safer, faster, more readable and reliable respect to the Objective-C. Here are some outlined Swift tips that we’ve collected through our experience using this new language (Swift). They’ve helped us to write the code more safe and cleanly.

I’ve divided this discussion in two sections. In first one, we are discussing for those who are getting started with the swift and in second one, discussing for who are already using.

Tips for the beginner

1. Use Playground to Validate:

Whenever you are going to implement any logic experiment and validate it on playground, can be use also for learning purpose. 
Playground is a very easy and interactive tool for swift. Playground don’t need to create a new project 😃. To create playground simple choose as an option from the Xcode launch:

Playground can be also created within Xcode:

Once you are on playground, can write the code on left side and result will be on right side and at the bottom of it:

2. Use Optionals Safely:

An optional is a property that can have a valid value or nothing (nil). Whenever we are going to print an optional. It’ll print the value with some extra text as Optional(value). We can implicitly unwrap the optional by using an exclamation (!) mark at end of optional property name, but we should avoid it. This known as the forcefully unwrapping, leads to application crashes. So use of exclamation (!) mark is danger 🚫.

There are some special condition in which exclamation (!) mark used. 
For example: IBOutlet when you’ll create an outlets from interface builder then it’ll be an implicitly unwrapped optional because UIKit assumes the you’ve hooked/connected your outlet to interface builder 😐. So it is risky to use implicitly unwrapping an optional value.
We can use if let pattern to handle this:

var name: String? = "Joy"
var lastName: String?
print(name)          //Result: Optional("Joy")
print(name!) //Result: Joy
if let value = name {
print(value)
} else {
print("name not set")
}
//Result: Joy, because name has a value
if let lastNameNew = lastName {
print(lastNameNew)
} else {
print("last name not set")
}
//Result: last name not set, because lastName has not any value

3. Manage constants in a single file:

To manage constants of your project, create a file and manage all constants here only. Organise all the constants in structure or a enum as:

enum AppConstants {
static let AppName = "MyFirstApp"
static let NoInternetMessage = "Sorry! No internet connection"
}

It’s very help full to manage and maintain the readability of your code. 
Lets say you’ve used NoInternetMessage at many places in your application code and now you need to make some changes in the message then you can easily change the message only at one place, it’ll reflected automatically everywhere. 👍

4. Use of nested function:

You can define a function with in bodies of other function, known as nested function.
Nested functions will be hidden from the outside world, but can be called and used by their enclosing function. An enclosing function can also return their nested function, so that can be used in outside world or another scope.
It’ll increase the readable and reusability of your code.

func chooseStepFunction(backward: Bool) -> (Int) -> Int {
func stepForward(input: Int) -> Int { return input + 1 }
func stepBackward(input: Int) -> Int { return input - 1 }
return backward ? stepBackward : stepForward
}
var currentValue = -4
let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)
// moveNearerToZero now refers to the nested stepForward() function
while currentValue != 0 {
print("\(currentValue)... ")
currentValue = moveNearerToZero(currentValue)
}
print("zero!")
//Result: 
// -4...
// -3...
// -2...
// -1...
// zero!
One more last tip for you guys, you can go for professionals tips also. 😜

Tips for the professionals

  1. Use of guard let:

When any action performs, you wanna be sure that all the needed data is available there to work properly. Then commonly developers solved this in two ways, that are:

a. Pyramids of doom:

When developer wanna to be sure for each needed data, validates the each used property, as:

var userName: String?
var email: String?
var password: String?
var policyAccepted: Bool = false
func checkAndSave() {
if let uname = userName {
if let mail = email {
if let pwd = password {
if policyAccepted {
//do the great thing, like signup
}
}
}
}
}

It’s known as pyramids of doom because we the creating the pyramid of the conditions.😮

b. Conditional Return:

Later on pyramids of doom, developers converted it to conditional return, validate for each used property and return, as:

func checkAndSave() {
if let uname = userName {return}
if let mail = email {return}
if let pwd = password {return}
if policyAccepted {return}

//do the great thing, like signup
}

It’s known as conditional return because we’re returning on each condition failure 😯.
- First solution isn’t good in the sense of code readability and debugging, so we’ll not go for it.
- Second solution is not good enough too, because be are unnecessary returning from the function, can’t get that why have we returned it?😕
- Another solution, we can use if let but it’s also have some limitations as the scope of variable will be with in the if block, So it’ll again leeds to pyramid of doom 🤔.

So, here comes the concept of guard let , introduced in swift 2.0. It’s so simple and easy to use, conditions and the let statements can be used together 😊, can be use as:

func checkAndSave() {
guard let uname = userName, let mail = email, let pwd = password, policyAccepted else {
return
}
//do the great thing, like signup
}

2. Use of defer block:

defer is a block, means “Some instructions i wanna execute later on no matter what is it?”. It can contain a single line of code or bunch of code lines. We can define a defer block in a function which will defiantly called when function execution ends 😯. It reduces the code redundancy as well.
- Lets say, you are creating an object of any resource in a function and using guard keyword to check something or returning the execution from any condition then either you resource will be in memory or you have to release it before finishing the execution of function. So it’ll lead to the code redundancy or by mistake you can leave resource in memory 🤔. To avoid this situation we can write the resource releasing code in defer block 🙂. as:

func processFile(filename: String) throws {
if exists(filename) {
let file = open(filename)
      //defer block is defined here to close the open file
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.
}
}

3. wherekeyword:

Earlier in swift if you wanna use pattern matching with the condition then you’ve to use two if statements separately, as:

var webUrl: String? = "https://medium.com"
if let path = webUrl {
if path.hasPrefix("https") {
//do the great thing
}
}

Later on, Swift introduced where keyword in version 2.0 to do the same thing within single statement, as:

if let path = webUrl where path.hasPrefix("https") {
//do the great thing
}

Now, Swift has replaced where keyword with the , (comma) sign, as:

if let path = webUrl, path.hasPrefix("https") {
//do the great thing
}

So, where is more use full. Read more about the where.😜

4. Make custom delegation weak:

Wait… what is weak and delegate? Let’s check it on the apple developer for exact definition😉.
A weak reference is a reference that does not keep a strong hold on the instance it refers to, and so does not stop ARC from disposing of the referenced instance.
Delegation is a design pattern that enables a class or structure to hand off (or delegate) some of its responsibilities to an instance of another type.

Whenever you assign the delegate of any class A in class B, then you are keeping the strong reference of class A in class B, and class A will also keep the strong reference of class B as the delegate. So it will leeds to the retain cycle for the objects and there will be the memory related issues and app will crash 😳.

So, next time you’re going to create your custom delegates, then make sure to create them as a weak reference 🙃, as:

protocol myCustomDelegation: class {
func newFunction(vaueToBeShare: String)
}
class A {
weak var delegate: myCustomDelegation?
//making delegate as weak to break the retain cycle
}
class B: myCustomDelegation {
internal func newFunction(vaueToBeShare: String) {
//do something here when the delegate is fired
}
   let obj = A()   //keeping strong reference of class A
   init() {
obj.delegate = self //assigning class A to class B's delegate
}
}

5. Omit Using KVO:

KVO (Key Value Observer), where you can set and observer for any key and the action when value of that key is changing. It is good but not better approach in swift because we some time forgot to remove the observer that will lead to application crash, or observers always broadcasted so it’a pain full to handle.

We’ve a replacement for the KVO in swift that is property observer. Using it we can get the action when the property’s value is going to changed. There are :

  • willSet is called just before the value is stored.
  • didSet is called immediately after the new value is stored.
class newClass {
var myNewObj: Int = 0 {
willSet(newValue) {
print("newValue = \(newValue), oldValue = \(myNewObj)")
}

didSet {
print("oldValue = \(oldValue), currentValue = \(myNewObj)")
}
}
}
let obj = newClass()
obj.myNewObj = 5
//Result: newValue = 5, oldValue = 0
//Result: oldValue = 0, currentValue = 5
obj.myNewObj = 8
//Result: newValue = 8, oldValue = 5
//Result: oldValue = 5, currentValue = 8

The willSet and didSet observers for myNewObj are called whenever the property is assigned a new value. This is true even if the new value is the same as the current value 😊.

⚠️ If you are changing the value of property1 in property observer of property2 and property2 in property observer of property1, it’ll make a chain, give the bad execution error at run time, nothing’ll say at compile time.

6. Use Multiple storyboards:

A storyboard is a visual representation of the user interface of an iOS application, showing screens of content and the connections between those screens.
Xcode creates a story board by default, and developers use it for complete application, so it become a monster and increase the loading time for your project in Xcode as well as launching time of you application on device, 
So try to create multiple storyboards in you application.

📣 “use storyboard for a complete story not for a complete project”

When we are using multiple storyboards, we can create the view controllers in many ways, to in deeply read this article on use of multiple storyboards.
Multiple storyboards can be used as:

enum AppStoryboards : String {
case Main = "Main"
case PreLogin = "PreLogin"
   var instance: UIStoryboard {
return UIStoryboard(name: self.rawValue, bundle: nil)
}
}
// USAGE :
let storyboard = AppStoryboard.Main.instance
let loginVC = AppStoryboard.PreLogin.instance.instantiateViewController(withIdentifier: "LoginVC")
// Old Way
let storyboard = UIStoryboard(name: “Main”, bundle: Bundle.main)

7. Omit using Globals:

It’s a good practice to use Global variables and functions, can help us make clean code and increase code reusability. But I will be a better to use extension for the same. Because by creating a Global class we are making a “GOD Class”. So lets split the Global class tasks by using extensions, will make the code more clean, readable and reusable.

Like:

// Average Practice
func square(x: Int) -> Int {return x * x}
let square3 = square(x: 3) //Print: 9
square(x: square3) //Print: 81
//Great Practice
extension Int {
   var square: Int { return self * self }
}
3.square //Print: 9
3.square.square //Print: 81

Get some extensions from GitHub.

8. Use Genrics:

Generics allow you to declare a variable which, on execution, may be assigned to a set of types defined by us.

// Average Practice
var stateName = ["Uttar Pradesh", "Kerala", "Assam", "Punjab"]
var areaArray = [240928, 38863, 78438, 50362]
var growth = [20.1, 4.9, 16.9, 13.7]
func printStateName(obj: [String]) {  print("stateNames: \(obj)")}
func printStateArea(obj: [Int]) {  print("areas: \(obj)")}
func printStateGrowth(obj: [Double]) {  print("population growth: \(obj)")}
printStateName(obj: stateName)
printStateArea(obj: areaArray)
printStateGrowth(obj: growth)
//Great Practice
func printArray<T> (_ obj: T, message: String = "") { print("\(message)\(obj)") }
printArray(stateName, message: "stateNames: ")
printArray(areaArray, message: "areas: ")
printArray(growth, message: "population growth: ")

Thanks you reading, please hit the recommend icon if like this collection 😊