gRPC and Server Side Swift: Getting Started

Learn how to define an API with gRPC and how to integrate it in a Vapor application. By Walter Tyree.

5 (3) · 2 Reviews

Download materials
Save for later
Share
You are currently viewing page 5 of 5 of this article. Click here to view the first page.

Adding the Completion Logic

Open TodoProvider.swift and add the new function to complete a TODO. The function body will look similar to that of deleteTodo(request, response). It searches by ID and then it toggles the completed field and saves the record using .update instead of .delete. It also returns the updated Todos_todo in the response instead of Empty. See if you can figure out the code. If you need help just open the spoiler below.

[spoiler title=”Solution”]

func completeTodo(
  request: Todos_TodoID, 
  context: StatusOnlyCallContext
) -> EventLoopFuture<Todos_Todo> {
  guard let uuid = UUID(uuidString: request.todoID) else {
    context.responseStatus.code = .invalidArgument
    return context.eventLoop.makeFailedFuture(
      GRPCStatus(code: .invalidArgument, message: "Invalid TodoID"))
  }
  return Todo.find(uuid, on: app.db(.psql))
    .unwrap(or: Abort(.notFound)).flatMap { [self] todo in
      todo.completed = !todo.completed
      return todo.update(on: app.db(.psql)).transform(to:
        context.eventLoop.makeSucceededFuture(Todos_Todo(todo)))
  }
}

[/spoiler]

Open the Todo.swift file and replace //TODO: Add field for task completed with:

@Field(key: "completed")
var completed: Bool

Then, add the completed field to the three init methods, so that your final code looks like this:

  init(id: UUID? = nil, title: String, completed: Bool? = false) {
    self.id = id
    self.title = title
    self.completed = completed ?? false
  }
}

extension Todos_Todo {
  init (_ todo: Todo) {
    if let todoid = todo.id {
      self.todoID = todoid.uuidString
    }
    self.completed = todo.completed
    self.title = todo.title
  }
}

extension Todo {
  convenience init (_ todo: Todos_Todo) {
    self.init(id: UUID(uuidString: todo.todoID), title: todo.title, 
              completed: todo.completed)
  }
}

Updating the Database Model

The next step is to add the field to the PostgreSQL database. The code is already in the project, in the file AddTodoCompletion.swift. Open the configure.swift file and uncomment the line app.migrations.add(AddTodoCompletion()).

Stop the app if it’s running and exit Evans if it’s open. Make sure the database server is still up.

Once you have done that, build the app with docker-compose build and run it with docker-compose up app. The app will auto-migrate.

Now, restart Evans and call FetchTodos to see your list. The output will look like this:

todos.TodoService@localhost:1234> call FetchTodos
{
  "todos": [
    {
      "title": "mytitle",
      "todoID": "BFC263CB-219A-4A6C-B1A0-B873E17E9969"
    }
  ]
}

Copy one of the todoID values and then call CompleteTodo and paste the UUID into the todoID field.

todos.TodoService@localhost:1234> call CompleteTodo
todoID (TYPE_STRING) => BFC263CB-219A-4A6C-B1A0-B873E17E9969
{
  "completed": true,
  "title": "mytitle",
  "todoID": "BFC263CB-219A-4A6C-B1A0-B873E17E9969"
}

Now type call FetchTodos and notice your TODO is marked as completed, like this.

todos.TodoService@localhost:1234> call FetchTodos
{
  "todos": [
    {
      "completed": true,
      "title": "mytitle",
      "todoID": "BFC263CB-219A-4A6C-B1A0-B873E17E9969"
    }
  ]
}

todos.TodoService@localhost:1234> 

You may notice that, in Evans, when the completed field is false it doesn’t appear in the Todo. This is a feature of proto3 as boolean values default to false. So, the server doesn’t send default values. Open todo.pb.swift and notice in the Todos_Todo struct that the completedvar is set to false when it gets created. By not sending default values back and forth, the client and server are able to save a little space.

It did take some work to modify the API; however, since you work with generated code instead of “stringly” typed URLs and JSON, the compiler and the IDE help you ensure that your code continues to match the API as it changes. Congrats!

Where to Go from Here?

The completed project is in the demo project’s final folder. You can download the project file if you haven’t already by clicking the Download Materials button at the top or bottom of this tutorial.

In this tutorial, you learned the basics of working with a .proto file to describe a gRPC API. You also learned how to generate Swift code using protoc and how to modify a Vapor app to use gRPC instead of HTTP.

The GitHub projects for protobuf, grpc-swift and protoc all contain more documentation and tutorials:

Here are a few things you can try to expand the current app:

  • Use the todo.pb.swift and todo.grpc.swift files to create an iOS or macOS client for your Vapor service.
  • Generate gRPC code in some other programming language and create a client for your Vapor service.
  • Add some more fields to the existing messages or services.

Please join the forum discussion below if you have any questions or comments!