diff options
Diffstat (limited to 'examples/objective-c/route_guide/README.md')
-rw-r--r-- | examples/objective-c/route_guide/README.md | 358 |
1 files changed, 1 insertions, 357 deletions
diff --git a/examples/objective-c/route_guide/README.md b/examples/objective-c/route_guide/README.md index 15864c01f4..6a6f7c0d33 100644 --- a/examples/objective-c/route_guide/README.md +++ b/examples/objective-c/route_guide/README.md @@ -1,360 +1,4 @@ #gRPC Basics: Objective-C -This tutorial provides a basic Objective-C programmer's introduction to working with gRPC. By -walking through this example you'll learn how to: +This is the supporting code for the tutorial "[gRPC Basics: Objective-C](http://www.grpc.io/docs/tutorials/basic/objective-c.html)." -- Define a service in a .proto file. -- Generate client code using the protocol buffer compiler. -- Use the Objective-C gRPC API to write a simple client for your service. - -It assumes a passing familiarity with [protocol buffers](https://developers.google.com/protocol-buffers/docs/overview). -Note that the example in this tutorial uses the proto3 version of the protocol buffers language, -which is currently in alpha release: you can find out more in the [proto3 language guide](https://developers.google.com/protocol-buffers/docs/proto3) -and see the [release notes](https://github.com/google/protobuf/releases) for the new version in the -protocol buffers Github repository. - -This isn't a comprehensive guide to using gRPC in Objective-C: more reference documentation is -coming soon. - -- [Why use gRPC?](#why-grpc) -- [Example code and setup](#setup) -- [Try it out!](#try) -- [Defining the service](#proto) -- [Generating client code](#protoc) -- [Creating the client](#client) - -<a name="why-grpc"></a> -## Why use gRPC? - -With gRPC you can define your service once in a .proto file and implement clients and servers in any -of gRPC's supported languages, which in turn can be run in environments ranging from servers inside -Google to your own tablet - all the complexity of communication between different languages and -environments is handled for you by gRPC. You also get all the advantages of working with protocol -buffers, including efficient serialization, a simple IDL, and easy interface updating. - -gRPC and proto3 are specially suited for mobile clients: gRPC is implemented on top of HTTP/2, which -results in network bandwidth savings over using HTTP/1.1. Serialization and parsing of the proto -binary format is more efficient than the equivalent JSON, resulting in CPU and battery savings. And -proto3 uses a runtime that has been optimized over the years at Google to keep code size to a -minimum. The latter is important in Objective-C, because the ability of the compiler to strip unused -code is limited by the dynamic nature of the language. - - -<a name="setup"></a> -## Example code and setup - -The example code for our tutorial is in [examples/objective-c/route_guide](.). -To download the example, clone this repository by running the following command: -```shell -$ git clone https://github.com/grpc/grpc.git -``` - -Then change your current directory to `examples/objective-c/route_guide`: -```shell -$ cd examples/objective-c/route_guide -``` - -Our example is a simple route mapping application that lets clients get information about features -on their route, create a summary of their route, and exchange route information such as traffic -updates with the server and other clients. - -You also should have [Cocoapods](https://cocoapods.org/#install) installed, as well as the relevant -tools to generate the client library code (and a server in another language, for testing). You can -obtain the latter by following [these setup instructions](https://github.com/grpc/homebrew-grpc). - - -<a name="try"></a> -## Try it out! - -To try the sample app, we need a gRPC server running locally. Let's compile and run, for example, -the C++ server in this repository: - -```shell -$ pushd ../../cpp/route_guide -$ make -$ ./route_guide_server & -$ popd -``` - -Now have Cocoapods generate and install the client library for our .proto files: - -```shell -$ pod install -``` - -(This might have to compile OpenSSL, which takes around 15 minutes if Cocoapods doesn't have it yet -on your computer's cache). - -Finally, open the XCode workspace created by Cocoapods, and run the app. You can check the calling -code in `ViewControllers.m` and see the results in XCode's log console. - -The next sections guide you step-by-step through how this proto service is defined, how to generate -a client library from it, and how to create an app that uses that library. - - -<a name="proto"></a> -## Defining the service - -First let's look at how the service we're using is defined. A gRPC *service* and its method -*request* and *response* types using [protocol buffers](https://developers.google.com/protocol-buffers/docs/overview). -You can see the complete .proto file for our example in [`examples/protos/route_guide.proto`](../../protos/route_guide.proto). - -To define a service, you specify a named `service` in your .proto file: - -```protobuf -service RouteGuide { - ... -} -``` - -Then you define `rpc` methods inside your service definition, specifying their request and response -types. Protocol buffers let you define four kinds of service method, all of which are used in the -`RouteGuide` service: - -- A *simple RPC* where the client sends a request to the server and receives a response later, just -like a normal remote procedure call. -```protobuf - // Obtains the feature at a given position. - rpc GetFeature(Point) returns (Feature) {} -``` - -- A *response-streaming RPC* where the client sends a request to the server and gets back a stream -of response messages. You specify a response-streaming method by placing the `stream` keyword before -the *response* type. -```protobuf - // Obtains the Features available within the given Rectangle. Results are - // streamed rather than returned at once (e.g. in a response message with a - // repeated field), as the rectangle may cover a large area and contain a - // huge number of features. - rpc ListFeatures(Rectangle) returns (stream Feature) {} -``` - -- A *request-streaming RPC* where the client sends a sequence of messages to the server. Once the -client has finished writing the messages, it waits for the server to read them all and return its -response. You specify a request-streaming method by placing the `stream` keyword before the -*request* type. -```protobuf - // Accepts a stream of Points on a route being traversed, returning a - // RouteSummary when traversal is completed. - rpc RecordRoute(stream Point) returns (RouteSummary) {} -``` - -- A *bidirectional streaming RPC* where both sides send a sequence of messages to the other. The two -streams operate independently, so clients and servers can read and write in whatever order they -like: for example, the server could wait to receive all the client messages before writing its -responses, or it could alternately read a message then write a message, or some other combination of -reads and writes. The order of messages in each stream is preserved. You specify this type of method -by placing the `stream` keyword before both the request and the response. -```protobuf - // Accepts a stream of RouteNotes sent while a route is being traversed, - // while receiving other RouteNotes (e.g. from other users). - rpc RouteChat(stream RouteNote) returns (stream RouteNote) {} -``` - -Our .proto file also contains protocol buffer message type definitions for all the request and -response types used in our service methods - for example, here's the `Point` message type: -```protobuf -// Points are represented as latitude-longitude pairs in the E7 representation -// (degrees multiplied by 10**7 and rounded to the nearest integer). -// Latitudes should be in the range +/- 90 degrees and longitude should be in -// the range +/- 180 degrees (inclusive). -message Point { - int32 latitude = 1; - int32 longitude = 2; -} -``` - -You can specify a prefix to be used for your generated classes by adding the `objc_class_prefix` -option at the top of the file. For example: -```protobuf -option objc_class_prefix = "RTG"; -``` - - -<a name="protoc"></a> -## Generating client code - -Next we need to generate the gRPC client interfaces from our .proto service definition. We do this -using the protocol buffer compiler (`protoc`) with a special gRPC Objective-C plugin. - -For simplicity, we've provided a [Podspec file](RouteGuide.podspec) -that runs `protoc` for you with the appropriate plugin, input, and output, and describes how to -compile the generated files. You just need to run in this directory (`examples/objective-c/route_guide`): - -```shell -$ pod install -``` - -which, before installing the generated library in the XCode project of this sample, runs: - -```shell -$ protoc -I ../../protos --objc_out=Pods/RouteGuide --objcgrpc_out=Pods/RouteGuide ../../protos/route_guide.proto -``` - -Running this command generates the following files under `Pods/RouteGuide/`: -- `RouteGuide.pbobjc.h`, the header which declares your generated message classes. -- `RouteGuide.pbobjc.m`, which contains the implementation of your message classes. -- `RouteGuide.pbrpc.h`, the header which declares your generated service classes. -- `RouteGuide.pbrpc.m`, which contains the implementation of your service classes. - -These contain: -- All the protocol buffer code to populate, serialize, and retrieve our request and response message -types. -- A class called `RTGRouteGuide` that lets clients call the methods defined in the `RouteGuide` -service. - -You can also use the provided Podspec file to generate client code from any other proto service -definition; just replace the name (matching the file name), version, and other metadata. - - -<a name="client"></a> -## Creating the client - -In this section, we'll look at creating an Objective-C client for our `RouteGuide` service. You can -see our complete example client code in [ViewControllers.m](ViewControllers.m). -(Note: In your apps, for maintainability and readability reasons, you shouldn't put all of your view -controllers in a single file; it's done here only to simplify the learning process). - -### Constructing a client object - -To call service methods, we first need to create a client object, an instance of the generated -`RTGRouteGuide` class. The designated initializer of the class expects a `NSString *` with the -server address and port we want to connect to: - -```objective-c -#import <RouteGuide/RouteGuide.pbrpc.h> - -static NSString * const kHostAddress = @"http://localhost:50051"; - -... - -RTGRouteGuide *client = [[RTGRouteGuide alloc] initWithHost:kHostAddress]; -``` - -Notice that we've specified the HTTP scheme in the host address. This is because the server we will -be using to test our client doesn't use [TLS](http://en.wikipedia.org/wiki/Transport_Layer_Security). -This is fine because it will be running locally on our development machine. The most common case, -though, is connecting with a gRPC server on the internet, running gRPC over TLS. For that case, the -HTTPS scheme can be specified (or no scheme at all, as HTTPS is the default value). The default -value of the port is that of the scheme selected: 443 for HTTPS and 80 for HTTP. - - -### Calling service methods - -Now let's look at how we call our service methods. As you will see, all these methods are -asynchronous, so you can call them from the main thread of your app without worrying about freezing -your UI or the OS killing your app. - -#### Simple RPC - -Calling the simple RPC `GetFeature` is nearly as straightforward as calling any other asynchronous -method on Cocoa. - -```objective-c -RTGPoint *point = [RTGPoint message]; -point.latitude = 40E7; -point.longitude = -74E7; - -[client getFeatureWithRequest:point handler:^(RTGFeature *response, NSError *error) { - if (response) { - // Successful response received - } else { - // RPC error - } -}]; -``` - -As you can see, we create and populate a request protocol buffer object (in our case `RTGPoint`). -Then, we call the method on the client object, passing it the request, and a block to handle the -response (or any RPC error). If the RPC finishes successfully, the handler block is called with a -`nil` error argument, and we can read the response information from the server from the response -argument. If, instead, some RPC error happens, the handler block is called with a `nil` response -argument, and we can read the details of the problem from the error argument. - -```objective-c -NSLog(@"Found feature called %@ at %@.", response.name, response.location); -``` - -#### Streaming RPCs - -Now let's look at our streaming methods. Here's where we call the response-streaming method -`ListFeatures`, which results in our client receiving a stream of geographical `RTGFeature`s: - -```objective-c -[client listFeaturesWithRequest:rectangle - eventHandler:^(BOOL done, RTGFeature *response, NSError *error) { - if (response) { - // Element of the stream of responses received - } else if (error) { - // RPC error; the stream is over. - } - if (done) { - // The stream is over (all the responses were received, or an error occured). Do any cleanup. - } -}]; -``` - -Notice how the signature of the `eventHandler` block now includes a `BOOL done` parameter. The -`eventHandler` block can be called any number of times; only on the last call is the `done` argument -value set to `YES`. If an error occurs, the RPC finishes and the block is called with the arguments -`(YES, nil, error)`. - -The request-streaming method `RecordRoute` expects a stream of `RTGPoint`s from the cient. This -stream is passed to the method as an object of class `GRXWriter`. The simplest way to create one is -to initialize one from a `NSArray` object: - - -```objective-c -#import <RxLibrary/GRXWriter+Immediate.h> - -... - -RTGPoint *point1 = [RTGPoint message]; -point.latitude = 40E7; -point.longitude = -74E7; - -RTGPoint *point2 = [RTGPoint message]; -point.latitude = 40E7; -point.longitude = -74E7; - -GRXWriter *locationsWriter = [GRXWriter writerWithContainer:@[point1, point2]]; - -[client recordRouteWithRequestsWriter:locationsWriter - handler:^(RTGRouteSummary *response, NSError *error) { - if (response) { - NSLog(@"Finished trip with %i points", response.pointCount); - NSLog(@"Passed %i features", response.featureCount); - NSLog(@"Travelled %i meters", response.distance); - NSLog(@"It took %i seconds", response.elapsedTime); - } else { - NSLog(@"RPC error: %@", error); - } -}]; - -``` - -The `GRXWriter` class is generic enough to allow for asynchronous streams, streams of future values, -or even infinite streams. - -Finally, let's look at our bidirectional streaming RPC `RouteChat()`. The way to call a -bidirectional streaming RPC is just a combination of how to call request-streaming RPCs and -response-streaming RPCs. - -```objective-c -[client routeChatWithRequestsWriter:notesWriter - eventHandler:^(BOOL done, RTGRouteNote *note, NSError *error) { - if (note) { - NSLog(@"Got message %@ at %@", note.message, note.location); - } else if (error) { - NSLog(@"RPC error: %@", error); - } - if (done) { - NSLog(@"Chat ended."); - } -}]; -``` - -The semantics for the handler block and the `GRXWriter` argument here are exactly the same as for -our request-streaming and response-streaming methods. Although both client and server will always -get the other's messages in the order they were written, the two streams operate completely -independently. |