5 Tips when getting started with gRPC and iOS

gPRC is a popular choice when doing networking on iOS

gRPC has grown to be a popular alternative to REST when making applications that communicate over a network. iOS applications are no exception.

This article hope to provide some useful tips for you who are looking to use gRPC in a future project, or just want some tips to improve existing code. We will also take a quick look at how to write tests for your gRPC client stubs.

What is GRPC?

gRPC is a open source high performance Remote Procedure Call (RPC) framework, with tools that helps you generate server and client code that can run on a wide variety of platforms (and programming languages). Including Swift and iOS.

Though not required, the generated gRPC code usually uses Protocol Buffers as the means to define and serialise the data transfered. Data and service definitions is specified in .proto files. The protocol buffer compiler, protoc, is then used to generate language specific code (with the help of plugins). For Swift and iOS, this means to run your .proto files through the compiler using the Swift Protobuf and gRPC Swift plugins.

It might look something like this:

Under the hood, the generated gRPC client code uses SwiftNIO together with Apple’s Network.framework and Dispatch.framework (also known as GCD — Grand Central Dispatch) as the means to synchronise, package and send data over the network.

SwiftNIO is a powerful event-driven asynchronous network application framework. It comes with a lot of powerful features, and is a core part of Vapor (an amazing Swift web framework).

5 tips

Re-use client stubs and GRPCChannels

It is recomended re-use client stubs and channels, so creating a client factory might be a good idea. Something like this:

Check out the official Performance Best Practices document for more details.

gRPC Swift also has a nice FAQ page worth checking out.

Utilise the power of SwiftNIO EventLoopFutures

SwiftNIO is a powerful framework, so utelising its components is not a bad idea. With EventLoopFutures and EventLoopPromises SwiftNIO provide a reactive way of working with asynchronous data.

There are useful chaining methods like .flatMap, to chain EventLoopFutures together in case you need to do a series RPC requests. And with one of the several reduce methods you can do multiple RPC request at the same time, and get the combined result when all the requests has completed.

Chaning methods like .map and .flatMapError makes it also possible to transform data and recover from errors. Check out the EventLoopFuture documentation for more details.

Recovering from closed channels during app background state

When an app enters background state, the system (iOS) will eventually close any TCP sockets opened by gRPC Swift. This will cause any RPC request initiated immediately after the app enters foreground to fail. gRPC will recover and open new TCP sockets, but will discard any ongoing requests and throw an GRPCStatus error with code .unavailable.

On Darwin platforms (like iOS), gRPC Swift / SwiftNIO defaults to using the Network framework and the Dispatch framework (also known as GCD — Grand Central Dispatch) to drive the data transport. Check out PlatformSupport and NIOTSEventLoopGroup for more insight.

Utilising SwiftNIO and EventLoopFuture, a simple recovery mechanism might look something like this for Streaming Calls:

Or something like this for Unary Calls:

Passing authorisation/access token as metadata

A nice way to pass authorisation or access tokens along with RPC calls, is to attach them as metadata to the request. When adding or manipulating request metadata, there is several ways to do it. But since fetching and refreshing authorisation / access tokens is usually an asynchronous process, doing so utilising SwiftNIO’s EventLoopFuture is a good alternative.

Something like this:

If authorisation / access token is not fetched asynchronously, this could also be done with an interceptor. Something like this:

Note: Currently gRPC Swift interceptors does not support asynchronous interaction with the ClientInterceptorContext object. If you try to e.g. fetch an access token asynchronous, and then call context.send(.metadata(headersWithAccessToken),promise: promise), the RPC request will fail with an GRPCError.InvalidState.

Avoid using client interceptors

In my experience, interceptors is mostly suited for the server part of gRPC. And is excellent if you need to do some sort of validation as requests come in, or mutate data as responses goes out.

On the client side, creating interceptors is both cumbersome and adds a lot of boilerplate (maybe this will change in the future 🤷‍♂️), so my tip is to avoid them completely. Interceptors is also “hidden” in the request / response pipeline, and not visible where the client stub’s methods are actually called (which might disorient developers that is new to the code base).

It is also not supported to do asynchronous work from an interceptor (like e.g. fetch a fresh access token to attach to the RPC request). The ClientInterceptorContext object you interact with in an interceptor, does not properly support to be interacted with asynchronously.

If you want to know more about interceptors and what to think about when using them, I advice you to check out the Interceptor Tutorial at the gRPC Swift repository on GitHub.

As an alternative to using Interceptors, one could utilise the capabilities to EventLoopFuture and the EventLoopPromise provided by SwiftNIO. That way you can chain your own EventLoopFutures together with EventLoopFutures returned by client stubs. E.g as shown with the AuthTokenProvider in the previous tip.

Adding things like tracing or logging to RPC requests is also easy to do by wrapping RPC calls in your own EventLoopFuture. Something like this:

That’s it for my 5 tips when getting started with gRPC and iOS 🙌 Hopefully you found some of them useful.

Happy coding!

Bonus: A couple of words about testing

Writing Unit tests is good practice to ensure features and functionality works as expected, and does not brake unintentionally.

SwiftNIO provides several types which is perfect for testing purposes, one of them being the EmbeddedEventLoop. Utilising this in a unit test might look something like this:

With the option TestClient set to true, the gRPC Swift protoc plugin can also generate testing stubs for client code. Take a look at the snippet at the top of the article to see how it might look like.

The test client stubs can then be used like this:

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store