Swift Ninja Logo

Lesson 020 – POSTing to a REST API

RestIntroduction

Back in Lesson 015, we took a look at what you need to do in order to make HTTP Requests against a REST API. In that example, we only made a GET request, which limited us to a “read-only” interaction with a server. In the real world, we would need to send data back to the server. Whether that is to log in, save a picture to an online album, or post a status update, we are going to move beyond “read-only” pretty quickly.

Good News

The good news is that we can leverage almost everything that we did back in Lesson 015. Today, we’ll be using a free tester REST API located at http://reqres.in/. When you POST to http://reqres.in/api/users, it will add an id and timestamp field and just echo back any data you posted to it. So, let’s take a look at how we POST.

There are a few differences right off the bat. First, we added these lines

The first line just sets the code to make the request as a POST instead of a GET. GET is the default, which is why we didn’t specify this previously. Next, declare a body for our request. Whenever you submit a form on a normal website, that data will often be transmitted in exactly the format you see here. We have a key=value syntax with ampersands (&) separating each term. Next we encode that string so it can be understood and assign it to the body of the request.

After that, everything is exactly the same, with the exception that I’m pulling different keys out of the resulting JSON array. That should make sense, given that we are hitting a different endpoint. Remember that this code is all on GitHub, so you can absolutely pull it down. Add values to the body and then try to pull them back out again. Get familiar with how this “song and dance” goes. For example, when I make this request, I get the following result

A Few Closing Notes

Even if you run my exact code, you will get a different id every time you request. That seems to be random. Also, whenever I run this in the playground, I always get these errors first, which do not seem to affect whether the code executes properly. I didn’t get a lot of help when I Googled it, and it seemed to be harmless, so I’m ignoring it.

Lesson 019 – Defer

DeferWhen you are writing applications for mobile devices, being a good steward of resources is important. When you are through using objects or resources, you should release them so that the device can allocate the resources for other purposes. However, making sure you clean up resources every time you prepare to return can be difficult and result in a lot of copy/paste code.

Introduction

The defer keyword creates a stack of commands that are executed when the current scope (typically a method) ends. For those of you unfamiliar with a stack, think of it like a list of things. However, unlike a queue where first in line is the first out of line, a stack operates on a Last-In-First-Out (LIFO) model.

Think about a list that contains A B C D. In a queue, which is a First-In-First-Out (FIFO) model, that list would be unloaded as A B C D. However, if that list were a stack, it would be unloaded as D C B A. This makes sense in English terms, too. If you had a stack of Blu-Ray Disks, the first one you would have to move to get to the bottom would be the last one you piled on.

Defer by Example

Defer is written as a block of code like if…else or do…while. Let us first take a look at code that requires cleanup to happen in multiple places.

You’ll notice that this method can return in two different places, depending on the argument that is passed in. However, if we declare the method’s cleanup code with defer right at the beginning, it will execute automatically when the method is exited – regardless of where that occurs.

Now, if I call this method with 0, I get

If I call the method with 1, I get

This example only dealt with one defer block, though. As I mentioned at the beginning, you can add several defer blocks in your code. Typically, you want to add the defer block right after you declare the thing you need to clean up. Take a look at this method. I declare a method-level cleanup, a cleanup after dealing with the NSBundle, and a cleanup after messing with the NSDate. As an added bonus, those only get added if method execution gets that far, saving my cleanup code from having several if statements and null checks.

Now, if I call into that method passing in 1, 2 or 3, I get the following 3 results. Notice how the added defer blocks are executed in reverse order from how they were added.

Conclusion

You can – of course – write successful Swift applications without using defer. However, defer is one of those language features that will help you to write cleaner code that is easier to read and to maintain. It is always a good idea to declare variables near where you use them (don’t declare them all at the top just to “put them in a spot”). And by the same token, it is better to declare the cleanup at the same time you declare the variables. It is a best practice that will serve you well as you progress on your career.

Lesson 018 – Guard

GuardIn our Introduction to Error Handling, we saw some ways to trap error conditions in our programs. However, the goal there is to control program flow when errors are raised from other parts of the program. That can be framework code (like the JSONObjectWithData call) or code that we have written ourselves.

Introduction

In today’s lesson, I want to talk about Swift’s guard keyword. Guard is a very subtle feature. You can actually program without it and still create a perfectly well-written program. However, guard allows you to express your intent more directly and specifically than the alternatives. In Lesson 016, we ended up with this method, which handles two pre-conditions before attempting the operation.

Enter Guard

The problem with this code is that we have to think of all of the things that we DON’T want instead of describing the things that we do want. This is where the subtle part comes in. Instead of describing the failing conditions, we describe the passing conditions. Those of you familiar with unit testing might see similarities with Assert statements. I’m fairly sure that’s intentional.

