Book Image

Swift: Developing iOS Applications

By : Jon Hoffman, Andrew J Wagner, Giordano Scalzo
Book Image

Swift: Developing iOS Applications

By: Jon Hoffman, Andrew J Wagner, Giordano Scalzo

Overview of this book

The Swift––Developing iOS Applications course will take you on a journey to become an efficient iOS and OS X developer, with the latest trending topic in town. Right from the basics to the advanced level topics, this course would cover everything in detail. We’ll embark our journey by dividing the learning path into four modules. Each of these modules are a mini course in their own right; and as you complete each one, you’ll gain key skills and be ready for the material in the next module. The first module is like a step-by-step guide to programming in Swift 2. Each topic is separated into compressible sections that are full of practical examples and easy-to-understand explanations. Each section builds on the previous topics, so you can develop a proficient and comprehensive understanding of app development in Swift 2. By the end of this module, you’ll have a basic understanding of Swift 2 and its functionalities. The second module will be the an easy-to-follow guide filled with tutorials to show you how to build real-world apps. The difficulty and complexity level increases chapter by chapter. Each chapter is dedicated to build a new app, beginning from a basic and unstyled app through to a full 3D game. The last two chapters show you how to build a complete client-server e-commerce app right from scratch. By the end of these modules, you’ll be able to build well-designed apps, effectively use AutoLayout, develop videogames, and build server apps. The third and the last module of our course will take an example-based approach where each concept covered is supported by example code to not only give you a good understanding of the concept, but also to demonstrate how to properly implement it.
Table of Contents (6 chapters)
4
A. Biblography
5
Index

One of the biggest changes in Swift 2 is that Apple added a feature called error handling. Handling error situations is often the least fun part of programming. It is usually much more exciting to handle a successful case, often referred to as the happy path because that is where the exciting functionality is. However, to make a truly great user experience and therefore a truly great piece of software, we must pay careful attention to what our software does when errors occur. The error-handling features of Swift help us in handling these situations succinctly and discourage us from ignoring errors in the first place.

In this chapter, we will discuss exactly what error-handling features Swift has and how they help us to write better software. We will do so by covering the following topics:

Before we talk about handling an error, we need to discuss how we can signal that an error has occurred in the first place. The term for this is throwing an error.

If we try to call a function, such as normal, Swift is going to give us an error, as shown in the following example:

To eliminate this error, we must add the try keyword before the call. However, before we move forward, I would recommend that you wrap all of your code inside a function, if you are following along in a playground. This is because throwing errors at the root level of a playground will not be handled properly and may even cause the playground to stop working. To wrap your code in a function, you can simply add the following code:

This defines a function called main that contains all the normal playground code that is called once, at the end of the playground.

Now, let's get back to using the try keyword. There are actually three forms of it: try, try?, and try!. Let's start by discussing the exclamation point form, as it is the simplest form.

We can also use the try? keyword, which is referred to as an optional try. Instead of allowing for the possibility of a crash, this will turn the result of the function into an optional:

The advantage here is that if the function throws an error, repeated3 will simply be set to nil. However, there are a couple strange scenarios with this. First, if the function already returns an optional, the result will be converted to an optional of an optional:

This means that you will have to unwrap the optional twice in order to get to the real value. The outer optional will be nil if an error is thrown and the inner optional will be nil if the method returned nil.

The other strange scenario is if the function doesn't return anything at all. In this case, using an optional try will create an optional void, as shown:

You can check the result for nil to determine if an error was thrown.

The biggest drawback to this technique is that there is no way to determine the reason an error was thrown. This isn't a problem for our repeatString:untilLongerThan: function because there is only one error scenario, but we will often have functions or methods that can fail in multiple ways. Especially, if these are called based on user input, we will want to be able to report to the user exactly why an error occurred.

To allow us to get more precise information on the reason for an error, we can use the final keyword, which is simply try.

To get an idea of the usefulness of catching an error, let's look at writing a new function that will create a list of random numbers. Our function will allow the user to configure how long the list should be and also what the range of possible random numbers should be.

The idea behind catching an error is that you get a chance to look at the error that was thrown. With our current error type, this wouldn't be terribly useful because there is no way to create different types of errors. A great option to fix this is to use an enumeration that implements the ErrorType protocol:

This enumeration has a case for both the errors which we will want to throw, so now we are ready to implement our function:

