Agenda
- Default values for parameters
- Error handling in functions
- Functions recap
- Checkpoint 4
Default values for parameters
Parameters of functions helps us to customize the outcome of the function by changing parameter values based on the use case. However, there can be cases where you either can’t provide values of all parameters or for some parameters. This will stop your code to build.
Swift provides us a solution for this problem by allowing us to provide default values. So, when you are calling a function and you did not specify value of a parameter, the default value can be used instead.
For example.
func printTimesTables(for number: Int, end: Int = 12) {
for i in 1...end {
print("\(i) x \(number) is \(i * number)")
}
}
printTimesTables(for: 5, end: 20)
printTimesTables(for: 8)
See, in this piece of code, you can either mention the value of end parameter or swift will accept the default value that you have set at the top.
keepingCapacity
When an array is created, swift allocates some memory to the array and also gives us flexibility to add new items by increasing the memory as we go. If you no longer need the array, you can remove the contents of the array by using .removeAll()
. By doing so, the memory that is allocated to the array will also be removed.
But sometimes, even though you remove the contents of the array, you might want to retain the memory capacity so that you can add contents to the array again. This can be achieved by kepingCapacity
parameter. The parameter takes boolean as value and the default value is set to false. So, if you want to retain memory capacity use .removeAll(keepingCapacity:true)
.
var characters = ["Lana", "Pam", "Ray", "Sterling"]
print(characters.count)
characters.removeAll(keepingCapacity: true)
print(characters.count)
In this example, all the items in the array characters are removed but the memory allocated to the array is retained.
Error handling in functions
Handling errors in our swift code happens in 3 steps.
- Telling swift about the possible errors that can happen.
- Writing a function that can flag errors if they happen.
- Calling that function, and handling any errors that might happen.
The tutorial takes an example of checking users password strength and explains all the steps as we go.
Defining possible errors
enum PasswordError: Error {
case short, obvious
}
Error is an existing type in swift. This says that there are two possible errors short and obvious.
Writing a function that can flag errors
For the user to see what kind of error they have made, a function should throw the error. When you throw an error, the function terminates immediately without sending back any value.
func checkPassword(_ password: String) throws -> String {
if password.count < 5 {
throw PasswordError.short
}
if password == "12345" {
throw PasswordError.obvious
}
if password.count < 8 {
return "OK"
} else if password.count < 10 {
return "Good"
} else {
return "Excellent"
}
}
If a function is able to throw errors without handling them itself, it has to be marked throws before the return type. The type of error doesn’t need to be specified and if there are no errors, the function must work normally and return the value in this case a string.
Calling the function
Now that we have defined the function that checks for possible errors, we should call the function and handle any errors that the function might throw. In swift programming, it is performed in three steps.
- Starting a block of work that might throw errors, using
do
. - Calling one or more throwing functions, using
try
. - Handling any thrown errors using
catch
.
Let us look how we handle the errors thrown by our checkPassword
function.
let string = "12345"
do {
let result = try checkPassword(string)
print("Password rating: \(result)")
} catch {
print("There was an error.")
}
We have given a possible password to our checkPassword
function. Now, if there is no error thrown by the function, it should print Password rating: some_value
. But the string we provided will be marked as an error by our function and throw the error of value obvious
. Since there was an error thrown, the catch block will catch the error and say There was an error.
. You can also write a function depending upon your requirement and can execute the function to handle the errors in the catch block.
Pseudocode for this is as follows:
do {
try someRiskyWork()
} catch {
print("Handle errors here")
}
You must know that when a try block is present it must be followed by one or more catch blocks that will handle the errors. The try block must always be inside a do block.
Multiple catch blocks can be written to handle different types of errors.
let string = "12345"
do {
let result = try checkPassword(string)
print("Password rating: \(result)")
} catch PasswordError.short {
print("Please use a longer password.")
} catch PasswordError.obvious {
print("I have the same combination on my luggage!")
} catch {
print("There was an error.")
}
Functions recap
- Functions let us reuse code easily by carving off chunks of code and giving it a name.
- All functions start with the word
func
, followed by the function’s name. The function’s body is contained inside opening and closing braces. - We can add parameters to make our functions more flexible — list them out one by one separated by commas: the name of the parameter, then a colon, then the type of the parameter.
- You can control how those parameter names are used externally, either by using a custom external parameter name or by using an underscore to disable the external name for that parameter.
- If you think there are certain parameter values you’ll use repeatedly, you can make them have a default value so your function takes less code to write and does the smart thing by default.
- Functions can return a value if you want, but if you want to return multiple pieces of data from a function you should use a tuple. These hold several named elements, but it’s limited in a way a dictionary is not — you list each element specifically, along with its type.
- Functions can throw errors: you create an enum defining the errors you want to happen, throw those errors inside the function as needed, then use
do
,try
, andcatch
to handle them at the call site.
Checkpoint 4
Challenge
Write a function that accepts an integer from 1 through 10,000, and returns the integer square root of that number. That sounds easy, but there are some catches:
- You can’t use Swift’s built-in
sqrt()
function or similar – you need to find the square root yourself. - If the number is less than 1 or greater than 10,000 you should throw an “out of bounds” error.
- You should only consider integer square roots — don’t worry about the square root of 3 being 1.732, for example.
- If you can’t find the square root, throw a “no root” error.
Solution
We are using brute force method to solve this challenge.
import Cocoaenum rootError: Error {case noroot, outofbounds}func squareRoot(number: Int) throws -> Int {if number<1 || number>10000 {throw rootError.outofbounds}for i in 1...100 {if i*i == number {return i}}throw rootError.noroot}var number = 20do {let result = try squareRoot(number: number)print("The square root of \(number) is \(result) ")}catch rootError.noroot{print("No interger root")}catch rootError.outofbounds {print("The number is out of bounds")}
That is all for today. See you tomorrow.