Result
Last updated
Last updated
This is an advanced topic, but it is so so helpful to networking that we will do our best to make it accessible to everyone. That being said, you should have an understanding of enums with associated values. An understanding of generics would be helpful but not required.
We can think of Result
as being implemented as follows:
It is an enum with two potential cases–a failure response and a success response. Each case has an associated piece of information. We can make the Success
type anything that we want. In contrast, our Failure
type must adopt the Error
protocol.
Every single call to the network manager should return a Result
because networking is fraught with potential errors that we must handle. Hence, we need to categorize every error we can with the level of specificity that you choose. To create a type that adopts the Error
protocol, we don't need to add any functionality; we just need to add conformance directly in the type declaration or in an extension:
Personally speaking, I like being specific, so I will actually enum
erate many of the potential errors in an enum
. But, you can choose to handle errors however you want. I know, on Eatery, we just have a struct
that stores a description of the error as a string.
As I mentioned earlier, Success
can be anything that we want. For the rest of this article I will just call it MySuccess
.
Now that we have a Success
type (MySuccess
) and a Failure
type (MyError
) we need to put those into the Result
type because that's what our network manager will be passing around.
Associating types to a generic is very easy. We just match the syntax of the Result
definition above and substitute in our types: Result<MySuccess, MyError>
.
Now that we have our two cases, we need to figure out how to deal with them.
.success(MySuccess)
.failure(MyError)
For a more in-depth discussion, read enums with associated values.
I, personally, like using switch statements because the syntax is clean. When we are processing our errors, we need to check two things: first, we need to check which case we are processing, and second, we need to process the extra associated value.
A normal switch case will handle the cases, but will not give us access to the associated values. To circumvent this, we need to bind the associated value to a variable. We can do this in two ways with our switch statement:
You may decide that you don't care about the associated value. In this case, we don't need to bind anything, but we do still need to handle it. In this case, we use a case
not a case-let
statement (like a normal switch). But, this will check for equality between the case and switching result. This means that unless we can match the associated value, it will not trip the case. We can fix this by associating every possible value (or actually none)! We do this with the wildcard:
You may decide that switches have too much boiler plate, that switches are too scary, or that you do not need to handle all of the cases.
Granted, you do need to understand the general logic between case handling because, to use if statements, we just take a case and put an if
before it.