Do you see the difference? Instead of saying if (dividend == 0), we say, guard (dividend != 0). Unlike an if statement, there is no “success block” only the else/failure block. If we have success, the code just keeps going. In my contrived example, this is obvious almost no matter which way we do our parameter checking. However, in an example that Apple provides, you can start to see how this is more clear.

Conclusion

As an exercise, I’d like you to think about how you might have done that without guard. I’ve worked through this a few different ways and each time, I have to either use more lines of code, or make the code itself look less elegant. If you can get yourself to think in this “assertion-based” manner, you are going to write much cleaner code.

Lesson 017 – Revisiting Error Handling for Lesson 015

Introduction

Back in Lesson 015, we made a simple call to a REST API. At the time, we cheated in handling one of the errors that could occur. Take a look at this line from Lesson 015 to remind you of what we did

Better Error Handling

Now that we have Lesson 016 under our belt, we know better how to handle that situation now. Instead of the try!, which tells Swift that we don’t expect an error at all. We are so confident that we’ve said, “even if we get an error, just blow up the program… life isn’t worth living”. Those are bold statements considering we are dealing with fickle network connections.

Here is the complete program with the proper error handling in place

Now, we’ve exchanged our try! for a try and wrapped that segment of code in a do…catch block. If you want to test this out, you can use these different URLs to force each of our possible error conditions.

Conclusion

Doing error handling in a situation like a network call is often a “worst case scenario”. I realize that it may seem like we did a lot of protecting and if and else and try, but you can see that we have at least 4 different ways that this could have failed. Often, our code is going to be much more straightforward, so don’t judge too harshly by this example 😉

Lesson 016 – Error Handling Basics Part 1

ErrorIn our last lesson, we glossed over handling the errors that could occur during our JSON parsing because I wanted to focus on the specific functionality we were tackling. Well, it is time to pay the piper, now! Let’s take a look at how we handle errors in Swift.

Throwing Errors the Unsafe Way

Let’s pretend that we are going to be good little stewards of our methods and we are going to check our parameters against boundary issues. And, if the value passed in is outside of our expected range, we will throw an exception. Given the following method, we want to add the parameter check to make sure that the parameter is not zero.

One thing we can do is use the fatalError method that Swift provides us, like this

Throwing Errors the Safe Way

If I call our method with a 0 value, an error is raised that says fatal error: Parameter ‘theNumber’ cannot be zero. That is one way to handle it, but it is not the preferred, “Swift” way. That isn’t very safe. Users of our method would have no idea that an exception could be thrown. However, we can let users know and force them to deal with it. Our first step is to declare our own error type and have it inherit from Swift’s ErrorType. I’m going to elect to create a MathError type and add an option for DivideByZero.

So, does this mean that we can then just throw this error instead?

No! This code gets us the following compile error: “error: error is not handled because the enclosing function is not declared ‘throws'”. What does that mean? Well, as part of our method declaration, we need to let Swift (and our users) know that we plan to throw an error. All you have to do is add a throws keyword in before the return type. This is how our method will look when it is properly set up.

Now if we try to call this function normally, we get a compile-time error.

When you use Swift Error Handling like it is intended, everything is very intentional, but you can short circuit things. That is what we did in our last lesson. If I include my try so the code will compile, but I am too lazy or don’t care about my users and don’t want to handle the error, I can use try!. So, if I want to call our method but not handle the error, I can write code like this

The second statement executes with the error “fatal error: ‘try!’ expression unexpectedly raised an error: MathError.DivideByZero”. So, if we use try!, we might as well have just raised a fatalError(), because execution will still stop. However, if you want to handle that error, you can just use regular try wrapped in a do…catch block. That kind of good stewardship looks like this

So, unlike some other languages where the block itself is a try…catch, in Swift we attempt a try inside of a do…catch block. That requires just a little bit of a mind shift.

Preparing for Multiple Possible Error Types

Sometimes, more than one kind of thing can go wrong. I’m going to change our MathError type to now look like this

Now, I’m also going to change our method to possibly return another error type. Since our method is dividing 5 by a number and returning an integer, we can’t handle non-whole number return values. Therefore, if someone passes in anything larger than 5, we will throw a MathError.TooLarge error. Here is how that change to the method should look

Now, we can have specific catch blocks to handle each kind of error. We can just pile those catch blocks on top of one another.

Now, when we pass in 6, we output the message “You must provide a number that is less than or equal to five”. If we pass in zero, we output the message “You cannot pass in a zero”. Neither one stops the execution of the entire program. However, it will stop executing what is inside the do block and jump to the appropriate catch, so you should keep your do…catch blocks as specific as possible. Don’t just wrap large portions of code inside of a do block so you do as little error handling as possible.

Conclusion

Error handling is Swift is a bit of a different mindset than some other languages. However, if you are always very explicit and deliberate with your code, you won’t have as many issues. Next time, we’ll take a look at how we should have handled those network calls in our last lesson as well as examining how Swift’s guard keyword works.

« Older Entries   Recent Entries »