Thursday, February 25, 2021

How to check an element belongs to an array, in Swift

 There are multiple ways to achieve this, some that just work, and some just beautiful.

Here are some examples that achieve the same thing, but some are doing it in style:

//ugliest

func search1(friend: String, friends: [String]) -> Bool {

    var found = false

    for aFriend in friends {

        if friend == aFriend {

            found = true

        }

    }

    return found

}


//uglier

func search2(friend: String, friends: [String]) -> Bool {

    for aFriend in friends {

        if friend == aFriend {

            return true

        }

    }

    return false

}


//ugly

func search3(friend: String, friends: [String]) -> Bool {

    return friends.filter { $0 == friend }.count > 0

}


//okay

func search4(friend: String, friends: [String]) -> Bool {

    return !friends.filter { $0 == friend }.isEmpty

}


//beautiful but maybe the most difficult to understand

func search5(friend: String, friends: [String]) -> Bool {

    return !friends.allSatisfy{ $0 != friend }

}

Some test code:

let friends = ["Susan", "John", "Jimmy", "Mary"]

let friend1 = "Mary"

let friend2 = "Max"


search1(friend: friend1, friends: friends)

search2(friend: friend1, friends: friends)

search3(friend: friend1, friends: friends)

search4(friend: friend1, friends: friends)

search5(friend: friend1, friends: friends)


search1(friend: friend2, friends: friends)

search2(friend: friend2, friends: friends)

search3(friend: friend2, friends: friends)

search4(friend: friend2, friends: friends)

search5(friend: friend2, friends: friends)


Inspired by Mihaela's post

Tuesday, September 1, 2020

Sorting in Swift

 If you have even tried to sort objects, you might have noticed it was a pain.

If you have a Date object:

struct Date {

    let year: Int

    let month: Int

    let day: Int

}

Then comparing two dates would be:

    static func < (lhs: Date, rhs: Date) -> Bool {

        if lhs.year != rhs.year {

            return lhs.year < rhs.year

        } else if lhs.month != rhs.month {

            return lhs.month < rhs.month

        } else {

            return lhs.day < rhs.day

        }

                    } 

Except if you use tuples and then it becomes:

    static func < (lhs: Date, rhs: Date) -> Bool {

        return (lhs.year, lhs.month, lhs.day) 

                < (rhs.year, rhs.month, rhs.day)

    }


(example is taken from Comparable documentation at Apple) 

Friday, February 10, 2017

Be cautious with the ternary operator

The operators in a programming language, in general, and in Swift, in particular, are of three types:

Unary - that operate on a single target. They can be added before or after the target.

For example -a or !a or a!.

They can also be binary when they are applied to two targets. For example: a + b.

There are also the ternary operators that are applied to three targets. In Swift, we have only one ternary operator that has the format:

condition ? value_if_true : value_if_false

For example:

var interestRate = income < 1000 ? 3 : 5

It is a useful shortcut for

If income < 1000 {
    interestRate = 3
} else {
    interestRate = 5
}

There are some cases when the ternary operator can become confusing.

For example,  in the case when the options for true and false are pretty long:

let message = isAdmin ? “Please enter your admin username and password” : “Please enter your username and password”

Here, and if statement would me the code better

var message: String

If isAdmin {
    message = “Please enter your admin username and password”
} else {
    message = “Please enter your username and password”
}

So, as a conclusion, the ternary operator is good but sometimes replacing it with if statement might be better.

Of course, in our case, the strings need to be replaced with constants and maybe localized, but that is a different topic for another time.

Friday, February 3, 2017

Use long names

Unfortunately, we have been used to the fact that a name has nothing to do with the object it names. John, Mary, and Steve say almost nothing about the people it names. Maybe just that they all speak English. We don’t know if the are tall or short if they are good with computers or know how to paint or play the piano.

Of course, the reason for that is that their parents had no idea how would their kids behave when they would grow up.

But, fortunately, it is different when we write the code. When we create an element of our code, it does not matter if it is a constant, variable, method or a class, we know for sure what it will be used for. So we are in a better position to choose a name. And even if we are wrong and the purpose of the element will evolve, we can change the name anytime. Most of the modern IDE-s have a refactoring option that can change a name across the project.

Another great feature the IDE-s of today have is that they prepopulate the names. You start typing a couple of letters and they give you a dropdown to select the best options for the specific context. So we don’t really type the whole names anymore.

And because of this, there is no reason to save the space and use short names. We can use names as long as we want if they are useful.

Let’s see few examples:

age -> userAge
error -> connectionErrorCode
name -> dogName
User -> UserView
Login -> LoginViewController
message -> errorMessageAfterLogin

The advantage is obvious. So go crazy!

Wednesday, February 1, 2017

Write tests

Welcome to another post about how to improve our code.

Today we are trying to prove that the quality of the code improves when we write tests.

We, as developers, hate to write tests. Why? Simple. It brings extra work without no real immediate advantage. Also, because we are the ones writing the code, we expect it to be perfect. So, why test a perfect code?

