Making your iOS application easy to read with these simple steps.
top of page

Making Your iOS Application Easy to Read With These Simple Steps.


Making Your iOS Application Easy to Read With These Simple Steps.

Good programmers are the ones who explain what they do in the most simple way they can. Even physicists explain wormholes with a piece of paper and a pencil pinned in it. What makes us different?

I have always tried to write my code as simply as I could, everything from picking the right name for a variable to using code conventions, but something was still missing, a way to understand the code without trying to understand the “how” I did it, but rather the outcome I was trying to achieve.

You might even say the ability to read the code as a story, rather than a bunch of code.

Let’s address 3 main topics:

The Problem

Reading other programmers code can be a real struggle. Without providing the proper context one can simply find him/herself lost in finding the meaning of a function or a property.

The Suggestion

From 1’s and 0’s to low-level code to elite languages, it is noticeable that code syntax becomes more and more user-friendly, thus inviting new programmers to the developer’s universe. As the syntax becomes clearer to read in plain English so should our code be, simple,to the point, and self-explanatory.

The Outcome

A well written code that masks itself as a story, making it easy to read and understand (even without context).

_

Naming Functions

How to do it the right way:

When we write function we assume that the person reading the function has enough context to understand what we were trying to achieve. Naming our function in a vague way “handleRedView()” will raise many questions about what “RedView” stands for? What is the main goal of this function?.

It seems that in some cases the function's purpose is vague and too complicated to understand without providing context.

We can separate our function proposes into 4 categories:

1. Informer functions

2. Management functions

3. Router functions

4. Execution functions

1. Informer function

Usually triggers router/management functions.

function examples:

1 delegate.dataHasUpdated()

2

3 func dataHasUpdated()

4 {

5 //Someone is informing you that something has happened

6 }

1 // Informer Functions

2 override func engineStarted()

3 {

4 super.engineStarted()

5 handleCarStarted()

6 }

Call back function, inform that something did/will happen and gives you the option to react.

Mostly used as delegate triggered actions, or notification handling functions .

2. Management function

Used to unite multiple function to achieve 1 higher purpose without dependency, all the code inside the block will execute.

1 // Management Function

2 func handleCarStarted()

