Microservices in Swift: Architecture and Practice for Pros

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Introduction: Why Swift is a Mature Platform for Microservices

Traditionally, Swift is associated with iOS and macOS development. However, with the emergence of Vapor and Hummingbird frameworks, as well as the SwiftNIO library, Swift has become a full-fledged language for server-side development. Microservice architecture in Swift is not just a trend, but a pragmatic choice for projects where performance, type safety, and native integration with the Apple ecosystem are important.

In this article, we will explore advanced aspects of building microservices in Swift: from transport selection to deployment in Docker. Code examples target Swift 5.9+ and macOS/Linux.



Microservice Architecture in Swift: From Monolith to Distributed System

Framework Choice: Vapor vs Hummingbird

For production solutions, Vapor 4 (stable, extensive community) or Hummingbird (lightweight, minimalistic) are recommended. Both are built on top of SwiftNIO.

The basic structure of a microservice in Vapor includes:

  • Routes — handling HTTP requests.
  • Controllers — business logic.
  • Services — isolated modules (database, external APIs).
  • Models — Fluent ORM for database operations.


// Example of basic microservice structure in Vaporimport Vapor

func routes(_ app: Application) throws { let orderController = OrderController() app.get("orders", ":id", use: orderController.getOrder) app.post("orders", use: orderController.createOrder)}

final class OrderController { func getOrder(req: Request) async throws -> Order { let orderID = try req.parameters.require("id", as: UUID.self) return try await Order.find(orderID, on: req.db) ?? Abort(.notFound) }}


Protocols and Dependency Injection

For loose coupling, use protocols. This simplifies testing and component replacement.

protocol PaymentServiceProtocol {    func processPayment(amount: Double, currency: String) async throws -> PaymentResult}

final class StripeService: PaymentServiceProtocol { func processPayment(amount: Double, currency: String) async throws -> PaymentResult { // Logic for calling Stripe API }}

// Registration in Vapor's DI containerapp.services.register(PaymentServiceProtocol.self) { _ in StripeService() }


Advanced Communication: gRPC and Async/Await

Why gRPC over REST

For internal microservices, gRPC is preferable to REST due to:

  • Strict typing via Protocol Buffers.
  • Binary protocol (less traffic).
  • Built-in streaming support.

Swift supports gRPC through the grpc-swift library. Example service definition:



// user.protoservice UserService {    rpc GetUser (UserRequest) returns (UserResponse);    rpc ListUsers (Empty) returns (stream UserResponse);}

message UserRequest { string user_id = 1;}

message UserResponse { string id = 1; string name = 2; string email = 3;}


Generating Swift code:

protoc --swift_out=. --grpc-swift_out=. user.proto


Server implementation in Swift:

import GRPCimport NIO

final class UserServiceImpl: UserServiceProvider { func getuser(request: UserRequest, context: StatusOnlyCallContext) -> EventLoopFuture { let user = UserResponse.with { $0.id = request.userID $0.name = "John Doe" $0.email = "john@example.com" } return context.eventLoop.makeSucceededFuture(user) }}


Async/Await in Microservices

Swift 5.5+ introduced

Blogs

Book Recommendations