This function begins by checking the error scenarios. It first checks to make sure that we are not trying to create a list of negative length. It then checks to make sure that the high value of the range is in fact greater than the low one. After that, we repeatedly add a random number to the output array for the requested number of times.

Note that this implementation uses the rand function, which we used in Chapter 2, Building Blocks – Variables, Collections, and Flow Control. To use it, you will need to import Foundation and also seed the random number with srand again.

Also, this use of random is a bit more complicated. Previously, we only needed to make sure that the random number was between zero and the length of our array; now, we need it to be between two arbitrary numbers. First, we determine the amount of different numbers we can generate, which is the difference between the high and low number plus one, because we want to include the high number. Then, we generate the random number within that range and finally, shift it to the actual range we want by adding the low number to the result. To make sure this works, let's think through a simple scenario. Lets say we want to generate a number between 4 and 10. The range size here will be 10 - 4 + 1 = 7, so we will be generating random numbers between 0 and 6. Then, when we add 4 to it, it will move that range to be between 4 and 10.

So, we now have a function that throws a couple of types of errors. If we want to catch the errors, we have to embed the call inside a do block and also add the try keyword:

do {
    try createRandomListContaininingXNumbers(
        5,
        between: 5,
        and: 10
    )
}

However, if we put this into a playground, within the main function, we will still get an error that the errors thrown from here are not handled. This will not produce an error if you put it at the root level of the playground because the playground will handle any error thrown by default. To handle them within a function, we need to add catch blocks. A catch block works the same as a switch case, just as if the switch were being performed on the error:

do {
    try createRandomListContaininingXNumbers(
        5,
        between: 5,
        and: 10
    )
}
catch RandomListError.NegativeListLength {
    print("Cannot create with a negative number of elements")
}
catch RandomListError.FirstNumberMustBeLower {
    print("First number must be lower than second number")
}

A catch block is defined with the keyword catch followed by the case description and then curly brackets that contain the code to be run for that case. Each catch block acts as a separate switch case. In our preceding example, we have defined two different catch blocks: one for each of the errors where we print out a user-understandable message.

However, if we add this to our playground, we still get an error that all errors are not handled because the enclosing catch is not exhaustive. That is because catch blocks are just like switches in that they have to cover every possible case. There is no way to say if our function can only throw random list errors, so we need to add a final catch block that handles any other errors:

The last catch block stores the error into a variable that is just of type ErrorType. All we can really do with that type is print it out. With our current implementation this will never be called, but it is possible that it will be called if we add a different error to our function later and forget to add a new catch block.

Note that currently there is no way to specify what type of error can be thrown from a specific function; so with this implementation there is no way for the compiler to ensure that we are covering every case of our error enumeration. We could instead perform a switch within a catch block, so that the compiler will at least force us to handle every case:

This technique will not cause the compiler to give us an error if we throw a completely different type of error from our function, but it will at least give us an error if we add a new case to our enumeration.

Another technique that we can use would be to define an error type that includes a description that should be displayed to a user:

Instead of throwing enumeration cases, we are creating instances of the UserError type with a text description of the problem. Now, when we call the function, we can just catch the error as a UserError type and print out the value of its userReadableDescription property:

This is a pretty attractive technique but it has its own drawback. This doesn't allow us to easily run certain code if a certain error occurs. This isn't important in a scenario where we are just reporting the error to the user, but it is very important for scenarios where we might more intelligently handle errors. For example, if we have an app that uploads information to the Internet, we will often run into Internet connection problems. Instead of just telling the user to try again later, we can save the information locally and automatically try to upload it again later without having to bother the user. However, Internet connectivity won't be the only reason an upload might fail. In other error circumstances, we will probably want to do something else.

A more robust solution might be to create a combination of both of these techniques. We can start by defining a protocol for errors that can be reported directly to the user:

Now we can create an enumeration for our specific errors that implements that protocol:

This enumeration is set up to have a raw type that is a string. This allows us to write a simpler implementation of the userReadableDescription property that just returns the raw value.

With this, our implementation of the function looks the same as earlier:

However, our error handling can now be more advanced. We can always just catch any UserErrorType and display it to the user, but we can also catch a specific enumeration case if we want to do something special in this scenario:

Keep in mind that the order of our catch blocks is very important, just like the order of switch cases is important. If we put our UserErrorType block before the NegativeListLength block, we would always just report it to the user, because once a catch block is satisfied, the program will skip every remaining block.

