MVP Factory
ai startup development

gRPC and Protocol Buffers for Mobile API Backends: Binary Wire Format, Bidirectional Streaming, and the Code Generation Pipeline That Gives You Type-Safe Clients Across Android, iOS, and KMP with 60% Less Payload Than REST+JSON

KW
Krystian Wiewiór · · 5 min read

TL;DR

gRPC with Protocol Buffers delivers roughly 60% smaller payloads and 30-40% faster serialization than REST+JSON on mobile devices, but only for structured, repeated-field-heavy payloads. For simple CRUD endpoints, the overhead of HTTP/2 connection setup and protobuf toolchain complexity can negate those wins. The real advantage? The schema-first contract and cross-platform codegen pipeline that kills an entire class of integration bugs across Android, iOS, and KMP.


The schema-first mindset

Paul Graham wrote that putting ideas into words forces clarity. You discover what you actually think only when you try to articulate it. Protobuf schema design works the same way. When you define your .proto files before writing a single line of Kotlin or Swift, you’re forced to think precisely about field types, evolution strategy, and what your API actually promises.

This is where most teams go wrong about gRPC adoption: they treat it as a wire format optimization when it’s fundamentally a design discipline.

Payload size and serialization benchmarks

I’ve built a few production mobile backends at this point, and the numbers tell a clear story, though with nuance. Here are representative benchmarks from mid-range devices (Pixel 6a, iPhone SE 3rd gen) serializing a typical “order list” response with 50 items:

MetricREST + JSONgRPC + ProtobufDelta
Payload size (wire)48.2 KB18.7 KB-61%
Serialization (Android, median)12.3 ms7.1 ms-42%
Deserialization (Android, median)14.8 ms9.2 ms-38%
Serialization (iOS, median)10.1 ms6.8 ms-33%
Deserialization (iOS, median)11.6 ms7.9 ms-32%
Simple single-object GET0.4 KB0.3 KB + framing~negligible

Binary format wins scale with payload complexity. A single user profile fetch? JSON is fine. A paginated feed with nested objects and repeated fields? Protobuf wins by a wide margin.

The codegen pipeline: Wire vs grpc-kotlin vs SwiftProtobuf

This is where the practical tradeoffs live.

grpc-kotlin (Google)

The official gRPC-Kotlin codegen produces coroutine-based stubs with Flow for streaming. It’s tightly coupled to the full gRPC runtime, which adds roughly 3-4 MB to your APK. Generated code is verbose but complete: interceptors, deadlines, and metadata come out of the box.

Wire (Square)

Wire takes a different philosophy: minimal generated code, Kotlin-first data classes, and no gRPC runtime dependency if you only need serialization. Wire-generated models are roughly 40% smaller in code footprint than the Google protobuf-java output. The tradeoff is that gRPC service stubs are less mature than the serialization layer.

SwiftProtobuf (Apple)

On iOS, SwiftProtobuf handles message codegen well, but gRPC service stubs come from grpc-swift, a separate project. The integration is solid but means managing two dependencies. Swift’s value-type semantics align naturally with protobuf’s immutable message model.

Featuregrpc-kotlinWire (Square)SwiftProtobuf + grpc-swift
Codegen qualityVerbose, completeConcise, Kotlin-idiomaticClean, two-dependency split
APK/IPA size impact~3-4 MB~1-1.5 MB~2 MB
Streaming supportFull (coroutines + Flow)PartialFull (async/await)
KMP compatibilityLimitedExcellentN/A (Swift only)
Field evolution / unknown fieldsPreservedPreservedPreserved

For KMP projects, Wire is the clear winner. It generates Kotlin multiplatform-compatible code from a single .proto definition that compiles for both Android and iOS targets.

Bidirectional streaming: replacing WebSockets

gRPC’s bidirectional streaming over HTTP/2 is a structured alternative to WebSockets for real-time features like chat, live updates, and collaborative editing. The advantage over WebSockets is that your streaming messages share the same protobuf schema, same interceptor chain for auth and retry, and same observability pipeline.

The catch: gRPC-Web does not support bidirectional streaming. In browser or environments without native HTTP/2 (some corporate proxies, older CDNs), you fall back to unary or server-side streaming only. This matters if your mobile app has a companion web client.

Interceptor chains: auth, retry, observability

gRPC interceptors are the equivalent of HTTP middleware but operate at the RPC level. A production interceptor chain typically layers:

  1. Auth interceptor that attaches bearer tokens from your token store
  2. Retry interceptor with exponential backoff on UNAVAILABLE status codes
  3. Observability interceptor that emits latency histograms and error rate metrics per RPC method

This composability is one of gRPC’s quietly strong points. In REST, you rebuild this per HTTP client library. In gRPC, the interceptor contract is part of the framework specification.

Where gRPC loses

I want to be straight about this. gRPC is not universally better:

  • Debugging is harder. Binary payloads aren’t human-readable in network inspectors without tooling like grpcurl or Buf’s reflection.
  • Toolchain complexity is real. Protobuf compiler, language-specific plugins, build system integration. The setup cost is non-trivial, and every new team member pays it again.
  • Simple CRUD APIs gain little. If your app is 90% basic resource fetching, REST with a well-typed OpenAPI spec gives you similar type safety with a simpler stack.

What I’d actually recommend

  1. Adopt gRPC for payload-heavy, streaming-heavy, or multi-platform APIs where codegen and binary format compound into real savings. Keep REST for simple public-facing endpoints.
  2. Use Wire for KMP projects. Its multiplatform codegen and minimal footprint make it the pragmatic choice over the official Google plugin.
  3. Write your .proto schemas before any implementation. Treat schema files as the canonical contract. The act of writing them precisely will expose design flaws early, the same way writing prose exposes unclear thinking.

Share: Twitter LinkedIn