Browse Source

Mostly feature-complete Swift gRPC Echo client.

Error-handling needs some review, particularly for cases where
an "expand" call is interrupted.
Tim Burks 9 years ago
parent
commit
62ff862f3e

+ 14 - 23
Examples/Echo/Go/go/src/server/server.go

@@ -29,40 +29,33 @@ import (
 	"google.golang.org/grpc/credentials"
 )
 
-// [START echoserver]
 type EchoServer struct{}
 
 var echoServer EchoServer
 
-// [END echoserver]
-
-// [START get]
-func (s *EchoServer) Get(ctx context.Context, r *pb.EchoRequest) (*pb.EchoResponse, error) {
+// requests are immediately returned, no inbound or outbound streaming
+func (s *EchoServer) Get(ctx context.Context, request *pb.EchoRequest) (*pb.EchoResponse, error) {
+	fmt.Printf("Get received: %s\n", request.Text)
 	response := &pb.EchoResponse{}
-	response.Text = "Go echo get: " + r.Text
-	fmt.Printf("Get received: %s\n", r.Text)
+	response.Text = "Go echo get: " + request.Text
 	return response, nil
 }
 
-// [END get]
-
+// requests stream in and are immediately streamed out
 func (s *EchoServer) Update(stream pb.Echo_UpdateServer) error {
 	count := 0
 	for {
-		in, err := stream.Recv()
+		request, err := stream.Recv()
 		if err == io.EOF {
 			return nil
 		}
 		if err != nil {
 			return err
 		}
-
+		fmt.Printf("Update received: %s\n", request.Text)
 		response := &pb.EchoResponse{}
-		response.Text = fmt.Sprintf("Go echo update (%d): %s", count, in.Text)
+		response.Text = fmt.Sprintf("Go echo update (%d): %s", count, request.Text)
 		count++
-
-		fmt.Printf("Update received: %s\n", in.Text)
-
 		if err := stream.Send(response); err != nil {
 			return err
 		}
@@ -70,17 +63,19 @@ func (s *EchoServer) Update(stream pb.Echo_UpdateServer) error {
 	return nil
 }
 
+// requests stream in, are appended together, and are returned in a single response when the input is closed
 func (s *EchoServer) Collect(stream pb.Echo_CollectServer) error {
 	parts := []string{}
 	for {
-		in, err := stream.Recv()
+		request, err := stream.Recv()
 		if err == io.EOF {
 			break
 		}
 		if err != nil {
 			return err
 		}
-		parts = append(parts, in.Text)
+		fmt.Printf("Collect received: %s\n", request.Text)
+		parts = append(parts, request.Text)
 	}
 	response := &pb.EchoResponse{}
 	response.Text = fmt.Sprintf("Go echo collect: %s", strings.Join(parts, " "))
@@ -90,23 +85,21 @@ func (s *EchoServer) Collect(stream pb.Echo_CollectServer) error {
 	return nil
 }
 
+// a single request is accepted and split into parts which are individually returned with a time delay
 func (s *EchoServer) Expand(request *pb.EchoRequest, stream pb.Echo_ExpandServer) error {
 	fmt.Printf("Expand received: %s\n", request.Text)
 	parts := strings.Split(request.Text, " ")
-
 	for i, part := range parts {
 		response := &pb.EchoResponse{}
 		response.Text = fmt.Sprintf("Go echo expand (%d): %s", i, part)
 		if err := stream.Send(response); err != nil {
 			return err
 		}
-		time.Sleep(1*time.Second)
+		time.Sleep(1 * time.Second)
 	}
-
 	return nil
 }
 
-// [START main]
 func main() {
 	var useTLS = flag.Bool("tls", false, "Use tls for connections.")
 
@@ -134,5 +127,3 @@ func main() {
 	pb.RegisterEchoServer(grpcServer, &echoServer)
 	grpcServer.Serve(lis)
 }
-
-// [END main]

+ 39 - 10
Examples/Echo/Swift/Echo/Base.lproj/MainMenu.xib

@@ -104,14 +104,14 @@
         <window title="Echo" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" id="QvC-M9-y7g">
             <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES"/>
             <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
-            <rect key="contentRect" x="335" y="390" width="400" height="179"/>
+            <rect key="contentRect" x="335" y="390" width="400" height="181"/>
             <rect key="screenRect" x="0.0" y="0.0" width="1200" height="1897"/>
             <view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ">
-                <rect key="frame" x="0.0" y="0.0" width="400" height="179"/>
+                <rect key="frame" x="0.0" y="0.0" width="400" height="181"/>
                 <autoresizingMask key="autoresizingMask"/>
                 <subviews>
                     <textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="DcE-8I-pH3">
-                        <rect key="frame" x="20" y="137" width="360" height="22"/>
+                        <rect key="frame" x="20" y="139" width="360" height="22"/>
                         <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
                         <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" placeholderString="Type something here." drawsBackground="YES" usesSingleLineMode="YES" id="dQe-de-Ark">
                             <font key="font" metaFont="system"/>
@@ -123,16 +123,25 @@
                         </connections>
                     </textField>
                     <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="EyU-Iq-sai">
-                        <rect key="frame" x="18" y="101" width="364" height="17"/>
+                        <rect key="frame" x="86" y="89" width="296" height="17"/>
                         <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
-                        <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" placeholderString="Your message will appear here." id="QFh-Sp-bTp">
+                        <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" placeholderString="The last message received from the service." id="QFh-Sp-bTp">
+                            <font key="font" metaFont="system"/>
+                            <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
+                            <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
+                        </textFieldCell>
+                    </textField>
+                    <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="pDG-mY-8Lh">
+                        <rect key="frame" x="86" y="114" width="296" height="17"/>
+                        <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
+                        <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" placeholderString="The last message sent to the service." id="GvW-RT-DGk">
                             <font key="font" metaFont="system"/>
                             <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
                             <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
                         </textFieldCell>
                     </textField>
                     <textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="jnU-RF-9F0">
-                        <rect key="frame" x="20" y="61" width="305" height="22"/>
+                        <rect key="frame" x="20" y="59" width="305" height="22"/>
                         <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
                         <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" title="localhost:8080" placeholderString="Server Address" drawsBackground="YES" id="mDa-qc-62R">
                             <font key="font" metaFont="system"/>
@@ -144,7 +153,7 @@
                         </connections>
                     </textField>
                     <segmentedControl verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Xjz-PB-FD9">
-                        <rect key="frame" x="18" y="16" width="276" height="24"/>
+                        <rect key="frame" x="18" y="18" width="276" height="24"/>
                         <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
                         <segmentedCell key="cell" borderStyle="border" alignment="left" style="rounded" trackingMode="selectOne" id="55X-rv-QL3">
                             <font key="font" metaFont="system"/>
@@ -160,7 +169,7 @@
                         </connections>
                     </segmentedControl>
                     <button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="lTw-do-2ef">
-                        <rect key="frame" x="308" y="11" width="75" height="32"/>
+                        <rect key="frame" x="308" y="13" width="75" height="32"/>
                         <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
                         <buttonCell key="cell" type="push" title="Close" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="d5c-kK-Cnr">
                             <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
@@ -171,7 +180,7 @@
                         </connections>
                     </button>
                     <button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="1Ls-ri-Jup">
-                        <rect key="frame" x="336" y="63" width="46" height="18"/>
+                        <rect key="frame" x="336" y="61" width="46" height="18"/>
                         <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
                         <buttonCell key="cell" type="check" title="TLS" bezelStyle="regularSquare" imagePosition="left" inset="2" id="2hc-58-S2L">
                             <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
@@ -181,9 +190,27 @@
                             <action selector="buttonValueChangedWithSender:" target="8aG-cq-jvr" id="Y0Q-PO-XLx"/>
                         </connections>
                     </button>
+                    <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="bQp-KD-brj">
+                        <rect key="frame" x="18" y="114" width="36" height="17"/>
+                        <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
+                        <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Sent:" id="EdO-zp-qOf">
+                            <font key="font" metaFont="system"/>
+                            <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
+                            <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
+                        </textFieldCell>
+                    </textField>
+                    <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="htX-ek-KVD">
+                        <rect key="frame" x="18" y="89" width="64" height="17"/>
+                        <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
+                        <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Received:" id="9Rz-Rq-Utq">
+                            <font key="font" metaFont="system"/>
+                            <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
+                            <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
+                        </textFieldCell>
+                    </textField>
                 </subviews>
             </view>
-            <point key="canvasLocation" x="139" y="-275.5"/>
+            <point key="canvasLocation" x="139" y="-274.5"/>
         </window>
         <viewController id="8aG-cq-jvr" customClass="EchoViewController" customModule="Echo" customModuleProvider="target">
             <connections>
@@ -193,6 +220,8 @@
                 <outlet property="closeButton" destination="lTw-do-2ef" id="gLi-Sn-tTw"/>
                 <outlet property="messageField" destination="DcE-8I-pH3" id="G2X-oh-frG"/>
                 <outlet property="outputField" destination="EyU-Iq-sai" id="9O7-AA-RS0"/>
+                <outlet property="receivedOutputField" destination="EyU-Iq-sai" id="39V-wQ-Rfw"/>
+                <outlet property="sentOutputField" destination="pDG-mY-8Lh" id="JCm-KC-T92"/>
                 <outlet property="view" destination="EiT-Mj-1SZ" id="HXg-ep-0aP"/>
             </connections>
         </viewController>

+ 1 - 1
Examples/Echo/Swift/Echo/EchoService.swift

@@ -50,7 +50,7 @@ public class EchoGetCall {
       try! call.perform(message: requestMessageData,
                         metadata: requestMetadata)
       {(callResult) in
-        print("Client received status \(callResult.statusCode): \(callResult.statusMessage!)")
+        print("Client received status \(callResult.statusCode) \(callResult.statusMessage!)")
 
         if let messageData = callResult.resultData {
           let responseMessage = try! Echo_EchoResponse(protobuf:messageData)

+ 30 - 26
Examples/Echo/Swift/Echo/EchoViewController.swift

@@ -35,7 +35,8 @@ import gRPC
 
 class EchoViewController : NSViewController, NSTextFieldDelegate {
   @IBOutlet weak var messageField: NSTextField!
-  @IBOutlet weak var outputField: NSTextField!
+  @IBOutlet weak var sentOutputField: NSTextField!
+  @IBOutlet weak var receivedOutputField: NSTextField!
   @IBOutlet weak var addressField: NSTextField!
   @IBOutlet weak var TLSButton: NSButton!
   @IBOutlet weak var callSelectButton: NSSegmentedControl!
@@ -58,7 +59,8 @@ class EchoViewController : NSViewController, NSTextFieldDelegate {
   @IBAction func messageReturnPressed(sender: NSTextField) {
     if enabled {
       do {
-        try callServer(address:addressField.stringValue)
+        try callServer(address:addressField.stringValue,
+                       host:"example.com")
       } catch (let error) {
         print(error)
       }
@@ -74,7 +76,7 @@ class EchoViewController : NSViewController, NSTextFieldDelegate {
   }
 
   @IBAction func buttonValueChanged(sender: NSSegmentedControl) {
-    if (nowStreaming && (sender.intValue == 0)) {
+    if (nowStreaming) {
       if let error = try? self.sendClose() {
         print(error)
       }
@@ -92,15 +94,24 @@ class EchoViewController : NSViewController, NSTextFieldDelegate {
   override func viewDidLoad() {
     gRPC.initialize()
     closeButton.isEnabled = false
-  }
-
-  override func viewDidAppear() {
     // prevent the UI from trying to send messages until gRPC is initialized
     DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
       self.enabled = true
     }
   }
 
+  func displayMessageSent(_ message:String) {
+    DispatchQueue.main.async {
+      self.sentOutputField.stringValue = message
+    }
+  }
+
+  func displayMessageReceived(_ message:String) {
+    DispatchQueue.main.async {
+      self.receivedOutputField.stringValue = message
+    }
+  }
+
   func prepareService(address: String, host: String) {
     if (service != nil) {
       return
@@ -108,17 +119,17 @@ class EchoViewController : NSViewController, NSTextFieldDelegate {
     if (TLSButton.intValue == 0) {
       service = EchoService(address:address)
     } else {
-      let certificateURL = Bundle.main.url(forResource: "ssl", withExtension: "crt")!
+      let certificateURL = Bundle.main.url(forResource: "ssl",
+                                           withExtension: "crt")!
       let certificates = try! String(contentsOf: certificateURL)
       service = EchoService(address:address, certificates:certificates, host:host)
     }
     if let service = service {
-      service.channel.host = host
+      service.channel.host = "example.com" // sample override
     }
   }
 
-  func callServer(address:String) throws -> Void {
-    let host = "example.com"
+  func callServer(address:String, host:String) throws -> Void {
     prepareService(address:address, host:host)
 
     let requestMetadata = Metadata(["x-goog-api-key":"YOUR_API_KEY",
@@ -130,15 +141,12 @@ class EchoViewController : NSViewController, NSTextFieldDelegate {
         let call = service.get()
         var requestMessage = Echo_EchoRequest()
         requestMessage.text = self.messageField.stringValue
+        self.displayMessageSent(requestMessage.text)
         call.perform(request:requestMessage) {(callResult, response) in
           if let response = response {
-            DispatchQueue.main.async {
-              self.outputField.stringValue = response.text
-            }
+            self.displayMessageReceived(response.text)
           } else {
-            DispatchQueue.main.async {
-              self.outputField.stringValue = "No message received. gRPC Status \(callResult.statusCode): \(callResult.statusMessage)"
-            }
+            self.displayMessageReceived("No message received. gRPC Status \(callResult.statusCode): \(callResult.statusMessage)")
           }
         }
       }
@@ -152,6 +160,7 @@ class EchoViewController : NSViewController, NSTextFieldDelegate {
         expandCall = service.expand()
         var requestMessage = Echo_EchoRequest()
         requestMessage.text = self.messageField.stringValue
+        self.displayMessageSent(requestMessage.text)
         try expandCall!.perform(request:requestMessage) {(callResult, response) in
         }
         try! self.receiveExpandMessage()
@@ -170,7 +179,6 @@ class EchoViewController : NSViewController, NSTextFieldDelegate {
         closeButton.isEnabled = true
       }
       self.sendCollectMessage()
-
     }
     else if (self.callSelectButton.selectedSegment == 3) {
       // STREAMING UPDATE
@@ -195,9 +203,7 @@ class EchoViewController : NSViewController, NSTextFieldDelegate {
     try expandCall.receiveMessage() {(responseMessage) in
       if let responseMessage = responseMessage {
         try self.receiveExpandMessage() // prepare to receive the next message
-        DispatchQueue.main.async {
-          self.outputField.stringValue = responseMessage.text
-        }
+        self.displayMessageReceived(responseMessage.text)
       } else {
         print("expand closed")
       }
@@ -208,6 +214,7 @@ class EchoViewController : NSViewController, NSTextFieldDelegate {
     if let collectCall = collectCall {
       var requestMessage = Echo_EchoRequest()
       requestMessage.text = self.messageField.stringValue
+      self.displayMessageSent(requestMessage.text)
       _ = collectCall.sendMessage(message:requestMessage)
     }
   }
@@ -218,9 +225,7 @@ class EchoViewController : NSViewController, NSTextFieldDelegate {
     }
     try collectCall.receiveMessage() {(responseMessage) in
       if let responseMessage = responseMessage {
-        DispatchQueue.main.async {
-          self.outputField.stringValue = responseMessage.text
-        }
+        self.displayMessageReceived(responseMessage.text)
       } else {
         print("collect closed")
         self.nowStreaming = false
@@ -229,12 +234,11 @@ class EchoViewController : NSViewController, NSTextFieldDelegate {
     }
   }
 
-
-
   func sendUpdateMessage() {
     if let updateCall = updateCall {
       var requestMessage = Echo_EchoRequest()
       requestMessage.text = self.messageField.stringValue
+      self.displayMessageSent(requestMessage.text)
       _ = updateCall.sendMessage(message:requestMessage)
     }
   }
@@ -247,7 +251,7 @@ class EchoViewController : NSViewController, NSTextFieldDelegate {
       try self.receiveUpdateMessage() // prepare to receive the next message
       if let responseMessage = responseMessage {
         DispatchQueue.main.async {
-          self.outputField.stringValue = responseMessage.text
+          self.receivedOutputField.stringValue = responseMessage.text
         }
       } else {
         print("update closed")

+ 2 - 2
Packages/gRPC/Sources/Call.swift

@@ -255,7 +255,7 @@ public class Call {
       do {
         try self.sendWithoutBlocking(data: data)
       } catch (let callError) {
-        print("grpc error: \(callError)")
+        print("Call sendMessage: grpc error \(callError)")
       }
     }
     return true
@@ -275,7 +275,7 @@ public class Call {
               do {
                 try self.sendWithoutBlocking(data: nextMessage)
               } catch (let callError) {
-                print("grpc error: \(callError)")
+                print("Call sendWithoutBlocking: grpc error \(callError)")
               }
             } else {
               // otherwise, we are finished writing

+ 1 - 1
Packages/gRPC/Sources/CompletionQueue.swift

@@ -133,7 +133,7 @@ internal class CompletionQueue {
               operationGroup.success = (event.success == 1)
               try operationGroup.completion(operationGroup)
             } catch (let callError) {
-              print("grpc error: \(callError)")
+              print("CompletionQueue runToCompletion: grpc error \(callError)")
             }
             self.operationGroupsMutex.lock()
             self.operationGroups[tag] = nil