Our managers hate the activity as well. Why? Because it costs them extra money and time without adding new features for the customers.

Then why do it? From the same reason, you test any equipment you buy from any store. You expect it to work, right? They probably test it before bringing it to the shop. And yet you thoroughly test it because you know, from experience, that some do fail.

When we write code we consider only some specific scenarios that we call sometimes happy paths. Then we implement the code according to only those.

It does not matter how you test your code. Ideally, you should use a framework (like XCTest), but you might as well write your own framework.

Let's take an example.

Assuming you work at a bank and you need a function to calculate the maximum interest rate a client can pay. As input, you have the mortgage he needs to pay per year and his available funds.

So assuming a client needs to pay $11 000 per year and his available funds, after deducting all the expenses from his salary, are $10 000, it is obvious that the maximum rate he can pay is 10%.

To calculate it we use the following formula

Rate = ((available funds - mortgage per year) / mortgage per year) * 100

so (11 000 - 10 000) / 10 000 * 100 = 10

let's quickly write the function to calculate that

let availableFunds = 11000
let mortgage = 10000
func computeMaximumInterestRate(availableFunds: Double, mortgage: Double) -> Double {
    return (availableFunds - mortgage) / mortgage * 100
}

Everything looks great. Let’s use this method:

computeMaximumInterestRate(availableFunds: 11000, mortgage: 10000)

All is good, it returns 10 meaning 10% as expected.

Now let’s take the tester hat and go crazy.

computeMaximumInterestRate(availableFunds: 10000, mortgage: 11000)

This returns 0. It means the customer can pay 0%. It might be okay, but in this case, the function should show that the user cannot really take this mortgage.

How about :

computeMaximumInterestRate(availableFunds: 9000, mortgage: -10000)

This returns -10. Is this a valid result? Not really. This means that if the client takes the mortgage, the bank will actually pay him some money.

This is similar to the famous bug Amazon had when they launched. If a user entered a negative number of items, the website will actually send some money to his credit card.

And finally:

computeMaximumInterestRate(availableFunds: 0, mortgage: 0)

This will crash the app.

We will not go into details about how to fix each of this problems, but we know that we need to fix them. Our function should differentiate between a valid result (like 10%) and an invalid one (like -10%) and maybe not return it at all, just raise an exception.

That’s it for now. See you next time!

Sunday, November 27, 2016

How to name a function - Coding in Style - episode 11



Welcome to Coding in style, a series of short videos about how to improve our code.

This is Dragos and you are watching the Episode number 11 where we talk about how to name functions and function parameters.

A few days ago I met a friend. “What are you doing these days?”, he asked. I replied: “Book”. “What do you mean?” he asked. “Swift 3”. “Are you are reading a book about Swift 3?” he said. “Yes, I do,” I replied. “Why didn’t say so?” he said.

Of course, this is not a real conversation as it would have been a strange one. But sometimes this is how we name our functions.

As we’ve seen in the previous episodes, the variables are the nouns of our code.

Similarly, the functions are the verbs. They show an action.

To check if a function name is good, you can have a chat with it:

“Hey function, whatcha doing?”

And the function should be able to reply:

“I [function name]”

You should be able to include the function name after “I” and make a valid sentence.

Let’s look at some examples:

"I dropDatabase" - yes
"I sortArray" - yes
"I user" - nop
"I database" - nop
"I createDatabase" - yes
"I databaseCreate" - sort of, but only if your name Yoda is

You can pick a capitalization rule and stick to it. For Swift, it is lower camelcase.

So dropDatabase is fine, but not dropDATABASE or drop_database.

This can be a bit weird in the case of a name that includes an abbreviation. For example HTML or HTTP.

For example, do you use formatHTML or formatHtml?

createURL or createUrl.

In my opinion, whatever you choose looks ugly, so just pick one and be consistent.

Be specific. The name should make it clear what is the purpose of the function.

For example, create is not a good one. createDatabase is better

Long names are okay.

Make the names as long as needed, but no longer than that.

For example, if you have a function createLocalDatabase and there is only one database, replace it with createDatabase.

Regarding the function parameters, apply the same rules as for variables. In couple words, the name of a parameter should be a good replacement in the sentence “my name is [parameter name]”.

Regarding the number of parameters, they should be as few as possible.

Here is what Uncle Bob (aka Robert C. Martin) says in his "Clean Code" book:

"The ideal number of arguments for a function is zero (niladic). Next comes one (monadic), followed closely by two (dyadic). Three arguments (triadic) should be avoided where possible. More than three (polyadic) requires very special justification—and then shouldn’t be used anyway."

How about methods? Obviously, we use the same rules.

But, BTW, do you know what is the difference between a function and a method? A method is a function that belongs to a class. So a function is a free bird. A method is a free bird that has been captured and put in a cage, called class.

What else? What are your ideas about naming a function?