3 {

4 turnLights(on: true)

5 turnAC(on: true

6 }

Reading this function we have all the information we need, execute this functions when the car has started, at this point we don’t care “how” its done rather the “what” it does.

3. Router function

Used to unite multiple functions to achieve 1 higher purpose with some sort of dependency, the code will execute only when intended to.

1 // Router Function

2 private func turnLights(on shouldTurnLightsOn: Bool)

3 {

4 if shouldTurnLightsOn

5 {

6 turnExteriorLightsOn()

7 checkForBurnedBulbs()

8 }

9 else { turnExteriorLightsOff() }

10 // When an if statment has only 1 thing to execute i like to write it

11 // at the same line with the "if" "else" word, it makes reading your code more fluent.

12 }

Router function mostly points to execution functions but in some cases can include logic itself only if the code won’t exceed 1 line.

4. Execution function

The implementation of the function name.

1 // Execution Function

2 private func turnExteriorLightsOn()

3 {

4 leftFrontLight .isOn = true

5 rightFrontLight .isOn = true

6 leftBackLight .isOn = true

7 rightBackLight .isOn = true

8 }

9 private func checkForBurnedBulbs()

10 {

11 for lightBulb in bulbs where !lightBulb.isUseable

12 {

13 Dashborad.display(errorType: .lights)

14 break

15 }

16 }

The logic itself could potently be complicated to understand but we already declared what the function does in the name, the function turns exterior lights on, the function checks for burned bulbs, understanding this may ease locating bugs in the future, and make it easier to add logic to the function without changing it’s name.

_

Eventually, after implementing these ideas into your application you should have organized objects with informer, management and router functions in your class segment.

1 class Car: Vehicle

2 {

3 // Informer Functions

4 override func engineStarted()

5 {

6 super.engineStarted()

7 handleCarStarted()

8 }

9 // Management Functions

10 private func handleCarStarted()

11 {

12 turnLights(on: true)

13 turnAC(on: true)

14 }

15 // Router Functions

16 private func turnLights(on shouldTurnLightsOn: Bool)

17 {

18 if shouldTurnLightsOn

19 {

20 turnExteriorLightsOn()

21 checkForBurnedBulbs()

22 }

23 else { turnExteriorLightsOff() }

24 // When an if statment has only 1 thing to execute i like to write it

25 // at the same line with the "if" "else" word, it makes reading your code more fluent.

26 }

27 private func handleTurnAC(on shouldTurnACOn: Bool)

28 {

29 if shouldTurnACOn

30 {

31 let tempeture = currentTemptureSelected()

32 let toggleFocus = currentACToggleFocus()

33 startAC(with: tempeture, aimedTo: toggleFocus)

34 }

35 else { turnACOff() }

36 }

37 }

and all the execution/logic in the extension block in the same file.

1 class Car

2 {

3 // Execution Functions

4 private func turnExteriorLightsOn()

5 {

6 leftFrontLight .isOn = true

7 rightFrontLight.isOn = true

8 leftBackLight .isOn = true

9 rightBackLight .isOn = true

10 }

11

12 private func checkFormBurnedBulbs()

13 {

14 for lightBulb in bulbs where !lightBulb.isUseable

15 {

16 Dashboard.display(errorType: .lights)

17 break

18 }

19 }

20 }

The outcome would be a clean short class, readable and easily maintained.

_

* Remember functions should always be written by the single responsibly principle.

Avoid using "and" in function name: playAndMinimize() loadAndPlay() This bad practice breaks the single responsibility principle and makes you write code suitable for both situations.

Avoid playing guessing games when naming a function: moveRedViewIfNeeded() This example guarantees future programmers would have to dive inside the function only to understand what the trigger for moving the Red View, which by the way also ambiguous.

* and no layoutIfNeeded() is not the same thing, in this case we know that self should re-layout its view's when setNeedsLayout bool is true. This example is universal to swift programming language while your function will probably remain private to your application.

_

Bonus (food for thought)

The first thing that comes in mind when thinking about readable code is using code convention, they are well known and commonly used, but using them does not necessarily makes your code better, it will make it cross-application but less readable.

“is” prefix should be used for boolean variables and methods to explain that the returned value is of type Boolean. #CodingConventions

"if" statement always addresses boolean values, then why do we need to declare “is” in each boolean property? why did apple changed swift syntax from view.hidden ~> view.isHidden ? there’s only one answer I could think of…. because “if view.isHidden” feels natural.

Let’s try implementing “is” prefix by this laws:

  • If a boolean property/method of a class used on an instance of that class (public) then “is” prefix is justified.

1 public var isHidden: Bool

2 {

3 return alpha == 0.0

4 }

5 if containerView.isHidden

  • If a property used inside the class (private) then the prefix is redundant.

1 public var positionedVerticaly: Bool

2 {

3 return view.frame.width /2 == centerX

4 }

5 if positionedVerticaly

6 if positionedVerticaly && positionedHorizontally

7 VS

8 if isPositionedVerticaly

9 if isPositionedVerticaly && isPositionedHorizontally

  • if a boolean property/method used privately and publicly then a computed propery should return the private property value.

1 public var isPositionedVerticaly: Bool

2 {

3 return positionedVerticaly

4 }

5 if containerView.isPositionedVerticaly

We could have used private(set ) and still use it publicly but think about the side effect achieved here, we have implemented encapsulation.

Encapsulation is used to hide the values or state of a structured data object inside a class, preventing unauthorized parties’ direct access to them.

You might be asking your self what about booleans that don’t address self directly, in this case, we should write that property name like so:

private var playerIsPlaying: Bool

private var gridConstraintIsEnabled()

“is” needs something to refer to: view.isHidden, “is” refer to view. In the example above we have done that same thing, playerIsPlaying, “is” refers to the player.

Remember: Developers usually read the code inside the function to try and understand its purpose long before they even bother on reading the declaration of properties.

/if playerIsPlaying { }/ opposing to /if isPlayerIsPlaying {}/

which of this options sound natural?, I’ll let you decide..


bottom of page