This is a pretty heavy handed solution; so, you may want to use a simpler solution at times. You may even come up with your own solutions in the future, but this gives you some options to play around with.

Forceful try

The try! keyword is

We can also use the try? keyword, which is referred to as an optional try. Instead of allowing for the possibility of a crash, this will turn the result of the function into an optional:

The advantage here is that if the function throws an error, repeated3 will simply be set to nil. However, there are a couple strange scenarios with this. First, if the function already returns an optional, the result will be converted to an optional of an optional:

This means that you will have to unwrap the optional twice in order to get to the real value. The outer optional will be nil if an error is thrown and the inner optional will be nil if the method returned nil.

The other strange scenario is if the function doesn't return anything at all. In this case, using an optional try will create an optional void, as shown:

You can check the result for nil to determine if an error was thrown.

The biggest drawback to this technique is that there is no way to determine the reason an error was thrown. This isn't a problem for our repeatString:untilLongerThan: function because there is only one error scenario, but we will often have functions or methods that can fail in multiple ways. Especially, if these are called based on user input, we will want to be able to report to the user exactly why an error occurred.

To allow us to get more precise information on the reason for an error, we can use the final keyword, which is simply try.

To get an idea of the usefulness of catching an error, let's look at writing a new function that will create a list of random numbers. Our function will allow the user to configure how long the list should be and also what the range of possible random numbers should be.

The idea behind catching an error is that you get a chance to look at the error that was thrown. With our current error type, this wouldn't be terribly useful because there is no way to create different types of errors. A great option to fix this is to use an enumeration that implements the ErrorType protocol:

This enumeration has a case for both the errors which we will want to throw, so now we are ready to implement our function:

This function begins by checking the error scenarios. It first checks to make sure that we are not trying to create a list of negative length. It then checks to make sure that the high value of the range is in fact greater than the low one. After that, we repeatedly add a random number to the output array for the requested number of times.

Note that this implementation uses the rand function, which we used in Chapter 2, Building Blocks – Variables, Collections, and Flow Control. To use it, you will need to import Foundation and also seed the random number with srand again.

Also, this use of random is a bit more complicated. Previously, we only needed to make sure that the random number was between zero and the length of our array; now, we need it to be between two arbitrary numbers. First, we determine the amount of different numbers we can generate, which is the difference between the high and low number plus one, because we want to include the high number. Then, we generate the random number within that range and finally, shift it to the actual range we want by adding the low number to the result. To make sure this works, let's think through a simple scenario. Lets say we want to generate a number between 4 and 10. The range size here will be 10 - 4 + 1 = 7, so we will be generating random numbers between 0 and 6. Then, when we add 4 to it, it will move that range to be between 4 and 10.

So, we now have a function that throws a couple of types of errors. If we want to catch the errors, we have to embed the call inside a do block and also add the try keyword:

do {
    try createRandomListContaininingXNumbers(
        5,
        between: 5,
        and: 10
    )
}

However, if we put this into a playground, within the main function, we will still get an error that the errors thrown from here are not handled. This will not produce an error if you put it at the root level of the playground because the playground will handle any error thrown by default. To handle them within a function, we need to add catch blocks. A catch block works the same as a switch case, just as if the switch were being performed on the error:

do {
    try createRandomListContaininingXNumbers(
        5,
        between: 5,
        and: 10
    )
}
catch RandomListError.NegativeListLength {
    print("Cannot create with a negative number of elements")
}
catch RandomListError.FirstNumberMustBeLower {
    print("First number must be lower than second number")
}

A catch block is defined with the keyword catch followed by the case description and then curly brackets that contain the code to be run for that case. Each catch block acts as a separate switch case. In our preceding example, we have defined two different catch blocks: one for each of the errors where we print out a user-understandable message.

However, if we add this to our playground, we still get an error that all errors are not handled because the enclosing catch is not exhaustive. That is because catch blocks are just like switches in that they have to cover every possible case. There is no way to say if our function can only throw random list errors, so we need to add a final catch block that handles any other errors:

The last catch block stores the error into a variable that is just of type ErrorType. All we can really do with that type is print it out. With our current implementation this will never be called, but it is possible that it will be called if we add a different error to our function later and forget to add a new catch block.

Note that currently there is no way to specify what type of error can be thrown from a specific function; so with this implementation there is no way for the compiler to ensure that we are covering every case of our error enumeration. We could instead perform a switch within a catch block, so that the compiler will at least force us to handle every case:

