Tips to become a better Swift developer
Tips to become a better Swift developer

Tips to become a better Swift developer

Swift provide features that helps to developers code safer, faster, more readable and reliable respect to 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 code more safe and cleanly.

Pramod Kumar
Published in
9 min readMar 26, 2017

--

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 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 Xcode launch:

Playground can be also created within Xcode:

Once you are on playground, can write code on left side and result will be on right side and at 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 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 that 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 constants in structure or enum as:

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

It’s very help full to manage and maintain readability of your code.
Let’s 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 readability 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 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 professionals

  1. Use of guard let:

When any action performs, you wanna be sure that all 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 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 if 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 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 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 execution from any condition then either you resource will be in memory or you have to release it before finishing execution of function. So it’ll lead to code redundancy or by mistake you can leave resource in memory 🤔. To avoid this situation we can write resource releasing code in defer block 🙂, like:

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

3. wherekeyword:

Earlier in swift if you wanna use pattern matching with 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 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 , (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 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 delegate of any class A in class B, then you are keeping strong reference of class A in class B, and class A will also keep strong reference of class B as the delegate. So it will leeds to the retain cycle for objects and there will be 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 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 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 value is stored.
  • didSet is called immediately after 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 property is assigned a new value. This is true even if new value is same as the current value 😊.

⚠️ If you are changing 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 user interface of an iOS application, showing screens of content and 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 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 it will be a better to use extension for same. Because by creating a Global class we are making a “GOD Class”. So let’s split Global class tasks by using extensions, will make code more clean, readable and reusable.

Like:

// Average Practicefunc square(x: Int) -> Int {return x * x}let square3 = square(x: 3) //Print: 9square(x: square3) //Print: 81//Great Practiceextension Int {   var square: Int { return self * self }}3.square //Print: 93.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 Practicevar 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 Practicefunc 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 recommend icon if like this collection 😊

--

--