104. Case Study: iOS Client (Swift) with a gRPC Go Backend
104. Case Study: iOS Client (Swift) with a gRPC Go Backend
When building modern backend systems, communication between applications becomes absolutely critical. One of the most popular approaches today is gRPC, an RPC framework built on Protocol Buffers (Protobuf) that is efficient and delivers high performance. In this article, I’ll walk through a case study of building communication between an iOS client (using Swift) and a Go backend, using gRPC as the communication protocol.
We’ll cover the reasoning behind the chosen architecture, the practical steps involved, and share hands-on code examples. This scenario is fairly common in modern application development that prioritizes performance and maintainability.
Why gRPC?
Before diving into the case study, let’s review: Why gRPC?
In short, gRPC offers:
- Efficiency: It uses Protobuf instead of JSON—smaller and faster.
- Interface contract: Protobuf provides strong typing through
.protofiles, so the client and backend share a clear contract. - Multi-language support: Including Go, Swift, Java, and more.
- Streaming: Supports real-time bidirectional streaming communication.
Let’s look at how gRPC and REST differ in certain aspects through the following table:
| REST/JSON | gRPC/Protobuf | |
|---|---|---|
| Serialization | Text (JSON) | Binary |
| Performance | Slower | Faster |
| API contract | Loose | Very strict |
| Streaming | Limited | Native |
| Multi-language support | Fairly good | Excellent |
The Case: A Simple Notes App
Imagine the scenario: we’re building a simple todo-list application.
- Client: iOS App (Swift)
- Backend: Go with gRPC
The app lets users perform CRUD operations (Create, Read, Update, Delete) on notes.
1. Designing the Protobuf Contract
First step: write the API contract in a Protobuf file (note.proto).
1syntax = "proto3";
2
3package note;
4
5service NoteService {
6 rpc CreateNote (CreateNoteRequest) returns (CreateNoteResponse);
7 rpc GetNotes (GetNotesRequest) returns (GetNotesResponse);
8 rpc DeleteNote (DeleteNoteRequest) returns (DeleteNoteResponse);
9}
10
11message Note {
12 string id = 1;
13 string title = 2;
14 string content = 3;
15 string created_at = 4;
16}
17
18message CreateNoteRequest {
19 string title = 1;
20 string content = 2;
21}
22
23message CreateNoteResponse {
24 Note note = 1;
25}
26
27message GetNotesRequest {}
28
29message GetNotesResponse {
30 repeated Note notes = 1;
31}
32
33message DeleteNoteRequest {
34 string id = 1;
35}
36
37message DeleteNoteResponse {
38 bool success = 1;
39}This file is the API contract that both the Go backend and the iOS client will use.
2. Backend Implementation: gRPC Go
a. Setting Up Protobuf & gRPC
Make sure you’ve installed protoc , the Go plugins, and the supporting modules:
1go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
2go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latestGenerate code from the .proto file:
1protoc --go_out=. --go-grpc_out=. note.protob. Implementing the Service
Here’s a partial implementation:
1type server struct {
2 note_pb.UnimplementedNoteServiceServer
3 notes map[string]*note_pb.Note
4}
5
6func (s *server) CreateNote(ctx context.Context, req *note_pb.CreateNoteRequest) (*note_pb.CreateNoteResponse, error) {
7 id := uuid.NewString()
8 now := time.Now().Format(time.RFC3339)
9 note := ¬e_pb.Note{
10 Id: id,
11 Title: req.Title,
12 Content: req.Content,
13 CreatedAt: now,
14 }
15 s.notes[id] = note
16 return ¬e_pb.CreateNoteResponse{Note: note}, nil
17}
18
19// Implement the other methods according to the contractc. Running the gRPC Server
1grpcServer := grpc.NewServer()
2note_pb.RegisterNoteServiceServer(grpcServer, &server{notes: map[string]*note_pb.Note{}})
3lis, _ := net.Listen("tcp", ":50051")
4grpcServer.Serve(lis)The Go backend is ready!
3. iOS Client: Swift
a. Integrating the Library
gRPC Swift support is available via Swift Package Manager or CocoaPods.
1// swift-tools-version: 5.6
2.package(url: "https://github.com/grpc/grpc-swift.git", from: "1.10.0")b. Generating Swift Code from Protobuf
Make sure the Swift plugin is installed:
1brew install grpc-swift
2protoc note.proto --swift_out=. --swift-grpc_out=.c. Creating the Client and Calling the API
1import GRPC
2import NIO
3import Note // generated from the Swift proto
4
5let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
6let channel = try! GRPCChannelPool.with(
7 target: .host("localhost", port: 50051),
8 transportSecurity: .plaintext,
9 eventLoopGroup: group
10)
11let client = Note_NoteServiceClient(channel: channel)
12
13let req = Note_CreateNoteRequest.with {
14 $0.title = "Learning gRPC"
15 $0.content = "Connecting Swift with Go"
16}
17let resp = try! client.createNote(req).response.wait()
18
19print("Successfully created note with id \(resp.note.id)")4. Flow Diagram
This illustrates the data flow from the client to the server.
sequenceDiagram
participant iOS as iOS App (Swift)
participant GoGRPC as gRPC Server (Go)
iOS->>GoGRPC: RPC CreateNote(title, content)
GoGRPC-->>iOS: New note + ID
iOS->>GoGRPC: RPC GetNotes()
GoGRPC-->>iOS: List of notes
5. Simulation: Creating and Retrieving Data
Steps:
- The iOS app sends a
CreateNoteRPC. - The Go server stores the note and returns a response.
- The iOS app sends a
GetNotesRPC. - The Go server returns the list of all notes.
Simulation results:
| Time | Client Action | Server Response |
|---|---|---|
| 2024-06-05 10:01 | CreateNote(“A”) | Note(id: “X1”, title: “A”) |
| 2024-06-05 10:02 | CreateNote(“B”) | Note(id: “X2”, title: “B”) |
| 2024-06-05 10:03 | GetNotes() | [Note “X1”, Note “X2”] |
6. Tips and Best Practices
- Versioning: Use Protobuf optional fields for backward compatibility.
- Transport Security: Enable TLS in production.
- Error Handling: Use Protobuf error enums.
- Streaming: Use streaming for real-time sync when needed.
7. Conclusion
gRPC ushers in a new era of efficient communication between applications. The case study above shows how a Go backend and an iOS client can communicate through a clear API contract, with high performance and great maintainability.
This combination is a great fit for modern applications, from microservices to mobile backends.
If you’re building cross-platform applications, set up the Protobuf build process to run automatically in your CI/CD pipeline, use the same dependencies across the whole team, and don’t forget to test across languages—because while gRPC is designed for cross-ecosystem use, each language’s implementation has its own nuances.
Further references:
Happy experimenting with gRPC—and I hope this case study helps you integrate your mobile app and backend more effectively!