This technique will not cause the compiler to give us an error if we throw a completely different type of error from our function, but it will at least give us an error if we add a new case to our enumeration.

Another technique that we can use would be to define an error type that includes a description that should be displayed to a user:

Instead of throwing enumeration cases, we are creating instances of the UserError type with a text description of the problem. Now, when we call the function, we can just catch the error as a UserError type and print out the value of its userReadableDescription property:

This is a pretty attractive technique but it has its own drawback. This doesn't allow us to easily run certain code if a certain error occurs. This isn't important in a scenario where we are just reporting the error to the user, but it is very important for scenarios where we might more intelligently handle errors. For example, if we have an app that uploads information to the Internet, we will often run into Internet connection problems. Instead of just telling the user to try again later, we can save the information locally and automatically try to upload it again later without having to bother the user. However, Internet connectivity won't be the only reason an upload might fail. In other error circumstances, we will probably want to do something else.

A more robust solution might be to create a combination of both of these techniques. We can start by defining a protocol for errors that can be reported directly to the user:

Now we can create an enumeration for our specific errors that implements that protocol:

This enumeration is set up to have a raw type that is a string. This allows us to write a simpler implementation of the userReadableDescription property that just returns the raw value.

With this, our implementation of the function looks the same as earlier:

However, our error handling can now be more advanced. We can always just catch any UserErrorType and display it to the user, but we can also catch a specific enumeration case if we want to do something special in this scenario:

Keep in mind that the order of our catch blocks is very important, just like the order of switch cases is important. If we put our UserErrorType block before the NegativeListLength block, we would always just report it to the user, because once a catch block is satisfied, the program will skip every remaining block.

This is a pretty heavy handed solution; so, you may want to use a simpler solution at times. You may even come up with your own solutions in the future, but this gives you some options to play around with.

Optional try

We can also

use the try? keyword, which is referred to as an optional try. Instead of allowing for the possibility of a crash, this will turn the result of the function into an optional:

The advantage here is that if the function throws an error, repeated3 will simply be set to nil. However, there are a couple strange scenarios with this. First, if the function already returns an optional, the result will be converted to an optional of an optional:

This means that you will have to unwrap the optional twice in order to get to the real value. The outer optional will be nil if an error is thrown and the inner optional will be nil if the method returned nil.

The other strange scenario is if the function doesn't return anything at all. In this case, using an optional try will create an optional void, as shown:

You can check the result for nil to determine if an error was thrown.

The biggest drawback to this technique is that there is no way to determine the reason an error was thrown. This isn't a problem for our repeatString:untilLongerThan: function because there is only one error scenario, but we will often have functions or methods that can fail in multiple ways. Especially, if these are called based on user input, we will want to be able to report to the user exactly why an error occurred.

To allow us to get more precise information on the reason for an error, we can use the final keyword, which is simply try.

To get an idea of the usefulness of catching an error, let's look at writing a new function that will create a list of random numbers. Our function will allow the user to configure how long the list should be and also what the range of possible random numbers should be.

The idea behind catching an error is that you get a chance to look at the error that was thrown. With our current error type, this wouldn't be terribly useful because there is no way to create different types of errors. A great option to fix this is to use an enumeration that implements the ErrorType protocol:

This enumeration has a case for both the errors which we will want to throw, so now we are ready to implement our function:

This function begins by checking the error scenarios. It first checks to make sure that we are not trying to create a list of negative length. It then checks to make sure that the high value of the range is in fact greater than the low one. After that, we repeatedly add a random number to the output array for the requested number of times.

Note that this implementation uses the rand function, which we used in Chapter 2, Building Blocks – Variables, Collections, and Flow Control. To use it, you will need to import Foundation and also seed the random number with srand again.

Also, this use of random is a bit more complicated. Previously, we only needed to make sure that the random number was between zero and the length of our array; now, we need it to be between two arbitrary numbers. First, we determine the amount of different numbers we can generate, which is the difference between the high and low number plus one, because we want to include the high number. Then, we generate the random number within that range and finally, shift it to the actual range we want by adding the low number to the result. To make sure this works, let's think through a simple scenario. Lets say we want to generate a number between 4 and 10. The range size here will be 10 - 4 + 1 = 7, so we will be generating random numbers between 0 and 6. Then, when we add 4 to it, it will move that range to be between 4 and 10.

