Skip to main content
Skip table of contents

Writing a Greeter Server in Swift

This page provides a step-by-step guide to writing the server-side of our Swift Greeter application.

The server consists of:

  • a Swift struct that implements the Greeter interface we defined earlier in Slice

  • an Ice object adapter that accepts TCP connections from clients, and later routes requests received over these connections to our Greeter implementation

You can find the complete source code for this example in the ice-demos repository.

Greeter Implementation

Compile Slice File with Slice Compiler

To write a Swift application with Ice, first configure SwiftPM to compile the Slice definitions. The Ice package contains a plugin for this purpose, CompileSwift, which can be added to the executableTarget in Package.swift.

SWIFT
.executableTarget(
    name: "Server",
    dependencies: [.product(name: "Ice", package: "ice-swift-nightly")],
    plugins: [.plugin(name: "CompileSlice", package: "ice-swift-nightly")]
),

The CompileSwift plugin compiles Greeter.ice into Greeter.swift and adds it as source file of the Server target. The generated code provides the APIs that we’ll need in our server code, so it’s an essential step of the development process.

A Swift protocol is generated for each Slice interface. In our example, Greeter.

Implement Servant

We implement the Greeter protocol with a Swift struct, Chatbot, defined in Chatbot.swift.

To get started we first need to import the Ice module. This is required for the Ice.Current parameter.

SWIFT
import Ice

Next we implement the Greeter protocol.

SWIFT
/// Chatbot is an Ice servant that implements Slice interface Greeter.
struct Chatbot: Greeter {
    // Implements the method greet from the Greeter protocol generated by 
    // the Slice compiler.
    func greet(name: String, current _: Ice.Current) -> String {
        print("Dispatching greet request { name = '\(name)' }")
        return "Hello, \(name)!"
    }
}

Since the Greeter Slice interface only has one operation (greet), there is only one method that Chatbot needs to implement.

You can see that the implementation of greeter is simple: it takes a name parameter, and returns a greeting based on the provided name.

Note that the signature of the greet method above does not include "async throws" like Greeter.greet. That's because this implementation is synchronous and does not throw any exception.

Main Server Program

Our main server code is placed in its own file, main.swift. First, we need to import the Ice module and create a CtrlCHandler object.

SWIFT
import Ice

// CtrlCHandler is a helper struct that handles Ctrl+C and similar signals. 
// It must be constructed at the beginning of the program, before creating 
// an Ice communicator or starting any thread.
let ctrlCHandler = CtrlCHandler()

We’ll discuss this more later. It’s important to create this object before anything else; just keep it in the back of your head for now.

The server application now be can be broken down into four pieces:

Create a Communicator

First, we create a Communicator with Ice.initialize:

CODE
var args = CommandLine.arguments
let communicator = try Ice.initialize(&args)
defer {
    communicator.destroy()
}

The communicator is our main entry point into the Ice runtime, handling the creation and caching of outgoing connections, among many other responsibilities..

It is important to properly clean up the communicator when done, which we do with the defer block that calls destroy().

Create an Object Adapter

Next, we create an object adapter using our communicator:

SWIFT
let adapter = try communicator.createObjectAdapterWithEndpoints(
    name: "GreeterAdapter", endpoints: "tcp -p 4061")

An object adapter serves two purposes in Ice:

  • it accepts connections from clients

  • it receives requests over these connections and routes them to servants based on the object identities carried by these requests

In this example, we create an object adapter named GreeterAdapter which listens for TCP connections on port 4061, and later receives requests over these connections.

We then register out Chatbot servant with this adapter under the identity greeter:

SWIFT
try adapter.add(servant: Chatbot(), id: Ice.Identity(name: "greeter"))

Later on, when the object adapter receives a request with identity “greeter”, it will route this request to our Chatbot instance. It is therefore essential that the client uses the same identity in its proxy.

Activate the Object Adapter

Next, we call activate on our object adapter to start accepting incoming connections and dispatch requests to our Chatbot servant:

SWIFT
try adapter.activate()
print("Listening on port 4061...")

Our server is now active, waiting for connections and requests from clients, and dispatching requests for “greeter” to our Chatbot servant.

Keep Running until Ctrl+C

It is essential to keep the server running and not fall off main prematurely. We use the following technique to achieve this goal:

  • wait in the main thread until an event occurs

  • catch Ctrl+C signals in a background thread and trigger this event upon Ctrl+C

The event we chose is “the communicator was shut down”.

The Ctrl+C handling is courtesy of the CtrlCHandler object we created earlier. We call its setCallback method to specify what to do when it catches a signal. In this case, we want to shut down the communicator:

SWIFT
// Shutdown the communicator when the user presses Ctrl+C.
ctrlCHandler.setCallback { signal in
    print("Caught signal \(signal), shutting down...")
    communicator.shutdown()
}

Next, we wait until the communicator is shut down:

SWIFT
// Wait until the communicator is shut down. 
// Here, this occurs when the user presses Ctrl+C.
await communicator.shutdownCompleted()

Finally, once the communicator is shut down by Ctrl+C, we reach the end of main and return. The communicator destruction destroys the object adapter, close all incoming connections, and perform various other clean-ups.

Running the Server

To run the server, execute the following command (the executable will be compiled if necessary):

CODE
swift run server

JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.