Network Requests in Swift Using the Result Type and Generics
How you can leverage Swift’s Result type combined with generics when making network requests
Result is an
enum with two cases:
failure, both of which are implemented using generics.
Success can be anything, but
failure must conform to the
Error protocol. (You can implement your own error types, or just use Swift’s
Introduction of the
Result type in Swift gives us a chance to handle the results of asynchronous functions in a cleaner and safer way.
Let’s see the benefits of using
Result vs. not using it on a simple network request example that uses generic types for both
We’ll start by implementing a
RequestError type and creating a
NetworkService class that has two functions.
They are both responsible for making a URL request, the difference being that one uses
Result for handling the result (pun intended), while the other one uses the plain old completion handler.
As you can see, using
Result makes the code easier to read.
On top of that, a completion handler approach is bug-prone because its
error values are both
Optional types, therefore, the following two examples would be valid (as far as the compiler is concerned), leading us to a weird state:
These kinds of bugs shouldn’t happen using our simple network request implementation, but as the code gets more complex (or is used differently) there’s certainly an option to make an error.
We can draw similar conclusions about code clarity and safety if we take a look at both functions when they are used by
Not only is the function that uses
Result much cleaner, but if there’s a bug in the function that uses the completion handler approach, it has the potential to completely mess the flow of our app.
For example, by leaving the app in a stuck state (when none of the values are present), or proceeding with the flow while simultaneously showing an error alert (when both of the values are present).
Result Type With Generics
The above network request example only supports
String values in case of success. But what if we want to support other value types, as well? Generics to the rescue!
First, we need to modify the
makeUrlReqeust function to support generic types.
Then, we must modify our consumer-side code.
Result with a generic type inside of a closure requires that the generic type is specified when creating the closure, otherwise, the compiler will throw the following error:
Finally, we need to change the
decodedData function to support decoding to other values besides a
String (check one possible implementation below), and voilà — you can now use any value you want!
Even though the example used in this article is a simple one, it clearly demonstrates how the
Result type can make our lives easier when we are handling the responses of network requests.
Thanks for reading and please let me know if you have any comments or questions!