So, we now have a function that throws a couple of types of errors. If we want to catch the errors, we have to embed the call inside a do block and also add the try keyword:

do {
    try createRandomListContaininingXNumbers(
        5,
        between: 5,
        and: 10
    )
}

However, if we put this into a playground, within the main function, we will still get an error that the errors thrown from here are not handled. This will not produce an error if you put it at the root level of the playground because the playground will handle any error thrown by default. To handle them within a function, we need to add catch blocks. A catch block works the same as a switch case, just as if the switch were being performed on the error:

do {
    try createRandomListContaininingXNumbers(
        5,
        between: 5,
        and: 10
    )
}
catch RandomListError.NegativeListLength {
    print("Cannot create with a negative number of elements")
}
catch RandomListError.FirstNumberMustBeLower {
    print("First number must be lower than second number")
}

A catch block is defined with the keyword catch followed by the case description and then curly brackets that contain the code to be run for that case. Each catch block acts as a separate switch case. In our preceding example, we have defined two different catch blocks: one for each of the errors where we print out a user-understandable message.

However, if we add this to our playground, we still get an error that all errors are not handled because the enclosing catch is not exhaustive. That is because catch blocks are just like switches in that they have to cover every possible case. There is no way to say if our function can only throw random list errors, so we need to add a final catch block that handles any other errors:

The last catch block stores the error into a variable that is just of type ErrorType. All we can really do with that type is print it out. With our current implementation this will never be called, but it is possible that it will be called if we add a different error to our function later and forget to add a new catch block.

Note that currently there is no way to specify what type of error can be thrown from a specific function; so with this implementation there is no way for the compiler to ensure that we are covering every case of our error enumeration. We could instead perform a switch within a catch block, so that the compiler will at least force us to handle every case:

This technique will not cause the compiler to give us an error if we throw a completely different type of error from our function, but it will at least give us an error if we add a new case to our enumeration.

Another technique that we can use would be to define an error type that includes a description that should be displayed to a user:

Instead of throwing enumeration cases, we are creating instances of the UserError type with a text description of the problem. Now, when we call the function, we can just catch the error as a UserError type and print out the value of its userReadableDescription property:

This is a pretty attractive technique but it has its own drawback. This doesn't allow us to easily run certain code if a certain error occurs. This isn't important in a scenario where we are just reporting the error to the user, but it is very important for scenarios where we might more intelligently handle errors. For example, if we have an app that uploads information to the Internet, we will often run into Internet connection problems. Instead of just telling the user to try again later, we can save the information locally and automatically try to upload it again later without having to bother the user. However, Internet connectivity won't be the only reason an upload might fail. In other error circumstances, we will probably want to do something else.

A more robust solution might be to create a combination of both of these techniques. We can start by defining a protocol for errors that can be reported directly to the user:

Now we can create an enumeration for our specific errors that implements that protocol:

This enumeration is set up to have a raw type that is a string. This allows us to write a simpler implementation of the userReadableDescription property that just returns the raw value.

With this, our implementation of the function looks the same as earlier:

However, our error handling can now be more advanced. We can always just catch any UserErrorType and display it to the user, but we can also catch a specific enumeration case if we want to do something special in this scenario:

Keep in mind that the order of our catch blocks is very important, just like the order of switch cases is important. If we put our UserErrorType block before the NegativeListLength block, we would always just report it to the user, because once a catch block is satisfied, the program will skip every remaining block.

This is a pretty heavy handed solution; so, you may want to use a simpler solution at times. You may even come up with your own solutions in the future, but this gives you some options to play around with.

Catching an error

To get an

idea of the usefulness of catching an error, let's look at writing a new function that will create a list of random numbers. Our function will allow the user to configure how long the list should be and also what the range of possible random numbers should be.

The idea behind catching an error is that you get a chance to look at the error that was thrown. With our current error type, this wouldn't be terribly useful because there is no way to create different types of errors. A great option to fix this is to use an enumeration that implements the ErrorType protocol:

This enumeration has a case for both the errors which we will want to throw, so now we are ready to implement our function:

This function begins by checking the error scenarios. It first checks to make sure that we are not trying to create a list of negative length. It then checks to make sure that the high value of the range is in fact greater than the low one. After that, we repeatedly add a random number to the output array for the requested number of times.

