OVERVIEW.md 5.8 KB

gRPC for Swift - Public API

This document will provide an overview of the gRPC API for Swift. It follows a standard form used by each language-platform implementation.

##Basic Functionality

The following examples use echo.proto to demonstrate basic gRPC operation and generated code.

###How is a New Stub Created?

Swift gRPC client and server code is generated by the protoc-gen-swiftgrpc plugin. The plugin is called from protoc and can be invoked for echo.proto like this:

protoc Examples/Echo/echo.proto --proto_path=Examples/Echo --swiftgrpc_out=. 

###Simple Request-Response RPC: Client-side RPC

var requestMessage = Echo_EchoRequest(text:message)
print("Sending: " + requestMessage.text)
let responseMessage = try service.get(requestMessage)
print("get received: " + responseMessage.text)

###Simple Request-Response RPC: Server Implementation of RPC

// get returns requests as they were received.
func get(request : Echo_EchoRequest) throws -> Echo_EchoResponse {
  return Echo_EchoResponse(text:"Swift echo get: " + request.text)
}

###Show how Client does two RPCs sequentially

###Show how Client does two RPCs asynchronously

###Any code for handling incoming RPC on server that might need to be written

###Server Streaming RPC: Client-side code

let requestMessage = Echo_EchoRequest(text:message)
print("Sending: " + requestMessage.text)
let expandCall = try service.expand(requestMessage)
var running = true
while running {
  do {
    let responseMessage = try expandCall.Receive()
    print("Received: \(responseMessage.text)")
  } catch Echo_EchoClientError.endOfStream {
    print("expand closed")
    running = false
  }
}

###Server Streaming RPC: Server-side code

// expand splits a request into words and returns each word in a separate message.
func expand(request : Echo_EchoRequest, session : Echo_EchoExpandSession) throws -> Void {
  let parts = request.text.components(separatedBy: " ")
  var i = 0
  for part in parts {
    try session.Send(Echo_EchoResponse(text:"Swift echo expand (\(i)): \(part)"))
    i += 1
    sleep(1)
  }
}

###How is a Server Created?

var done = NSCondition()
let echoProvider = EchoProvider()
var echoServer: Echo_EchoServer!
if useSSL {
  print("Starting secure server")
  let certificateURL = URL(fileURLWithPath:"ssl.crt")
  let keyURL = URL(fileURLWithPath:"ssl.key")
  echoServer = Echo_EchoServer(address:"localhost:8443",
                               certificateURL:certificateURL,
                               keyURL:keyURL,
                               provider:echoProvider)
} else {
  print("Starting insecure server")
  echoServer = Echo_EchoServer(address:"localhost:8081",
                               provider:echoProvider)
}
echoServer.start()
// Block to keep the main thread from finishing while the server runs.
// This server never exits. Kill the process to stop it.
done.lock()
done.wait()
done.unlock()

##Advanced

###RPC canceling on client side

###Code to look for and handle cancelled RPC on Server side

###Client Streaming RPC: Client-side code

let collectCall = try service.collect()
let parts = message.components(separatedBy:" ")
for part in parts {
  let requestMessage = Echo_EchoRequest(text:part)
  print("Sending: " + part)
  try collectCall.Send(requestMessage)
  sleep(1)
}
let responseMessage = try collectCall.CloseAndReceive()

###Client Streaming RPC: Server-side code

// collect collects a sequence of messages and returns them concatenated when the caller closes.
func collect(session : Echo_EchoCollectSession) throws -> Void {
  var parts : [String] = []
  while true {
    do {
      let request = try session.Receive()
      parts.append(request.text)
    } catch Echo_EchoServerError.endOfStream {
      break
    } catch (let error) {
      print("\(error)")
    }
  }
  let response = Echo_EchoResponse(text:"Swift echo collect: " + parts.joined(separator: " "))
  try session.SendAndClose(response)
}

###Flow control interactions while sending & receiving messages

###Flow control and buffer pool : Control API

###Bi Directional Streaming : Client-side code

var done = NSCondition()
let updateCall = try service.update()
DispatchQueue.global().async {
  var running = true
  while running {
    do {
      let responseMessage = try updateCall.Receive()
      print("Received: \(responseMessage.text)")
    } catch Echo_EchoClientError.endOfStream {
      print("update closed")
      done.lock()
      done.signal()
      done.unlock()
      break
    } catch (let error) {
      print("error: \(error)")
    }
  }
}
let parts = message.components(separatedBy:" ")
for part in parts {
  let requestMessage = Echo_EchoRequest(text:part)
  print("Sending: " + requestMessage.text)
  try updateCall.Send(requestMessage)
  sleep(1)
}
try updateCall.CloseSend()
// Wait for the call to complete.
done.lock()
done.wait()
done.unlock()

###Bi Directional Streaming : Server-side code

// update streams back messages as they are received in an input stream.
func update(session : Echo_EchoUpdateSession) throws -> Void {
  var count = 0
  while true {
    do {
      let request = try session.Receive()
      count += 1
      try session.Send(Echo_EchoResponse(text:"Swift echo update (\(count)): \(request.text)"))
    } catch Echo_EchoServerError.endOfStream {
      break
    } catch (let error) {
      print("\(error)")
    }
  }
  try session.Close()
}

###Any stub deletion/cleanup code needed