That is about it for today. We talked about how to name a function. In a previous episode, more exactly episode 7, we discussed how to name entities in general and in episode 8 we talked about how to name a variable or a constant. Feel free to watch them for more details.

That’s about it for now. See you soon!

Friday, November 11, 2016

Reducing the cyclomatic complexity - Coding in style - Episode 9




Welcome to Coding in style, a series of short videos about how to improve  our coding.

This is Dragos and you are watching the Episode number 9 where we talk about how to reduce the complexity of a program.

What if you asked somebody the following question: how can I get to the downtown? And the answer was: You take the bus 102 or 103. If you take the 102, you go for two blocks and get off. Then, if it’s morning, you take 104 bus, if it’s evening and during a weekday you take 105. If it’s weekend you take 106. If you take 104, you get off after 3 blocks, if you take 105 after 5 blocks. Nobody can remember such a thing. But, lots of times, we write code like this.

As you know, the developing process does not end when you finish a task. It does not end when you launch the product or when you fix a bug. It finishes only when the code and the product are trashed and erased from existence. Until then, every little piece of code you write has a big chance to be visited again and changed.

Because of that, it is good idea to write awesome code every step of the way. Do to others as you would have them do to you can be translated, in this case, write your code as you would like others to write the code for you. In a good style, that is.

Maybe one of the most important qualities some code has is to be easy to understand. If nobody can understand our code, it does not mean we are very smart and the code is awesome. No, no, no. On the contrary. It means our brain is so complicated, that we are not able to write simple code.

There is one measure for the complexity of the code. It is called cyclomatic complexity.

The cyclomatic complexity defines the number of possible paths a piece of code can follow. It can be defined for a whole app or for a function.

For example:
//Example 1

func printHello() {
   print("Hello")
}

There is only one option of execution, so the complexity is 1.
The following function has complexity 2, as there are two possible paths:

//Example 2
func checkIfTeenager(age:Int) {
   if age < 13 {
       print("not teenager")
   } else {
       print("teenager")
   }
}
While there is some debate about what complexity should be maximum, it is clear that the smaller the better.
Here are some suggestions to reduce it.
//Example 3
func checkIfPositive(n:Int) -> Bool {
   if n > 0 {
       return true
   } else {
       return false
   }
}
The function has the complexity 2 as there are two different paths. The more if-s one adds the bigger the complexity is.
We can eliminate the if statement, and thus reducing the complexity, by returning directly the Boolean:
func checkIfPositive1(n:Int) -> Bool {
   return (n > 0)
}
//Example 4
func convertToString(n:Int) -> String {
   switch n {
       case 0:
           return "zero"
       case 1:
           return "one"
       case 2:
           return "two"
       case 3:
           return "three"
       case 4:
           return "four"
       case 5:
           return "five"
       case 6:
           return "six"
       case 7:
           return "seven"
       case 8:
           return "eight"
       case 9:
           return "nine"
       default:
           return "unknown"
   }
}
This ugly function has complexity 11. It also has ugliness 1000, but that is not a measure that is invented yet.
What to do?

We can replace the ugly function with:
func convertToString1(n:Int) -> String {
   
   if (n>=0) && (n<=9) {
       let numberAsString = ["zero", "one", "two", "three", "four", "five",
"six", "seven", "eight", "nine"]
       return numberAsString[n]
   } else {
       return "unknown"
   }
}
In this case, the complexity is down to 2.

Let’s check another example, for formatting an address:

//Example 5

func formatAddress(street: String, city:String, country:String) -> String {
   if street == "" {
       if city == "" {
           if country == "" {
               return ""
           } else {
               return country
           }
       } else {
           if country == "" {
               return city
           } else {
               return city + "," + country
           }
       }
   } else {
       if city == "" {
           if country == "" {
               return street
           } else {
               return street + "," + country
           }
       } else {
           if country == "" {
               return street + "," + city
           } else {
               return street + "," + city + ", " + country
           }
       }
   }
}

Not only it is a solution that does not look nice, it is also error prone. Also, it is long. And it has the complexity of 8, which is pretty big.

We can reduce the complexity by writing a helper function that just adds a command to an item:

func appendItem(item:String) -> String {
   if item == "" {
       return ""
   } else {
       return item + ","
   }
}

Then we can format the address by appending all the elements to each other:

func formatAddress1(street: String, city:String, country:String) -> String {
   var result = appendItem(item: street)
+ appendItem(item: city)
+ appendItem(item: country)
   if result.hasSuffix(",") {
       result.remove(at: result.index(before: result.endIndex))
   }
   return result
}

That is about it for today. We talked about reducing the complexity of the code. While it might seem awesome that one is able to write complex code, the reality is the another way round. The smarter one developer is, the simpler and easier to understood code he writes.

What do you think about the topic? What are the ways you like to simplify your code? Feel free to leave a message to the video or send me an email.

Do you have any idea for a topic, send me a message as well!

That’s about it for now. See you next time!