Note that this implementation uses the rand function, which we used in Chapter 2, Building Blocks – Variables, Collections, and Flow Control. To use it, you will need to import Foundation and also seed the random number with srand again.

Also, this use of random is a bit more complicated. Previously, we only needed to make sure that the random number was between zero and the length of our array; now, we need it to be between two arbitrary numbers. First, we determine the amount of different numbers we can generate, which is the difference between the high and low number plus one, because we want to include the high number. Then, we generate the random number within that range and finally, shift it to the actual range we want by adding the low number to the result. To make sure this works, let's think through a simple scenario. Lets say we want to generate a number between 4 and 10. The range size here will be 10 - 4 + 1 = 7, so we will be generating random numbers between 0 and 6. Then, when we add 4 to it, it will move that range to be between 4 and 10.

So, we now have a function that throws a couple of types of errors. If we want to catch the errors, we have to embed the call inside a do block and also add the try keyword:

do {
    try createRandomListContaininingXNumbers(
        5,
        between: 5,
        and: 10
    )
}

However, if we put this into a playground, within the main function, we will still get an error that the errors thrown from here are not handled. This will not produce an error if you put it at the root level of the playground because the playground will handle any error thrown by default. To handle them within a function, we need to add catch blocks. A catch block works the same as a switch case, just as if the switch were being performed on the error:

do {
    try createRandomListContaininingXNumbers(
        5,
        between: 5,
        and: 10
    )
}
catch RandomListError.NegativeListLength {
    print("Cannot create with a negative number of elements")
}
catch RandomListError.FirstNumberMustBeLower {
    print("First number must be lower than second number")
}

A catch block is defined with the keyword catch followed by the case description and then curly brackets that contain the code to be run for that case. Each catch block acts as a separate switch case. In our preceding example, we have defined two different catch blocks: one for each of the errors where we print out a user-understandable message.

However, if we add this to our playground, we still get an error that all errors are not handled because the enclosing catch is not exhaustive. That is because catch blocks are just like switches in that they have to cover every possible case. There is no way to say if our function can only throw random list errors, so we need to add a final catch block that handles any other errors:

The last catch block stores the error into a variable that is just of type ErrorType. All we can really do with that type is print it out. With our current implementation this will never be called, but it is possible that it will be called if we add a different error to our function later and forget to add a new catch block.

Note that currently there is no way to specify what type of error can be thrown from a specific function; so with this implementation there is no way for the compiler to ensure that we are covering every case of our error enumeration. We could instead perform a switch within a catch block, so that the compiler will at least force us to handle every case:

This technique will not cause the compiler to give us an error if we throw a completely different type of error from our function, but it will at least give us an error if we add a new case to our enumeration.

Another technique that we can use would be to define an error type that includes a description that should be displayed to a user:

Instead of throwing enumeration cases, we are creating instances of the UserError type with a text description of the problem. Now, when we call the function, we can just catch the error as a UserError type and print out the value of its userReadableDescription property:

This is a pretty attractive technique but it has its own drawback. This doesn't allow us to easily run certain code if a certain error occurs. This isn't important in a scenario where we are just reporting the error to the user, but it is very important for scenarios where we might more intelligently handle errors. For example, if we have an app that uploads information to the Internet, we will often run into Internet connection problems. Instead of just telling the user to try again later, we can save the information locally and automatically try to upload it again later without having to bother the user. However, Internet connectivity won't be the only reason an upload might fail. In other error circumstances, we will probably want to do something else.

A more robust solution might be to create a combination of both of these techniques. We can start by defining a protocol for errors that can be reported directly to the user:

Now we can create an enumeration for our specific errors that implements that protocol:

This enumeration is set up to have a raw type that is a string. This allows us to write a simpler implementation of the userReadableDescription property that just returns the raw value.

With this, our implementation of the function looks the same as earlier:

However, our error handling can now be more advanced. We can always just catch any UserErrorType and display it to the user, but we can also catch a specific enumeration case if we want to do something special in this scenario:

Keep in mind that the order of our catch blocks is very important, just like the order of switch cases is important. If we put our UserErrorType block before the NegativeListLength block, we would always just report it to the user, because once a catch block is satisfied, the program will skip every remaining block.

This is a pretty heavy handed solution; so, you may want to use a simpler solution at times. You may even come up with your own solutions in the future, but this gives you some options to play around with.

Propagating errors

The last

So far, we have not had to be too concerned about what happens in a function after we throw an error. There are times when we will need to perform a certain action before exiting a function, regardless of if we threw an error or not.

Now, as I hinted before, there will be circumstances where we need to perform some action before exiting a function or method regardless of if we throw an error or not. You could potentially put that functionality into a function which is called before throwing each error, but Swift provides a better way called a defer block. A defer block simply allows you to give some code to be run right before exiting the function or method. Let's take a look at an example of a personal chef type that must always clean up after attempting to cook some food:

In the make crème brûlée method, we start out with a defer block that calls the clean method. This is not executed right away; it's executed immediately after an error is thrown or immediately before the method exits. This ensures that no matter how the making of the crème brûlée goes, the personal chef will still clean up after itself.

In fact, defer even works when returning from a function or method at any point:

Here, we have defined a small ingredient type and a pantry type. The pantry has a list of ingredients and a method to help us get an ingredient out of it. When we go to get an ingredient, we first have to open the door, so we need to make sure that we close the door at the end, whether or not we find an ingredient. This is another perfect scenario for a defer block.

One last thing to be aware of with defer blocks is that you can define as many defer blocks as you like. Each defer block will be called in the reverse order to which they are defined. So, the most recent deferred block will be called first and the oldest deferred block will be called last. We can take a look at a simple example:

In this example, "A" will be printed first because it was the last block to be deferred and "C" will be printed last.

Ultimately, it is a great idea to use defer any time you perform some action that will require clean-up. You may not have any extra returns or throws when first implementing it, but it will make it much safer to make updates to your code later.

Order of execution when errors occur

An

Now, as I hinted before, there will be circumstances where we need to perform some action before exiting a function or method regardless of if we throw an error or not. You could potentially put that functionality into a function which is called before throwing each error, but Swift provides a better way called a defer block. A defer block simply allows you to give some code to be run right before exiting the function or method. Let's take a look at an example of a personal chef type that must always clean up after attempting to cook some food:

In the make crème brûlée method, we start out with a defer block that calls the clean method. This is not executed right away; it's executed immediately after an error is thrown or immediately before the method exits. This ensures that no matter how the making of the crème brûlée goes, the personal chef will still clean up after itself.

In fact, defer even works when returning from a function or method at any point:

Here, we have defined a small ingredient type and a pantry type. The pantry has a list of ingredients and a method to help us get an ingredient out of it. When we go to get an ingredient, we first have to open the door, so we need to make sure that we close the door at the end, whether or not we find an ingredient. This is another perfect scenario for a defer block.

One last thing to be aware of with defer blocks is that you can define as many defer blocks as you like. Each defer block will be called in the reverse order to which they are defined. So, the most recent deferred block will be called first and the oldest deferred block will be called last. We can take a look at a simple example:

In this example, "A" will be printed first because it was the last block to be deferred and "C" will be printed last.

Ultimately, it is a great idea to use defer any time you perform some action that will require clean-up. You may not have any extra returns or throws when first implementing it, but it will make it much safer to make updates to your code later.

Deferring execution

Now, as I

hinted before, there will be circumstances where we need to perform some action before exiting a function or method regardless of if we throw an error or not. You could potentially put that functionality into a function which is called before throwing each error, but Swift provides a better way called a defer block. A defer block simply allows you to give some code to be run right before exiting the function or method. Let's take a look at an example of a personal chef type that must always clean up after attempting to cook some food:

In the make crème brûlée method, we start out with a defer block that calls the clean method. This is not executed right away; it's executed immediately after an error is thrown or immediately before the method exits. This ensures that no matter how the making of the crème brûlée goes, the personal chef will still clean up after itself.

In fact, defer even works when returning from a function or method at any point:

Here, we have defined a small ingredient type and a pantry type. The pantry has a list of ingredients and a method to help us get an ingredient out of it. When we go to get an ingredient, we first have to open the door, so we need to make sure that we close the door at the end, whether or not we find an ingredient. This is another perfect scenario for a defer block.

One last thing to be aware of with defer blocks is that you can define as many defer blocks as you like. Each defer block will be called in the reverse order to which they are defined. So, the most recent deferred block will be called first and the oldest deferred block will be called last. We can take a look at a simple example:

In this example, "A" will be printed first because it was the last block to be deferred and "C" will be printed last.

Ultimately, it is a great idea to use defer any time you perform some action that will require clean-up. You may not have any extra returns or throws when first implementing it, but it will make it much safer to make updates to your code later.