Merge pull request #4025 from jtattermusch/csharp_perf_instrumentation

Add simple profiling instrumentation for C#
diff --git a/.travis.yml b/.travis.yml
index b6c8062..d2d1c8b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,42 +1,32 @@
-language: cpp
-before_install:
-  - sudo add-apt-repository ppa:yjwong/gflags -y
-  - sudo add-apt-repository ppa:h-rayflood/llvm -y
-  - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF
-  - echo "deb http://download.mono-project.com/repo/debian wheezy main" | sudo tee /etc/apt/sources.list.d/mono-xamarin.list
-  - echo "deb http://download.mono-project.com/repo/debian wheezy-libtiff-compat main" | sudo tee -a /etc/apt/sources.list.d/mono-xamarin.list
-  - sudo apt-get update -qq
-  - sudo apt-get install -qq libgtest-dev libgflags-dev python-virtualenv python-dev python3-dev clang-3.5
-  - sudo pip install --upgrade virtualenv
-  - sudo pip install cpp-coveralls mako simplejson
-  - sudo apt-get install -qq mono-devel nunit
-  - wget www.nuget.org/NuGet.exe -O nuget.exe
+language: objective-c
+osx_image: xcode7.1
 env:
   global:
-    - RUBY_VERSION=2.1
-    - COVERALLS_PARALLEL=true
-    - CPPFLAGS=-I/tmp/prebuilt/include
-    - NUGET="mono nuget.exe"
-  matrix:
-    - CONFIG=opt TEST=sanity JOBS=1
-    - CONFIG=gcov TEST=c JOBS=16
-    - CONFIG=gcov TEST=c++ JOBS=16
-    - CONFIG=opt TEST=c JOBS=16
-    - CONFIG=opt TEST=c++ JOBS=16
-    - CONFIG=opt TEST=node JOBS=16
-    - CONFIG=opt TEST=ruby JOBS=16
-    - CONFIG=opt TEST=python JOBS=1
-    - CONFIG=opt TEST=csharp JOBS=16
-    - USE_GCC=4.4 CONFIG=opt TEST=build JOBS=16
-script:
-  - rvm use $RUBY_VERSION
-  - gem install bundler
-  - ./tools/run_tests/prepare_travis.sh
-  - if [ ! -z "$USE_GCC" ] ; then export CC=gcc-$USE_GCC ; export CXX=g++-$USE_GCC ; fi
-  - ./tools/run_tests/run_tests.py -l $TEST -t -j $JOBS -c $CONFIG -s 4.0
-after_success:
-  - if [ "$CONFIG" = "gcov" ] ; then coveralls --exclude third_party --exclude gens --exclude test --exclude tools --exclude src/compiler -b. --gcov-options '\-p' ; fi
+    - CONFIG=opt
+    - TEST=objc
+    - JOBS=1
+before_install:
+  - brew install gflags
+  # Pod install does this too, but we don't want the output.
+  - pod repo update --silent
+install:
+  - make grpc_objective_c_plugin
+  - pushd src/objective-c/tests
+  # Needs to be verbose, or otherwise OpenSSL's prepare_command makes Travis
+  # time out:
+  - pod install --verbose
+  - popd
+before_script:
+  - make interop_server
+  - bins/$CONFIG/interop_server --port=5050 &
+  - bins/$CONFIG/interop_server --port=5051 --use_tls &
+xcode_workspace: src/objective-c/tests/Tests.xcworkspace
+xcode_scheme:
+  - RxLibraryUnitTests
+  - InteropTestsLocalSSL
+  - InteropTestsLocalCleartext
+  # TODO(jcanizales): Investigate why they time out:
+  # - InteropTestsRemote
+xcode_sdk: iphonesimulator9.1
 notifications:
   email: false
-  webhooks:
-    - https://coveralls.io/webhook?repo_token=54IxAHPjJNdQJzJAhPU0MFpCtg7KvcydQ
diff --git a/examples/README.md b/examples/README.md
index cc4b239..84ec800 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -18,7 +18,7 @@
 * [Android Java](https://github.com/grpc/grpc-java/tree/master/examples/android)
 * [Python](python/helloworld)
 * [C#](csharp)
-* [Objective-C](objective-c/route_guide)
+* [Objective-C](objective-c/helloworld)
 * [PHP](php)
 
 ## What's in this repository?
diff --git a/examples/objective-c/auth_sample/README.md b/examples/objective-c/auth_sample/README.md
index 5ae6452..c560b7a 100644
--- a/examples/objective-c/auth_sample/README.md
+++ b/examples/objective-c/auth_sample/README.md
@@ -1,189 +1,3 @@
 #OAuth2 on gRPC: Objective-C
 
-This example application demostrates how to use OAuth2 on gRPC to make authenticated API calls on
-behalf of a user. By walking through it you'll learn how to use the Objective-C gRPC API to:
-
-- Initialize and configure a remote call object before the RPC is started.
-- Set request metadata elements on a call, which are semantically equivalent to HTTP request
-headers.
-- Read response metadata from a call, which is equivalent to HTTP response headers and trailers.
-
-It assumes you know the basics on how to make gRPC API calls using the Objective-C client library,
-as shown in the [Hello World](../helloworld)
-or [Route Guide](../route_guide) tutorials,
-and are familiar with OAuth2 concepts like _access token_.
-
-- [Example code and setup](#setup)
-- [Try it out!](#try)
-- [Create an RPC object and start it later](#rpc-object)
-- [Set request metadata of a call: Authorization header with an access token](#request-metadata)
-- [Get response metadata of a call: Auth challenge header](#response-metadata)
-
-<a name="setup"></a>
-## Example code and setup
-
-The example code for our tutorial is in [examples/objective-c/auth_sample](.).
-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/auth_sample`:
-```shell
-$ cd examples/objective-c/auth_sample
-```
-
-Our example is a simple application with two views. The first one lets a user sign in and out using
-the OAuth2 flow of Google's [iOS SignIn library](https://developers.google.com/identity/sign-in/ios/).
-(Google's library is used in this example because the test gRPC service we are going to call expects
-Google account credentials, but neither gRPC nor the Objective-C client library is tied to any
-specific OAuth2 provider). The second view makes a gRPC request to the test server, using the
-access token obtained by the first view.
-
-Note: OAuth2 libraries need the application to register and obtain an ID from the identity provider
-(in the case of this example app, Google). The app's XCode project is configured using that ID, so
-you shouldn't copy this project "as is" for your own app: it would result in your app being
-identified in the consent screen as "gRPC-AuthSample", and not having access to real Google
-services. Instead, configure your own XCode project following the [instructions here](https://developers.google.com/identity/sign-in/ios/).
-
-As with the other examples, you also should have [Cocoapods](https://cocoapods.org/#install)
-installed, as well as the relevant tools to generate the client library code. 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, first 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.
-
-The first view, `SelectUserViewController.h/m`, asks you to sign in with your Google account, and to
-give the "gRPC-AuthSample" app the following permissions:
-
-- View your email address.
-- View your basic profile info.
-- "Test scope for access to the Zoo service".
-
-This last permission, corresponding to the scope `https://www.googleapis.com/auth/xapi.zoo` doesn't
-grant any real capability: it's only used for testing. You can log out at any time.
-
-The second view, `MakeRPCViewController.h/m`, makes a gRPC request to a test server at
-https://grpc-test.sandbox.google.com, sending the access token along with the request. The test
-service simply validates the token and writes in its response which user it belongs to, and which
-scopes it gives access to. (The client application already knows those two values; it's a way to
-verify that everything went as expected).
-
-The next sections guide you step-by-step through how the gRPC call in `MakeRPCViewController` is
-performed.
-
-<a name="rpc-object"></a>
-## Create an RPC object and start it later
-
-The other basic tutorials show how to invoke an RPC by calling an asynchronous method in a generated
-client object. This shows how to initialize an object that represents the RPC, and configure it
-before starting the network request.
-
-Assume you have a proto service definition like this:
-
-```protobuf
-option objc_class_prefix = "AUTH";
-
-service TestService {
-  rpc UnaryCall(Request) returns (Response);
-}
-```
-
-A `unaryCallWithRequest:handler:` method, with which you're already familiar, is generated for the
-`AUTHTestService` class:
-
-```objective-c
-[client unaryCallWithRequest:request handler:^(AUTHResponse *response, NSError *error) {
-  ...
-}];
-```
-
-In addition, an `RPCToUnaryCallWithRequest:handler:` method is generated, which returns a
-not-yet-started RPC object:
-
-```objective-c
-#import <ProtoRPC/ProtoRPC.h>
-
-ProtoRPC *call =
-    [client RPCToUnaryCallWithRequest:request handler:^(AUTHResponse *response, NSError *error) {
-      ...
-    }];
-```
-
-The RPC represented by this object can be started at any later time like this:
-
-```objective-c
-[call start];
-```
-
-<a name="request-metadata"></a>
-## Set request metadata of a call: Authorization header with an access token
-
-The `ProtoRPC` class has a `requestMetadata` property (inherited from `GRPCCall`) defined like this:
-
-```objective-c
-- (NSMutableDictionary *)requestMetadata; // nonatomic
-- (void)setRequestMetadata:(NSDictionary *)requestMetadata; // nonatomic, copy
-```
-
-Setting it to a dictionary of metadata keys and values will have them sent on the wire when the call
-is started. gRPC metadata are pieces of information about the call sent by the client to the server
-(and vice versa). They take the form of key-value pairs and are essentially opaque to gRPC itself.
-
-```objective-c
-call.requestMetadata = @{@"My-Header": @"Value for this header",
-                         @"Another-Header": @"Its value"};
-```
-
-For convenience, the property is initialized with an empty `NSMutableDictionary`, so that request
-metadata elements can be set like this:
-
-```objective-c
-call.requestMetadata[@"My-Header"] = @"Value for this header";
-```
-
-If you have an access token, OAuth2 specifies it is to be sent in this format:
-
-```objective-c
-call.requestMetadata[@"Authorization"] = [@"Bearer " stringByAppendingString:accessToken];
-```
-
-<a name="response-metadata"></a>
-## Get response metadata of a call: Auth challenge header
-
-The `ProtoRPC` class also inherits a `responseMetadata` property, analogous to the request metadata
-we just looked at. It's defined like this:
-
-```objective-c
-@property(atomic, readonly) NSDictionary *responseMetadata;
-```
-
-To access OAuth2's authentication challenge header you write:
-
-```objective-c
-call.responseMetadata[@"www-authenticate"]
-```
-
-Note that, as gRPC metadata elements are mapped to HTTP/2 headers (or trailers), the keys of the
-response metadata are always ASCII strings in lowercase.
-
-Many uses cases of response metadata are getting more details about an RPC error. For convenience,
-when a `NSError` instance is passed to an RPC handler block, the response metadata dictionary can
-also be accessed this way:
-
-```objective-c
-error.userInfo[kGRPCStatusMetadataKey]
-```
+This is the supporting code for the tutorial "[OAuth2 on gRPC: Objective-C](http://www.grpc.io/docs/tutorials/auth/oauth2-objective-c.html)."
diff --git a/examples/objective-c/helloworld/README.md b/examples/objective-c/helloworld/README.md
index 75df1a7..81c5aaa 100644
--- a/examples/objective-c/helloworld/README.md
+++ b/examples/objective-c/helloworld/README.md
@@ -12,11 +12,13 @@
 example used in [Getting started](https://github.com/grpc/grpc/tree/master/examples).
 
 The example code for this and our other examples lives in the `examples` directory. Clone
-this repository to your local machine by running the following command:
+this repository to your local machine by running the following commands:
 
 
 ```sh
 $ git clone https://github.com/grpc/grpc.git
+$ cd grpc
+$ git submodule update --init
 ```
 
 Change your current directory to `examples/objective-c/helloworld`
@@ -53,4 +55,4 @@
 
 ## Tutorial
 
-You can find a more detailed tutorial in [gRPC Basics: Objective-C](../route_guide/README.md).
+You can find a more detailed tutorial in [gRPC Basics: Objective-C](http://www.grpc.io/docs/tutorials/basic/objective-c.html).
diff --git a/examples/objective-c/route_guide/README.md b/examples/objective-c/route_guide/README.md
index 15864c0..6a6f7c0 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:
-
-- 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.");
-  }
-}];
-```
+This is the supporting code for the tutorial "[gRPC Basics: Objective-C](http://www.grpc.io/docs/tutorials/basic/objective-c.html)."
 
-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.
diff --git a/examples/objective-c/route_guide/RouteGuideClient.xcodeproj/project.pbxproj b/examples/objective-c/route_guide/RouteGuideClient.xcodeproj/project.pbxproj
index 6ab6b27..f997755 100644
--- a/examples/objective-c/route_guide/RouteGuideClient.xcodeproj/project.pbxproj
+++ b/examples/objective-c/route_guide/RouteGuideClient.xcodeproj/project.pbxproj
@@ -121,6 +121,7 @@
 				6325277A1B1D0395003073D9 /* Frameworks */,
 				6325277B1B1D0395003073D9 /* Resources */,
 				FFE0BCF30339E7A50A989EAB /* Copy Pods Resources */,
+				B5388EC5A25E89021740B916 /* Embed Pods Frameworks */,
 			);
 			buildRules = (
 			);
@@ -177,6 +178,21 @@
 /* End PBXResourcesBuildPhase section */
 
 /* Begin PBXShellScriptBuildPhase section */
+		B5388EC5A25E89021740B916 /* Embed Pods Frameworks */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "Embed Pods Frameworks";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-RouteGuideClient/Pods-RouteGuideClient-frameworks.sh\"\n";
+			showEnvVarsInLog = 0;
+		};
 		C6FC30AD2376EC04317237C5 /* Check Pods Manifest.lock */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
diff --git a/examples/objective-c/route_guide/ViewControllers.m b/examples/objective-c/route_guide/ViewControllers.m
index cfc3338..0b1a1cf 100644
--- a/examples/objective-c/route_guide/ViewControllers.m
+++ b/examples/objective-c/route_guide/ViewControllers.m
@@ -32,13 +32,14 @@
  */
 
 #import <UIKit/UIKit.h>
+#import <GRPCClient/GRPCCall+Tests.h>
 #import <RouteGuide/RouteGuide.pbrpc.h>
 #import <RxLibrary/GRXWriter+Immediate.h>
 #import <RxLibrary/GRXWriter+Transformations.h>
 
-static NSString * const kHostAddress = @"http://localhost:50051";
+static NSString * const kHostAddress = @"localhost:50051";
 
-// Category to override RTGPoint's description.
+/** Category to override RTGPoint's description. */
 @interface RTGPoint (Description)
 - (NSString *)description;
 @end
@@ -53,7 +54,7 @@
 }
 @end
 
-// Category to give RTGRouteNote a convenience constructor.
+/** Category to give RTGRouteNote a convenience constructor. */
 @interface RTGRouteNote (Constructors)
 + (instancetype)noteWithMessage:(NSString *)message
                        latitude:(float)latitude
@@ -75,9 +76,10 @@
 
 #pragma mark Demo: Get Feature
 
-// Run the getFeature demo. Calls getFeature with a point known to have a feature and a point known
-// not to have a feature.
-
+/**
+ * Run the getFeature demo. Calls getFeature with a point known to have a feature and a point known
+ * not to have a feature.
+ */
 @interface GetFeatureViewController : UIViewController
 @end
 
@@ -86,7 +88,10 @@
 - (void)viewDidLoad {
   [super viewDidLoad];
 
-  RTGRouteGuide *client = [[RTGRouteGuide alloc] initWithHost:kHostAddress];
+  // This only needs to be done once per host, before creating service objects for that host.
+  [GRPCCall useInsecureConnectionsForHost:kHostAddress];
+
+  RTGRouteGuide *service = [[RTGRouteGuide alloc] initWithHost:kHostAddress];
 
   void (^handler)(RTGFeature *response, NSError *error) = ^(RTGFeature *response, NSError *error) {
     if (response.name.length) {
@@ -102,8 +107,8 @@
   point.latitude = 409146138;
   point.longitude = -746188906;
 
-  [client getFeatureWithRequest:point handler:handler];
-  [client getFeatureWithRequest:[RTGPoint message] handler:handler];
+  [service getFeatureWithRequest:point handler:handler];
+  [service getFeatureWithRequest:[RTGPoint message] handler:handler];
 }
 
 @end
@@ -111,9 +116,10 @@
 
 #pragma mark Demo: List Features
 
-// Run the listFeatures demo. Calls listFeatures with a rectangle containing all of the features in
-// the pre-generated database. Prints each response as it comes in.
-
+/**
+ * Run the listFeatures demo. Calls listFeatures with a rectangle containing all of the features in
+ * the pre-generated database. Prints each response as it comes in.
+ */
 @interface ListFeaturesViewController : UIViewController
 @end
 
@@ -122,7 +128,7 @@
 - (void)viewDidLoad {
   [super viewDidLoad];
 
-  RTGRouteGuide *client = [[RTGRouteGuide alloc] initWithHost:kHostAddress];
+  RTGRouteGuide *service = [[RTGRouteGuide alloc] initWithHost:kHostAddress];
 
   RTGRectangle *rectangle = [RTGRectangle message];
   rectangle.lo.latitude = 405E6;
@@ -131,8 +137,8 @@
   rectangle.hi.longitude = -745E6;
 
   NSLog(@"Looking for features between %@ and %@", rectangle.lo, rectangle.hi);
-  [client listFeaturesWithRequest:rectangle
-                     eventHandler:^(BOOL done, RTGFeature *response, NSError *error) {
+  [service listFeaturesWithRequest:rectangle
+                      eventHandler:^(BOOL done, RTGFeature *response, NSError *error) {
     if (response) {
       NSLog(@"Found feature at %@ called %@.", response.location, response.name);
     } else if (error) {
@@ -146,10 +152,11 @@
 
 #pragma mark Demo: Record Route
 
-// Run the recordRoute demo. Sends several randomly chosen points from the pre-generated feature
-// database with a variable delay in between. Prints the statistics when they are sent from the
-// server.
-
+/**
+ * Run the recordRoute demo. Sends several randomly chosen points from the pre-generated feature
+ * database with a variable delay in between. Prints the statistics when they are sent from the
+ * server.
+ */
 @interface RecordRouteViewController : UIViewController
 @end
 
@@ -171,9 +178,10 @@
     return location;
   }];
 
-  RTGRouteGuide *client = [[RTGRouteGuide alloc] initWithHost:kHostAddress];
+  RTGRouteGuide *service = [[RTGRouteGuide alloc] initWithHost:kHostAddress];
 
-  [client recordRouteWithRequestsWriter:locations handler:^(RTGRouteSummary *response, NSError *error) {
+  [service recordRouteWithRequestsWriter:locations
+                                 handler:^(RTGRouteSummary *response, NSError *error) {
     if (response) {
       NSLog(@"Finished trip with %i points", response.pointCount);
       NSLog(@"Passed %i features", response.featureCount);
@@ -190,9 +198,10 @@
 
 #pragma mark Demo: Route Chat
 
-// Run the routeChat demo. Send some chat messages, and print any chat messages that are sent from
-// the server.
-
+/**
+ * Run the routeChat demo. Send some chat messages, and print any chat messages that are sent from
+ * the server.
+ */
 @interface RouteChatViewController : UIViewController
 @end
 
@@ -210,10 +219,10 @@
     return note;
   }];
 
-  RTGRouteGuide *client = [[RTGRouteGuide alloc] initWithHost:kHostAddress];
+  RTGRouteGuide *service = [[RTGRouteGuide alloc] initWithHost:kHostAddress];
 
-  [client routeChatWithRequestsWriter:notesWriter
-                         eventHandler:^(BOOL done, RTGRouteNote *note, NSError *error) {
+  [service routeChatWithRequestsWriter:notesWriter
+                          eventHandler:^(BOOL done, RTGRouteNote *note, NSError *error) {
     if (note) {
       NSLog(@"Got message %@ at %@", note.message, note.location);
     } else if (error) {
diff --git a/gRPC.podspec b/gRPC.podspec
index d101253..2100fc8 100644
--- a/gRPC.podspec
+++ b/gRPC.podspec
@@ -566,8 +566,12 @@
     ss.header_mappings_dir = '.'
     # This isn't officially supported in Cocoapods. We've asked for an alternative:
     # https://github.com/CocoaPods/CocoaPods/issues/4386
-    ss.xcconfig = { 'HEADER_SEARCH_PATHS' => '"$(PODS_ROOT)/Headers/Private/gRPC" ' +
-                                             '"$(PODS_ROOT)/Headers/Private/gRPC/include"' }
+    ss.xcconfig = {
+      'USE_HEADERMAP' => 'NO',
+      'ALWAYS_SEARCH_USER_PATHS' => 'NO',
+      'USER_HEADER_SEARCH_PATHS' => '"$(PODS_ROOT)/Headers/Private/gRPC"',
+      'HEADER_SEARCH_PATHS' => '"$(PODS_ROOT)/Headers/Private/gRPC/include"'
+    }
 
     ss.requires_arc = false
     ss.libraries = 'z'
diff --git a/src/core/iomgr/pollset_posix.c b/src/core/iomgr/pollset_posix.c
index bce1ce9..6f478cc 100644
--- a/src/core/iomgr/pollset_posix.c
+++ b/src/core/iomgr/pollset_posix.c
@@ -121,12 +121,14 @@
       if ((flags & GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP) != 0) {
         specific_worker->reevaluate_polling_on_wakeup = 1;
       }
+      specific_worker->kicked_specifically = 1;
       grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd);
     } else if ((flags & GRPC_POLLSET_CAN_KICK_SELF) != 0) {
       GPR_TIMER_MARK("kick_yoself", 0);
       if ((flags & GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP) != 0) {
         specific_worker->reevaluate_polling_on_wakeup = 1;
       }
+      specific_worker->kicked_specifically = 1;
       grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd);
     }
   } else if (gpr_tls_get(&g_current_thread_poller) != (gpr_intptr)p) {
@@ -242,6 +244,7 @@
   /* this must happen before we (potentially) drop pollset->mu */
   worker->next = worker->prev = NULL;
   worker->reevaluate_polling_on_wakeup = 0;
+  worker->kicked_specifically = 0;
   /* TODO(ctiller): pool these */
   grpc_wakeup_fd_init(&worker->wakeup_fd);
   /* If there's work waiting for the pollset to be idle, and the
@@ -308,7 +311,7 @@
     if (worker->reevaluate_polling_on_wakeup) {
       worker->reevaluate_polling_on_wakeup = 0;
       pollset->kicked_without_pollers = 0;
-      if (queued_work) {
+      if (queued_work || worker->kicked_specifically) {
         /* If there's queued work on the list, then set the deadline to be
            immediate so we get back out of the polling loop quickly */
         deadline = gpr_inf_past(GPR_CLOCK_MONOTONIC);
diff --git a/src/core/iomgr/pollset_posix.h b/src/core/iomgr/pollset_posix.h
index 34f76db..95ebeab 100644
--- a/src/core/iomgr/pollset_posix.h
+++ b/src/core/iomgr/pollset_posix.h
@@ -51,6 +51,7 @@
 typedef struct grpc_pollset_worker {
   grpc_wakeup_fd wakeup_fd;
   int reevaluate_polling_on_wakeup;
+  int kicked_specifically;
   struct grpc_pollset_worker *next;
   struct grpc_pollset_worker *prev;
 } grpc_pollset_worker;
diff --git a/src/csharp/README.md b/src/csharp/README.md
index 2083e18..65ae0b5 100644
--- a/src/csharp/README.md
+++ b/src/csharp/README.md
@@ -1,3 +1,4 @@
+[![Nuget](https://img.shields.io/nuget/v/Grpc.svg)](http://www.nuget.org/packages/Grpc/)
 gRPC C#
 =======
 
diff --git a/src/node/README.md b/src/node/README.md
index 5d89e22..b46b986 100644
--- a/src/node/README.md
+++ b/src/node/README.md
@@ -1,3 +1,4 @@
+[![npm](https://img.shields.io/npm/v/grpc.svg)](https://www.npmjs.com/package/grpc)
 # Node.js gRPC Library
 
 ## Status
diff --git a/src/node/src/client.js b/src/node/src/client.js
index 596ea5e..3cdd550 100644
--- a/src/node/src/client.js
+++ b/src/node/src/client.js
@@ -33,6 +33,17 @@
 
 /**
  * Client module
+ *
+ * This module contains the factory method for creating Client classes, and the
+ * method calling code for all types of methods.
+ *
+ * For example, to create a client and call a method on it:
+ *
+ * var proto_obj = grpc.load(proto_file_path);
+ * var Client = proto_obj.package.subpackage.ServiceName;
+ * var client = new Client(server_address, client_credentials);
+ * var call = client.unaryMethod(arguments, callback);
+ *
  * @module
  */
 
diff --git a/src/node/src/common.js b/src/node/src/common.js
index ebaaa13..e4fe5a8 100644
--- a/src/node/src/common.js
+++ b/src/node/src/common.js
@@ -32,6 +32,8 @@
  */
 
 /**
+ * This module contains functions that are common to client and server
+ * code. None of them should be used directly by gRPC users.
  * @module
  */
 
diff --git a/src/node/src/metadata.js b/src/node/src/metadata.js
index 183c3ad..0a2f148 100644
--- a/src/node/src/metadata.js
+++ b/src/node/src/metadata.js
@@ -33,6 +33,15 @@
 
 /**
  * Metadata module
+ *
+ * This module defines the Metadata class, which represents header and trailer
+ * metadata for gRPC calls. Here is an example of how to use it:
+ *
+ * var metadata = new metadata_module.Metadata();
+ * metadata.set('key1', 'value1');
+ * metadata.add('key1', 'value2');
+ * metadata.get('key1') // returns ['value1', 'value2']
+ *
  * @module
  */
 
diff --git a/src/node/src/server.js b/src/node/src/server.js
index 89e1090..d1fb627 100644
--- a/src/node/src/server.js
+++ b/src/node/src/server.js
@@ -33,6 +33,17 @@
 
 /**
  * Server module
+ *
+ * This module contains all the server code for Node gRPC: both the Server
+ * class itself and the method handler code for all types of methods.
+ *
+ * For example, to create a Server, add a service, and start it:
+ *
+ * var server = new server_module.Server();
+ * server.addProtoService(protobuf_service_descriptor, service_implementation);
+ * server.bind('address:port', server_credential);
+ * server.start();
+ *
  * @module
  */
 
@@ -746,8 +757,8 @@
  * Binds the server to the given port, with SSL enabled if creds is given
  * @param {string} port The port that the server should bind on, in the format
  *     "address:port"
- * @param {boolean=} creds Server credential object to be used for SSL. Pass
- *     nothing for an insecure port
+ * @param {ServerCredentials=} creds Server credential object to be used for
+ *     SSL. Pass an insecure credentials object for an insecure port.
  */
 Server.prototype.bind = function(port, creds) {
   if (this.started) {
diff --git a/src/objective-c/GRPCClient/GRPCCall+Tests.m b/src/objective-c/GRPCClient/GRPCCall+Tests.m
index bade0b2..c8e8133 100644
--- a/src/objective-c/GRPCClient/GRPCCall+Tests.m
+++ b/src/objective-c/GRPCClient/GRPCCall+Tests.m
@@ -40,6 +40,9 @@
 + (void)useTestCertsPath:(NSString *)certsPath
                 testName:(NSString *)testName
                  forHost:(NSString *)host {
+  if (!host || !certsPath || !testName) {
+    [NSException raise:NSInvalidArgumentException format:@"host, path and name must be provided."];
+  }
   GRPCHost *hostConfig = [GRPCHost hostWithAddress:host];
   hostConfig.pathToCertificates = certsPath;
   hostConfig.hostNameOverride = testName;
diff --git a/src/objective-c/README.md b/src/objective-c/README.md
index a861a9f..c1d25b9 100644
--- a/src/objective-c/README.md
+++ b/src/objective-c/README.md
@@ -1,3 +1,4 @@
+[![Cocoapods](https://img.shields.io/cocoapods/v/gRPC.svg)](https://cocoapods.org/pods/gRPC)
 # gRPC for Objective-C
 
 - [Install protoc with the gRPC plugin](#install)
diff --git a/src/objective-c/tests/GRPCClientTests.m b/src/objective-c/tests/GRPCClientTests.m
index 09a55e0..00c4b88 100644
--- a/src/objective-c/tests/GRPCClientTests.m
+++ b/src/objective-c/tests/GRPCClientTests.m
@@ -42,9 +42,6 @@
 #import <RxLibrary/GRXWriteable.h>
 #import <RxLibrary/GRXWriter+Immediate.h>
 
-// These are a few tests similar to InteropTests, but which use the generic gRPC client (GRPCCall)
-// rather than a generated proto library on top of it.
-
 static NSString * const kHostAddress = @"localhost:5050";
 static NSString * const kPackage = @"grpc.testing";
 static NSString * const kService = @"TestService";
@@ -53,11 +50,10 @@
 static ProtoMethod *kEmptyCallMethod;
 static ProtoMethod *kUnaryCallMethod;
 
-// This is an observer class for testing that responseMetadata is KVO-compliant
-
+/** Observer class for testing that responseMetadata is KVO-compliant */
 @interface PassthroughObserver : NSObject
-
-- (instancetype) initWithCallback:(void (^)(NSString*, id, NSDictionary*))callback;
+- (instancetype) initWithCallback:(void (^)(NSString*, id, NSDictionary*))callback
+    NS_DESIGNATED_INITIALIZER;
 
 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change
                        context:(void *)context;
@@ -67,23 +63,38 @@
   void (^_callback)(NSString*, id, NSDictionary*);
 }
 
+- (instancetype)init {
+  return [self initWithCallback:nil];
+}
+
 - (instancetype)initWithCallback:(void (^)(NSString *, id, NSDictionary *))callback {
-  self = [super init];
-  if (self) {
+  if (!callback) {
+    return nil;
+  }
+  if ((self = [super init])) {
     _callback = callback;
   }
   return self;
-  
 }
 
-- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
-{
+- (void)observeValueForKeyPath:(NSString *)keyPath
+                      ofObject:(id)object
+                        change:(NSDictionary *)change
+                       context:(void *)context {
   _callback(keyPath, object, change);
   [object removeObserver:self forKeyPath:keyPath];
 }
 
 @end
 
+# pragma mark Tests
+
+/**
+ * A few tests similar to InteropTests, but which use the generic gRPC client (GRPCCall) rather than
+ * a generated proto library on top of it. Its RPCs are sent to a local cleartext server.
+ *
+ * TODO(jcanizales): Run them also against a local SSL server and against a remote server.
+ */
 @interface GRPCClientTests : XCTestCase
 @end
 
@@ -180,6 +191,7 @@
   [self waitForExpectationsWithTimeout:8 handler:nil];
 }
 
+// TODO(jcanizales): Activate this test against the remote server.
 - (void)testMetadata {
   __weak XCTestExpectation *expectation = [self expectationWithDescription:@"RPC unauthorized."];
 
diff --git a/src/objective-c/tests/InteropTests.h b/src/objective-c/tests/InteropTests.h
index 1045c3d..6d54343 100644
--- a/src/objective-c/tests/InteropTests.h
+++ b/src/objective-c/tests/InteropTests.h
@@ -33,11 +33,17 @@
 
 #import <XCTest/XCTest.h>
 
-// Implements tests as described here:
-// https://github.com/grpc/grpc/blob/master/doc/interop-test-descriptions.md
-
+/**
+ * Implements tests as described here:
+ * https://github.com/grpc/grpc/blob/master/doc/interop-test-descriptions.md
+ *
+ * This is an abstract class that needs to be subclassed. See |+host|.
+ */
 @interface InteropTests : XCTestCase
-// Returns @"grpc-test.sandbox.google.com".
-// Override in a subclass to perform the same tests against a different address.
+/**
+ * Host to send the RPCs to. The base implementation returns nil, which would make all tests to
+ * fail.
+ * Override in a subclass to perform these tests against a specific address.
+ */
 + (NSString *)host;
 @end
diff --git a/src/objective-c/tests/InteropTests.m b/src/objective-c/tests/InteropTests.m
index af58e2b..26877b1 100644
--- a/src/objective-c/tests/InteropTests.m
+++ b/src/objective-c/tests/InteropTests.m
@@ -78,21 +78,20 @@
 
 #pragma mark Tests
 
-static NSString * const kRemoteSSLHost = @"grpc-test.sandbox.google.com";
-
 @implementation InteropTests {
   RMTTestService *_service;
 }
 
 + (NSString *)host {
-  return kRemoteSSLHost;
+  return nil;
 }
 
 - (void)setUp {
-  _service = [RMTTestService serviceWithHost:self.class.host];
+  _service = self.class.host ? [RMTTestService serviceWithHost:self.class.host] : nil;
 }
 
 - (void)testEmptyUnaryRPC {
+  XCTAssertNotNil(self.class.host);
   __weak XCTestExpectation *expectation = [self expectationWithDescription:@"EmptyUnary"];
 
   RMTEmpty *request = [RMTEmpty message];
@@ -110,6 +109,7 @@
 }
 
 - (void)testLargeUnaryRPC {
+  XCTAssertNotNil(self.class.host);
   __weak XCTestExpectation *expectation = [self expectationWithDescription:@"LargeUnary"];
 
   RMTSimpleRequest *request = [RMTSimpleRequest message];
@@ -132,6 +132,7 @@
 }
 
 - (void)testClientStreamingRPC {
+  XCTAssertNotNil(self.class.host);
   __weak XCTestExpectation *expectation = [self expectationWithDescription:@"ClientStreaming"];
 
   RMTStreamingInputCallRequest *request1 = [RMTStreamingInputCallRequest message];
@@ -164,6 +165,7 @@
 }
 
 - (void)testServerStreamingRPC {
+  XCTAssertNotNil(self.class.host);
   __weak XCTestExpectation *expectation = [self expectationWithDescription:@"ServerStreaming"];
 
   NSArray *expectedSizes = @[@31415, @9, @2653, @58979];
@@ -200,6 +202,7 @@
 }
 
 - (void)testPingPongRPC {
+  XCTAssertNotNil(self.class.host);
   __weak XCTestExpectation *expectation = [self expectationWithDescription:@"PingPong"];
 
   NSArray *requests = @[@27182, @8, @1828, @45904];
@@ -243,6 +246,7 @@
 }
 
 - (void)testEmptyStreamRPC {
+  XCTAssertNotNil(self.class.host);
   __weak XCTestExpectation *expectation = [self expectationWithDescription:@"EmptyStream"];
   [_service fullDuplexCallWithRequestsWriter:[GRXWriter emptyWriter]
                                 eventHandler:^(BOOL done,
@@ -256,6 +260,7 @@
 }
 
 - (void)testCancelAfterBeginRPC {
+  XCTAssertNotNil(self.class.host);
   __weak XCTestExpectation *expectation = [self expectationWithDescription:@"CancelAfterBegin"];
 
   // A buffered pipe to which we never write any value acts as a writer that just hangs.
@@ -273,6 +278,7 @@
 }
 
 - (void)testCancelAfterFirstResponseRPC {
+  XCTAssertNotNil(self.class.host);
   __weak XCTestExpectation *expectation = [self expectationWithDescription:@"CancelAfterFirstResponse"];
 
   // A buffered pipe to which we write a single value but never close
diff --git a/src/objective-c/tests/InteropTestsLocalCleartext.m b/src/objective-c/tests/InteropTestsLocalCleartext.m
index 2d7d3c4..56927a8 100644
--- a/src/objective-c/tests/InteropTestsLocalCleartext.m
+++ b/src/objective-c/tests/InteropTestsLocalCleartext.m
@@ -31,15 +31,13 @@
  *
  */
 
-// Repeat of the tests in InteropTests.m, but sending the RPCs to a local cleartext server instead
-// of the remote SSL one.
-
 #import <GRPCClient/GRPCCall+Tests.h>
 
 #import "InteropTests.h"
 
 static NSString * const kLocalCleartextHost = @"localhost:5050";
 
+/** Tests in InteropTests.m, sending the RPCs to a local cleartext server. */
 @interface InteropTestsLocalCleartext : InteropTests
 @end
 
diff --git a/src/objective-c/tests/InteropTestsLocalSSL.m b/src/objective-c/tests/InteropTestsLocalSSL.m
index f69f806..9d7afef 100644
--- a/src/objective-c/tests/InteropTestsLocalSSL.m
+++ b/src/objective-c/tests/InteropTestsLocalSSL.m
@@ -31,15 +31,13 @@
  *
  */
 
-// Repeat of the tests in InteropTests.m, but sending the RPCs to a local SSL server instead of the
-// remote one.
-
 #import <GRPCClient/GRPCCall+Tests.h>
 
 #import "InteropTests.h"
 
 static NSString * const kLocalSSLHost = @"localhost:5051";
 
+/** Tests in InteropTests.m, sending the RPCs to a local SSL server. */
 @interface InteropTestsLocalSSL : InteropTests
 @end
 
diff --git a/src/objective-c/tests/InteropTestsRemote.m b/src/objective-c/tests/InteropTestsRemote.m
new file mode 100644
index 0000000..a67be98
--- /dev/null
+++ b/src/objective-c/tests/InteropTestsRemote.m
@@ -0,0 +1,50 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#import <GRPCClient/GRPCCall+Tests.h>
+
+#import "InteropTests.h"
+
+static NSString * const kRemoteSSLHost = @"grpc-test.sandbox.google.com";
+
+/** Tests in InteropTests.m, sending the RPCs to a remote SSL server. */
+@interface InteropTestsRemote : InteropTests
+@end
+
+@implementation InteropTestsRemote
+
++ (NSString *)host {
+  return kRemoteSSLHost;
+}
+
+@end
diff --git a/src/objective-c/tests/LocalClearTextTests.m b/src/objective-c/tests/LocalClearTextTests.m
deleted file mode 100644
index 976fff5..0000000
--- a/src/objective-c/tests/LocalClearTextTests.m
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- *
- * Copyright 2015, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#import <UIKit/UIKit.h>
-#import <XCTest/XCTest.h>
-
-#import <GRPCClient/GRPCCall.h>
-#import <ProtoRPC/ProtoMethod.h>
-#import <RouteGuide/RouteGuide.pbobjc.h>
-#import <RouteGuide/RouteGuide.pbrpc.h>
-#import <RxLibrary/GRXWriteable.h>
-#import <RxLibrary/GRXWriter+Immediate.h>
-
-// These tests require a gRPC "RouteGuide" sample server to be running locally. You can compile and
-// run one by following the instructions here: https://github.com/grpc/grpc/blob/master/examples/cpp/cpptutorial.md#try-it-out
-// Be sure to have the C gRPC library installed in your system (for example, by having followed the
-// instructions at https://github.com/grpc/homebrew-grpc
-
-static NSString * const kRouteGuideHost = @"http://localhost:50051";
-static NSString * const kPackage = @"routeguide";
-static NSString * const kService = @"RouteGuide";
-
-@interface LocalClearTextTests : XCTestCase
-@end
-
-@implementation LocalClearTextTests
-
-// This test currently fails: see Issue #1907.
-//- (void)testConnectionToLocalServer {
-//  __weak XCTestExpectation *expectation = [self expectationWithDescription:@"Server reachable."];
-//
-//  // This method isn't implemented by the local server.
-//  GRPCMethodName *method = [[GRPCMethodName alloc] initWithPackage:kPackage
-//                                                         interface:kService
-//                                                            method:@"EmptyCall"];
-//
-//  GRXWriter *requestsWriter = [GRXWriter writerWithValue:[NSData data]];
-//
-//  GRPCCall *call = [[GRPCCall alloc] initWithHost:kRouteGuideHost
-//                                           method:method
-//                                   requestsWriter:requestsWriter];
-//
-//  id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
-//    XCTFail(@"Received unexpected response: %@", value);
-//  } completionHandler:^(NSError *errorOrNil) {
-//    XCTAssertNotNil(errorOrNil, @"Finished without error!");
-//    XCTAssertEqual(errorOrNil.code, 12, @"Finished with unexpected error: %@", errorOrNil);
-//    [expectation fulfill];
-//  }];
-//
-//  [call startWithWriteable:responsesWriteable];
-//
-//  [self waitForExpectationsWithTimeout:8.0 handler:nil];
-//}
-
-- (void)testEmptyRPC {
-  __weak XCTestExpectation *response = [self expectationWithDescription:@"Empty response received."];
-  __weak XCTestExpectation *completion = [self expectationWithDescription:@"Empty RPC completed."];
-
-  ProtoMethod *method = [[ProtoMethod alloc] initWithPackage:kPackage
-                                                     service:kService
-                                                      method:@"RecordRoute"];
-
-  GRXWriter *requestsWriter = [GRXWriter emptyWriter];
-
-  GRPCCall *call = [[GRPCCall alloc] initWithHost:kRouteGuideHost
-                                             path:method.HTTPPath
-                                   requestsWriter:requestsWriter];
-
-  id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
-    XCTAssertNotNil(value, @"nil value received as response.");
-    XCTAssertEqual([value length], 0, @"Non-empty response received: %@", value);
-    [response fulfill];
-  } completionHandler:^(NSError *errorOrNil) {
-    XCTAssertNil(errorOrNil, @"Finished with unexpected error: %@", errorOrNil);
-    [completion fulfill];
-  }];
-
-  [call startWithWriteable:responsesWriteable];
-
-  [self waitForExpectationsWithTimeout:2.0 handler:nil];
-}
-
-- (void)testSimpleProtoRPC {
-  __weak XCTestExpectation *response = [self expectationWithDescription:@"Response received."];
-  __weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."];
-
-  ProtoMethod *method = [[ProtoMethod alloc] initWithPackage:kPackage
-                                                     service:kService
-                                                      method:@"GetFeature"];
-
-  RGDPoint *point = [RGDPoint message];
-  point.latitude = 28E7;
-  point.longitude = -15E7;
-  GRXWriter *requestsWriter = [GRXWriter writerWithValue:[point data]];
-
-  GRPCCall *call = [[GRPCCall alloc] initWithHost:kRouteGuideHost
-                                             path:method.HTTPPath
-                                   requestsWriter:requestsWriter];
-
-  id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
-    XCTAssertNotNil(value, @"nil value received as response.");
-    RGDFeature *feature = [RGDFeature parseFromData:value error:NULL];
-    XCTAssertEqualObjects(point, feature.location);
-    XCTAssertNotNil(feature.name, @"Response's name is nil.");
-    [response fulfill];
-  } completionHandler:^(NSError *errorOrNil) {
-    XCTAssertNil(errorOrNil, @"Finished with unexpected error: %@", errorOrNil);
-    [completion fulfill];
-  }];
-
-  [call startWithWriteable:responsesWriteable];
-
-  [self waitForExpectationsWithTimeout:2.0 handler:nil];
-}
-
-- (void)testSimpleProtoRPCUsingGeneratedService {
-  __weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."];
-
-  RGDPoint *point = [RGDPoint message];
-  point.latitude = 28E7;
-  point.longitude = -15E7;
-
-  RGDRouteGuide *service = [[RGDRouteGuide alloc] initWithHost:kRouteGuideHost];
-  [service getFeatureWithRequest:point handler:^(RGDFeature *response, NSError *error) {
-    XCTAssertNil(error, @"Finished with unexpected error: %@", error);
-    XCTAssertEqualObjects(point, response.location);
-    XCTAssertNotNil(response.name, @"Response's name is nil.");
-    [completion fulfill];
-  }];
-
-  [self waitForExpectationsWithTimeout:2.0 handler:nil];
-}
-@end
diff --git a/src/objective-c/tests/Podfile b/src/objective-c/tests/Podfile
index 2aa837f..2a9b894 100644
--- a/src/objective-c/tests/Podfile
+++ b/src/objective-c/tests/Podfile
@@ -6,10 +6,26 @@
 pod 'RemoteTest', :path => "../generated_libraries/RemoteTestClient"
 pod 'RouteGuide', :path => "../generated_libraries/RouteGuideClient"
 
-link_with 'AllTests'
+link_with 'AllTests',
+          'RxLibraryUnitTests',
+          'InteropTests',
+          'InteropTestsLocalSSL',
+          'InteropTestsLocalCleartext'
 
 target 'Tests' do
 end
 
 target 'AllTests' do
 end
+
+target 'RxLibraryUnitTests' do
+end
+
+target 'InteropTestsRemote' do
+end
+
+target 'InteropTestsLocalSSL' do
+end
+
+target 'InteropTestsLocalCleartext' do
+end
diff --git a/src/objective-c/tests/Tests.xcodeproj/project.pbxproj b/src/objective-c/tests/Tests.xcodeproj/project.pbxproj
index 3a1c3d9..b042961 100644
--- a/src/objective-c/tests/Tests.xcodeproj/project.pbxproj
+++ b/src/objective-c/tests/Tests.xcodeproj/project.pbxproj
@@ -7,16 +7,33 @@
 	objects = {
 
 /* Begin PBXBuildFile section */
+		036D953EE34B1FD523647ACD /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 35F2B6BF3BAE8F0DC4AFD76E /* libPods.a */; };
+		08A8BB02D19A53D902B214B8 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 35F2B6BF3BAE8F0DC4AFD76E /* libPods.a */; };
+		50267643BA114A2A724D4FDF /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 35F2B6BF3BAE8F0DC4AFD76E /* libPods.a */; };
 		6312AE4E1B1BF49B00341DEE /* GRPCClientTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6312AE4D1B1BF49B00341DEE /* GRPCClientTests.m */; };
-		63175DFF1B1B9FAF00027841 /* LocalClearTextTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 63175DFE1B1B9FAF00027841 /* LocalClearTextTests.m */; };
 		63423F4A1B150A5F006CF63C /* libTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 635697C71B14FC11007A7283 /* libTests.a */; };
-		63423F511B151B77006CF63C /* RxLibraryUnitTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 63423F501B151B77006CF63C /* RxLibraryUnitTests.m */; };
 		635697CD1B14FC11007A7283 /* Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 635697CC1B14FC11007A7283 /* Tests.m */; };
 		635ED2EC1B1A3BC400FDE5C3 /* InteropTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 635ED2EB1B1A3BC400FDE5C3 /* InteropTests.m */; };
 		63715F561B780C020029CB0B /* InteropTestsLocalCleartext.m in Sources */ = {isa = PBXBuildFile; fileRef = 63715F551B780C020029CB0B /* InteropTestsLocalCleartext.m */; };
+		6379CC4D1BE1662A001BC0A1 /* InteropTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 635ED2EB1B1A3BC400FDE5C3 /* InteropTests.m */; settings = {ASSET_TAGS = (); }; };
+		6379CC4E1BE1662B001BC0A1 /* InteropTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 635ED2EB1B1A3BC400FDE5C3 /* InteropTests.m */; settings = {ASSET_TAGS = (); }; };
+		6379CC501BE16703001BC0A1 /* InteropTestsRemote.m in Sources */ = {isa = PBXBuildFile; fileRef = 6379CC4F1BE16703001BC0A1 /* InteropTestsRemote.m */; settings = {ASSET_TAGS = (); }; };
+		6379CC511BE1683B001BC0A1 /* InteropTestsRemote.m in Sources */ = {isa = PBXBuildFile; fileRef = 6379CC4F1BE16703001BC0A1 /* InteropTestsRemote.m */; settings = {ASSET_TAGS = (); }; };
+		6379CC531BE17709001BC0A1 /* TestCertificates.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 63E240CF1B6C63DC005F3B0E /* TestCertificates.bundle */; settings = {ASSET_TAGS = (); }; };
+		63DC84181BE15179000708E8 /* libTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 635697C71B14FC11007A7283 /* libTests.a */; settings = {ASSET_TAGS = (); }; };
+		63DC841E1BE15180000708E8 /* RxLibraryUnitTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 63423F501B151B77006CF63C /* RxLibraryUnitTests.m */; settings = {ASSET_TAGS = (); }; };
+		63DC84281BE15267000708E8 /* libTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 635697C71B14FC11007A7283 /* libTests.a */; settings = {ASSET_TAGS = (); }; };
+		63DC842E1BE15278000708E8 /* RxLibraryUnitTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 63423F501B151B77006CF63C /* RxLibraryUnitTests.m */; settings = {ASSET_TAGS = (); }; };
+		63DC842F1BE1527D000708E8 /* InteropTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 635ED2EB1B1A3BC400FDE5C3 /* InteropTests.m */; settings = {ASSET_TAGS = (); }; };
+		63DC84391BE15294000708E8 /* libTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 635697C71B14FC11007A7283 /* libTests.a */; settings = {ASSET_TAGS = (); }; };
+		63DC84481BE152B5000708E8 /* libTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 635697C71B14FC11007A7283 /* libTests.a */; settings = {ASSET_TAGS = (); }; };
+		63DC844E1BE15350000708E8 /* InteropTestsLocalCleartext.m in Sources */ = {isa = PBXBuildFile; fileRef = 63715F551B780C020029CB0B /* InteropTestsLocalCleartext.m */; settings = {ASSET_TAGS = (); }; };
+		63DC844F1BE15353000708E8 /* InteropTestsLocalSSL.m in Sources */ = {isa = PBXBuildFile; fileRef = 63E240CD1B6C4E2B005F3B0E /* InteropTestsLocalSSL.m */; settings = {ASSET_TAGS = (); }; };
+		63DC84501BE153AA000708E8 /* GRPCClientTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6312AE4D1B1BF49B00341DEE /* GRPCClientTests.m */; settings = {ASSET_TAGS = (); }; };
 		63E240CE1B6C4E2B005F3B0E /* InteropTestsLocalSSL.m in Sources */ = {isa = PBXBuildFile; fileRef = 63E240CD1B6C4E2B005F3B0E /* InteropTestsLocalSSL.m */; };
 		63E240D01B6C63DC005F3B0E /* TestCertificates.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 63E240CF1B6C63DC005F3B0E /* TestCertificates.bundle */; };
 		7D8A186224D39101F90230F6 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 35F2B6BF3BAE8F0DC4AFD76E /* libPods.a */; };
+		DCFAE001609CCBFE69DFA6A1 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 35F2B6BF3BAE8F0DC4AFD76E /* libPods.a */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
@@ -27,6 +44,34 @@
 			remoteGlobalIDString = 635697C61B14FC11007A7283;
 			remoteInfo = Tests;
 		};
+		63DC84191BE15179000708E8 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 635697BF1B14FC11007A7283 /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = 635697C61B14FC11007A7283;
+			remoteInfo = Tests;
+		};
+		63DC84291BE15267000708E8 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 635697BF1B14FC11007A7283 /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = 635697C61B14FC11007A7283;
+			remoteInfo = Tests;
+		};
+		63DC843A1BE15294000708E8 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 635697BF1B14FC11007A7283 /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = 635697C61B14FC11007A7283;
+			remoteInfo = Tests;
+		};
+		63DC84491BE152B5000708E8 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 635697BF1B14FC11007A7283 /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = 635697C61B14FC11007A7283;
+			remoteInfo = Tests;
+		};
 /* End PBXContainerItemProxy section */
 
 /* Begin PBXCopyFilesBuildPhase section */
@@ -45,7 +90,6 @@
 		0A4F89D9C90E9C30990218F0 /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = "<group>"; };
 		35F2B6BF3BAE8F0DC4AFD76E /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; };
 		6312AE4D1B1BF49B00341DEE /* GRPCClientTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GRPCClientTests.m; sourceTree = "<group>"; };
-		63175DFE1B1B9FAF00027841 /* LocalClearTextTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LocalClearTextTests.m; sourceTree = "<group>"; };
 		63423F441B150A5F006CF63C /* AllTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AllTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
 		63423F501B151B77006CF63C /* RxLibraryUnitTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RxLibraryUnitTests.m; sourceTree = "<group>"; };
 		635697C71B14FC11007A7283 /* libTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libTests.a; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -53,6 +97,11 @@
 		635697D81B14FC11007A7283 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 		635ED2EB1B1A3BC400FDE5C3 /* InteropTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = InteropTests.m; sourceTree = "<group>"; };
 		63715F551B780C020029CB0B /* InteropTestsLocalCleartext.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = InteropTestsLocalCleartext.m; sourceTree = "<group>"; };
+		6379CC4F1BE16703001BC0A1 /* InteropTestsRemote.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = InteropTestsRemote.m; sourceTree = "<group>"; };
+		63DC84131BE15179000708E8 /* RxLibraryUnitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RxLibraryUnitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+		63DC84231BE15267000708E8 /* InteropTestsRemote.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = InteropTestsRemote.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+		63DC84341BE15294000708E8 /* InteropTestsLocalSSL.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = InteropTestsLocalSSL.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+		63DC84431BE152B5000708E8 /* InteropTestsLocalCleartext.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = InteropTestsLocalCleartext.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
 		63E240CC1B6C4D3A005F3B0E /* InteropTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InteropTests.h; sourceTree = "<group>"; };
 		63E240CD1B6C4E2B005F3B0E /* InteropTestsLocalSSL.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = InteropTestsLocalSSL.m; sourceTree = "<group>"; };
 		63E240CF1B6C63DC005F3B0E /* TestCertificates.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = TestCertificates.bundle; sourceTree = "<group>"; };
@@ -76,6 +125,42 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		63DC84101BE15179000708E8 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				63DC84181BE15179000708E8 /* libTests.a in Frameworks */,
+				036D953EE34B1FD523647ACD /* libPods.a in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		63DC84201BE15267000708E8 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				63DC84281BE15267000708E8 /* libTests.a in Frameworks */,
+				DCFAE001609CCBFE69DFA6A1 /* libPods.a in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		63DC84311BE15294000708E8 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				63DC84391BE15294000708E8 /* libTests.a in Frameworks */,
+				08A8BB02D19A53D902B214B8 /* libPods.a in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		63DC84401BE152B5000708E8 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				63DC84481BE152B5000708E8 /* libTests.a in Frameworks */,
+				50267643BA114A2A724D4FDF /* libPods.a in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 /* End PBXFrameworksBuildPhase section */
 
 /* Begin PBXGroup section */
@@ -112,6 +197,10 @@
 			children = (
 				635697C71B14FC11007A7283 /* libTests.a */,
 				63423F441B150A5F006CF63C /* AllTests.xctest */,
+				63DC84131BE15179000708E8 /* RxLibraryUnitTests.xctest */,
+				63DC84231BE15267000708E8 /* InteropTestsRemote.xctest */,
+				63DC84341BE15294000708E8 /* InteropTestsLocalSSL.xctest */,
+				63DC84431BE152B5000708E8 /* InteropTestsLocalCleartext.xctest */,
 			);
 			name = Products;
 			sourceTree = "<group>";
@@ -122,10 +211,10 @@
 				6312AE4D1B1BF49B00341DEE /* GRPCClientTests.m */,
 				63E240CC1B6C4D3A005F3B0E /* InteropTests.h */,
 				635ED2EB1B1A3BC400FDE5C3 /* InteropTests.m */,
+				6379CC4F1BE16703001BC0A1 /* InteropTestsRemote.m */,
 				63E240CD1B6C4E2B005F3B0E /* InteropTestsLocalSSL.m */,
 				63715F551B780C020029CB0B /* InteropTestsLocalCleartext.m */,
 				63423F501B151B77006CF63C /* RxLibraryUnitTests.m */,
-				63175DFE1B1B9FAF00027841 /* LocalClearTextTests.m */,
 				635697CC1B14FC11007A7283 /* Tests.m */,
 				635697D71B14FC11007A7283 /* Supporting Files */,
 			);
@@ -152,6 +241,7 @@
 				63423F411B150A5F006CF63C /* Frameworks */,
 				63423F421B150A5F006CF63C /* Resources */,
 				A441F71824DCB9D0CA297748 /* Copy Pods Resources */,
+				5F14F59509E10C2852014F9E /* Embed Pods Frameworks */,
 			);
 			buildRules = (
 			);
@@ -180,6 +270,90 @@
 			productReference = 635697C71B14FC11007A7283 /* libTests.a */;
 			productType = "com.apple.product-type.library.static";
 		};
+		63DC84121BE15179000708E8 /* RxLibraryUnitTests */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 63DC841B1BE15179000708E8 /* Build configuration list for PBXNativeTarget "RxLibraryUnitTests" */;
+			buildPhases = (
+				B2986CEEE8CDD4901C97598B /* Check Pods Manifest.lock */,
+				63DC840F1BE15179000708E8 /* Sources */,
+				63DC84101BE15179000708E8 /* Frameworks */,
+				63DC84111BE15179000708E8 /* Resources */,
+				4F5690DC0E6AD6663FE78B8B /* Embed Pods Frameworks */,
+				C977426A8727267BBAC7D48E /* Copy Pods Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+				63DC841A1BE15179000708E8 /* PBXTargetDependency */,
+			);
+			name = RxLibraryUnitTests;
+			productName = RxLibraryUnitTests;
+			productReference = 63DC84131BE15179000708E8 /* RxLibraryUnitTests.xctest */;
+			productType = "com.apple.product-type.bundle.unit-test";
+		};
+		63DC84221BE15267000708E8 /* InteropTestsRemote */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 63DC842B1BE15267000708E8 /* Build configuration list for PBXNativeTarget "InteropTestsRemote" */;
+			buildPhases = (
+				4C406327D3907A5E5FBA8AC9 /* Check Pods Manifest.lock */,
+				63DC841F1BE15267000708E8 /* Sources */,
+				63DC84201BE15267000708E8 /* Frameworks */,
+				63DC84211BE15267000708E8 /* Resources */,
+				900B6EDD4D16BE7D765C3885 /* Embed Pods Frameworks */,
+				C2E09DC4BD239F71160F0CC1 /* Copy Pods Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+				63DC842A1BE15267000708E8 /* PBXTargetDependency */,
+			);
+			name = InteropTestsRemote;
+			productName = InteropTests;
+			productReference = 63DC84231BE15267000708E8 /* InteropTestsRemote.xctest */;
+			productType = "com.apple.product-type.bundle.unit-test";
+		};
+		63DC84331BE15294000708E8 /* InteropTestsLocalSSL */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 63DC843C1BE15294000708E8 /* Build configuration list for PBXNativeTarget "InteropTestsLocalSSL" */;
+			buildPhases = (
+				5C20DCCB71C3991E6FE78C22 /* Check Pods Manifest.lock */,
+				63DC84301BE15294000708E8 /* Sources */,
+				63DC84311BE15294000708E8 /* Frameworks */,
+				63DC84321BE15294000708E8 /* Resources */,
+				C591129ACE9F6CC5EE03FCDE /* Embed Pods Frameworks */,
+				693DD0B453431D64EA24FD66 /* Copy Pods Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+				63DC843B1BE15294000708E8 /* PBXTargetDependency */,
+			);
+			name = InteropTestsLocalSSL;
+			productName = InteropTestsLocalSSL;
+			productReference = 63DC84341BE15294000708E8 /* InteropTestsLocalSSL.xctest */;
+			productType = "com.apple.product-type.bundle.unit-test";
+		};
+		63DC84421BE152B5000708E8 /* InteropTestsLocalCleartext */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 63DC844B1BE152B5000708E8 /* Build configuration list for PBXNativeTarget "InteropTestsLocalCleartext" */;
+			buildPhases = (
+				7418AC7B3844B29E48D24FC7 /* Check Pods Manifest.lock */,
+				63DC843F1BE152B5000708E8 /* Sources */,
+				63DC84401BE152B5000708E8 /* Frameworks */,
+				63DC84411BE152B5000708E8 /* Resources */,
+				A8E3AC66DF770B774114A30E /* Embed Pods Frameworks */,
+				8AD3130D3C58A0FB32FF2A36 /* Copy Pods Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+				63DC844A1BE152B5000708E8 /* PBXTargetDependency */,
+			);
+			name = InteropTestsLocalCleartext;
+			productName = InteropTestsLocalCleartext;
+			productReference = 63DC84431BE152B5000708E8 /* InteropTestsLocalCleartext.xctest */;
+			productType = "com.apple.product-type.bundle.unit-test";
+		};
 /* End PBXNativeTarget section */
 
 /* Begin PBXProject section */
@@ -195,6 +369,18 @@
 					635697C61B14FC11007A7283 = {
 						CreatedOnToolsVersion = 6.3.1;
 					};
+					63DC84121BE15179000708E8 = {
+						CreatedOnToolsVersion = 7.0.1;
+					};
+					63DC84221BE15267000708E8 = {
+						CreatedOnToolsVersion = 7.0.1;
+					};
+					63DC84331BE15294000708E8 = {
+						CreatedOnToolsVersion = 7.0.1;
+					};
+					63DC84421BE152B5000708E8 = {
+						CreatedOnToolsVersion = 7.0.1;
+					};
 				};
 			};
 			buildConfigurationList = 635697C21B14FC11007A7283 /* Build configuration list for PBXProject "Tests" */;
@@ -211,6 +397,10 @@
 			targets = (
 				635697C61B14FC11007A7283 /* Tests */,
 				63423F431B150A5F006CF63C /* AllTests */,
+				63DC84121BE15179000708E8 /* RxLibraryUnitTests */,
+				63DC84221BE15267000708E8 /* InteropTestsRemote */,
+				63DC84331BE15294000708E8 /* InteropTestsLocalSSL */,
+				63DC84421BE152B5000708E8 /* InteropTestsLocalCleartext */,
 			);
 		};
 /* End PBXProject section */
@@ -224,9 +414,158 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		63DC84111BE15179000708E8 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		63DC84211BE15267000708E8 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		63DC84321BE15294000708E8 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				6379CC531BE17709001BC0A1 /* TestCertificates.bundle in Resources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		63DC84411BE152B5000708E8 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 /* End PBXResourcesBuildPhase section */
 
 /* Begin PBXShellScriptBuildPhase section */
+		4C406327D3907A5E5FBA8AC9 /* Check Pods Manifest.lock */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "Check Pods Manifest.lock";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n    cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n    exit 1\nfi\n";
+			showEnvVarsInLog = 0;
+		};
+		4F5690DC0E6AD6663FE78B8B /* Embed Pods Frameworks */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "Embed Pods Frameworks";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-frameworks.sh\"\n";
+			showEnvVarsInLog = 0;
+		};
+		5C20DCCB71C3991E6FE78C22 /* Check Pods Manifest.lock */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "Check Pods Manifest.lock";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n    cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n    exit 1\nfi\n";
+			showEnvVarsInLog = 0;
+		};
+		5F14F59509E10C2852014F9E /* Embed Pods Frameworks */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "Embed Pods Frameworks";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-frameworks.sh\"\n";
+			showEnvVarsInLog = 0;
+		};
+		693DD0B453431D64EA24FD66 /* Copy Pods Resources */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "Copy Pods Resources";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n";
+			showEnvVarsInLog = 0;
+		};
+		7418AC7B3844B29E48D24FC7 /* Check Pods Manifest.lock */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "Check Pods Manifest.lock";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n    cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n    exit 1\nfi\n";
+			showEnvVarsInLog = 0;
+		};
+		8AD3130D3C58A0FB32FF2A36 /* Copy Pods Resources */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "Copy Pods Resources";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n";
+			showEnvVarsInLog = 0;
+		};
+		900B6EDD4D16BE7D765C3885 /* Embed Pods Frameworks */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "Embed Pods Frameworks";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-frameworks.sh\"\n";
+			showEnvVarsInLog = 0;
+		};
 		914ADDD7106BA9BB8A7E569F /* Check Pods Manifest.lock */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
@@ -257,6 +596,81 @@
 			shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n";
 			showEnvVarsInLog = 0;
 		};
+		A8E3AC66DF770B774114A30E /* Embed Pods Frameworks */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "Embed Pods Frameworks";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-frameworks.sh\"\n";
+			showEnvVarsInLog = 0;
+		};
+		B2986CEEE8CDD4901C97598B /* Check Pods Manifest.lock */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "Check Pods Manifest.lock";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n    cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n    exit 1\nfi\n";
+			showEnvVarsInLog = 0;
+		};
+		C2E09DC4BD239F71160F0CC1 /* Copy Pods Resources */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "Copy Pods Resources";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n";
+			showEnvVarsInLog = 0;
+		};
+		C591129ACE9F6CC5EE03FCDE /* Embed Pods Frameworks */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "Embed Pods Frameworks";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-frameworks.sh\"\n";
+			showEnvVarsInLog = 0;
+		};
+		C977426A8727267BBAC7D48E /* Copy Pods Resources */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "Copy Pods Resources";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n";
+			showEnvVarsInLog = 0;
+		};
 /* End PBXShellScriptBuildPhase section */
 
 /* Begin PBXSourcesBuildPhase section */
@@ -265,11 +679,11 @@
 			buildActionMask = 2147483647;
 			files = (
 				63715F561B780C020029CB0B /* InteropTestsLocalCleartext.m in Sources */,
-				63175DFF1B1B9FAF00027841 /* LocalClearTextTests.m in Sources */,
-				63423F511B151B77006CF63C /* RxLibraryUnitTests.m in Sources */,
+				6379CC511BE1683B001BC0A1 /* InteropTestsRemote.m in Sources */,
 				63E240CE1B6C4E2B005F3B0E /* InteropTestsLocalSSL.m in Sources */,
 				6312AE4E1B1BF49B00341DEE /* GRPCClientTests.m in Sources */,
 				635ED2EC1B1A3BC400FDE5C3 /* InteropTests.m in Sources */,
+				63DC842E1BE15278000708E8 /* RxLibraryUnitTests.m in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -281,6 +695,42 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		63DC840F1BE15179000708E8 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				63DC841E1BE15180000708E8 /* RxLibraryUnitTests.m in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		63DC841F1BE15267000708E8 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				63DC842F1BE1527D000708E8 /* InteropTests.m in Sources */,
+				6379CC501BE16703001BC0A1 /* InteropTestsRemote.m in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		63DC84301BE15294000708E8 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				63DC844F1BE15353000708E8 /* InteropTestsLocalSSL.m in Sources */,
+				6379CC4D1BE1662A001BC0A1 /* InteropTests.m in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		63DC843F1BE152B5000708E8 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				63DC84501BE153AA000708E8 /* GRPCClientTests.m in Sources */,
+				63DC844E1BE15350000708E8 /* InteropTestsLocalCleartext.m in Sources */,
+				6379CC4E1BE1662B001BC0A1 /* InteropTests.m in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 /* End PBXSourcesBuildPhase section */
 
 /* Begin PBXTargetDependency section */
@@ -289,6 +739,26 @@
 			target = 635697C61B14FC11007A7283 /* Tests */;
 			targetProxy = 63423F4B1B150A5F006CF63C /* PBXContainerItemProxy */;
 		};
+		63DC841A1BE15179000708E8 /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = 635697C61B14FC11007A7283 /* Tests */;
+			targetProxy = 63DC84191BE15179000708E8 /* PBXContainerItemProxy */;
+		};
+		63DC842A1BE15267000708E8 /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = 635697C61B14FC11007A7283 /* Tests */;
+			targetProxy = 63DC84291BE15267000708E8 /* PBXContainerItemProxy */;
+		};
+		63DC843B1BE15294000708E8 /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = 635697C61B14FC11007A7283 /* Tests */;
+			targetProxy = 63DC843A1BE15294000708E8 /* PBXContainerItemProxy */;
+		};
+		63DC844A1BE152B5000708E8 /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = 635697C61B14FC11007A7283 /* Tests */;
+			targetProxy = 63DC84491BE152B5000708E8 /* PBXContainerItemProxy */;
+		};
 /* End PBXTargetDependency section */
 
 /* Begin XCBuildConfiguration section */
@@ -418,6 +888,110 @@
 			};
 			name = Release;
 		};
+		63DC841C1BE15179000708E8 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = FF7B5489BCFE40111D768DD0 /* Pods.debug.xcconfig */;
+			buildSettings = {
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				ENABLE_TESTABILITY = YES;
+				INFOPLIST_FILE = Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = io.grpc.RxLibraryUnitTests;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Debug;
+		};
+		63DC841D1BE15179000708E8 /* Release */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 0A4F89D9C90E9C30990218F0 /* Pods.release.xcconfig */;
+			buildSettings = {
+				INFOPLIST_FILE = Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = io.grpc.RxLibraryUnitTests;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Release;
+		};
+		63DC842C1BE15267000708E8 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = FF7B5489BCFE40111D768DD0 /* Pods.debug.xcconfig */;
+			buildSettings = {
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				ENABLE_TESTABILITY = YES;
+				INFOPLIST_FILE = Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = io.grpc.InteropTests;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Debug;
+		};
+		63DC842D1BE15267000708E8 /* Release */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 0A4F89D9C90E9C30990218F0 /* Pods.release.xcconfig */;
+			buildSettings = {
+				INFOPLIST_FILE = Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = io.grpc.InteropTests;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Release;
+		};
+		63DC843D1BE15294000708E8 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = FF7B5489BCFE40111D768DD0 /* Pods.debug.xcconfig */;
+			buildSettings = {
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				ENABLE_TESTABILITY = YES;
+				INFOPLIST_FILE = Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = io.grpc.InteropTestsLocalSSL;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Debug;
+		};
+		63DC843E1BE15294000708E8 /* Release */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 0A4F89D9C90E9C30990218F0 /* Pods.release.xcconfig */;
+			buildSettings = {
+				INFOPLIST_FILE = Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = io.grpc.InteropTestsLocalSSL;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Release;
+		};
+		63DC844C1BE152B5000708E8 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = FF7B5489BCFE40111D768DD0 /* Pods.debug.xcconfig */;
+			buildSettings = {
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				ENABLE_TESTABILITY = YES;
+				INFOPLIST_FILE = Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = io.grpc.InteropTestsLocalCleartext;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Debug;
+		};
+		63DC844D1BE152B5000708E8 /* Release */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 0A4F89D9C90E9C30990218F0 /* Pods.release.xcconfig */;
+			buildSettings = {
+				INFOPLIST_FILE = Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = io.grpc.InteropTestsLocalCleartext;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Release;
+		};
 /* End XCBuildConfiguration section */
 
 /* Begin XCConfigurationList section */
@@ -448,6 +1022,42 @@
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;
 		};
+		63DC841B1BE15179000708E8 /* Build configuration list for PBXNativeTarget "RxLibraryUnitTests" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				63DC841C1BE15179000708E8 /* Debug */,
+				63DC841D1BE15179000708E8 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		63DC842B1BE15267000708E8 /* Build configuration list for PBXNativeTarget "InteropTestsRemote" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				63DC842C1BE15267000708E8 /* Debug */,
+				63DC842D1BE15267000708E8 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		63DC843C1BE15294000708E8 /* Build configuration list for PBXNativeTarget "InteropTestsLocalSSL" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				63DC843D1BE15294000708E8 /* Debug */,
+				63DC843E1BE15294000708E8 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		63DC844B1BE152B5000708E8 /* Build configuration list for PBXNativeTarget "InteropTestsLocalCleartext" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				63DC844C1BE152B5000708E8 /* Debug */,
+				63DC844D1BE152B5000708E8 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
 /* End XCConfigurationList section */
 	};
 	rootObject = 635697BF1B14FC11007A7283 /* Project object */;
diff --git a/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/AllTests.xcscheme b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/AllTests.xcscheme
index a7e0ed1..e6a052a 100644
--- a/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/AllTests.xcscheme
+++ b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/AllTests.xcscheme
@@ -23,10 +23,10 @@
       </BuildActionEntries>
    </BuildAction>
    <TestAction
+      buildConfiguration = "Debug"
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
-      shouldUseLaunchSchemeArgsEnv = "YES"
-      buildConfiguration = "Debug">
+      shouldUseLaunchSchemeArgsEnv = "YES">
       <Testables>
          <TestableReference
             skipped = "NO">
@@ -45,6 +45,9 @@
                   Identifier = "GRPCClientTests/testMetadata">
                </Test>
                <Test
+                  Identifier = "InteropTests">
+               </Test>
+               <Test
                   Identifier = "LocalClearTextTests">
                </Test>
                <Test
@@ -62,15 +65,18 @@
             ReferencedContainer = "container:Tests.xcodeproj">
          </BuildableReference>
       </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
    </TestAction>
    <LaunchAction
+      buildConfiguration = "Debug"
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
       launchStyle = "0"
       useCustomWorkingDirectory = "NO"
-      buildConfiguration = "Debug"
       ignoresPersistentStateOnLaunch = "NO"
       debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
       allowLocationSimulation = "YES">
       <MacroExpansion>
          <BuildableReference
@@ -85,10 +91,10 @@
       </AdditionalOptions>
    </LaunchAction>
    <ProfileAction
+      buildConfiguration = "Release"
       shouldUseLaunchSchemeArgsEnv = "YES"
       savedToolIdentifier = ""
       useCustomWorkingDirectory = "NO"
-      buildConfiguration = "Release"
       debugDocumentVersioning = "YES">
       <MacroExpansion>
          <BuildableReference
diff --git a/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTestsLocalCleartext.xcscheme b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTestsLocalCleartext.xcscheme
new file mode 100644
index 0000000..ce358bf
--- /dev/null
+++ b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTestsLocalCleartext.xcscheme
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0700"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "NO"
+            buildForArchiving = "NO"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "63DC84421BE152B5000708E8"
+               BuildableName = "InteropTestsLocalCleartext.xctest"
+               BlueprintName = "InteropTestsLocalCleartext"
+               ReferencedContainer = "container:Tests.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "63DC84421BE152B5000708E8"
+               BuildableName = "InteropTestsLocalCleartext.xctest"
+               BlueprintName = "InteropTestsLocalCleartext"
+               ReferencedContainer = "container:Tests.xcodeproj">
+            </BuildableReference>
+            <SkippedTests>
+               <Test
+                  Identifier = "GRPCClientTests/testConnectionToRemoteServer">
+               </Test>
+               <Test
+                  Identifier = "GRPCClientTests/testMetadata">
+               </Test>
+               <Test
+                  Identifier = "InteropTests">
+               </Test>
+            </SkippedTests>
+         </TestableReference>
+      </Testables>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "63DC84421BE152B5000708E8"
+            BuildableName = "InteropTestsLocalCleartext.xctest"
+            BlueprintName = "InteropTestsLocalCleartext"
+            ReferencedContainer = "container:Tests.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "63DC84421BE152B5000708E8"
+            BuildableName = "InteropTestsLocalCleartext.xctest"
+            BlueprintName = "InteropTestsLocalCleartext"
+            ReferencedContainer = "container:Tests.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTestsLocalSSL.xcscheme b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTestsLocalSSL.xcscheme
new file mode 100644
index 0000000..f268da1
--- /dev/null
+++ b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTestsLocalSSL.xcscheme
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0700"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "NO"
+            buildForArchiving = "NO"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "63DC84331BE15294000708E8"
+               BuildableName = "InteropTestsLocalSSL.xctest"
+               BlueprintName = "InteropTestsLocalSSL"
+               ReferencedContainer = "container:Tests.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "63DC84331BE15294000708E8"
+               BuildableName = "InteropTestsLocalSSL.xctest"
+               BlueprintName = "InteropTestsLocalSSL"
+               ReferencedContainer = "container:Tests.xcodeproj">
+            </BuildableReference>
+            <SkippedTests>
+               <Test
+                  Identifier = "InteropTests">
+               </Test>
+            </SkippedTests>
+         </TestableReference>
+      </Testables>
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "63DC84331BE15294000708E8"
+            BuildableName = "InteropTestsLocalSSL.xctest"
+            BlueprintName = "InteropTestsLocalSSL"
+            ReferencedContainer = "container:Tests.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "63DC84331BE15294000708E8"
+            BuildableName = "InteropTestsLocalSSL.xctest"
+            BlueprintName = "InteropTestsLocalSSL"
+            ReferencedContainer = "container:Tests.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTestsRemote.xcscheme b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTestsRemote.xcscheme
new file mode 100644
index 0000000..186d720
--- /dev/null
+++ b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTestsRemote.xcscheme
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0700"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "NO"
+            buildForArchiving = "NO"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "63DC84221BE15267000708E8"
+               BuildableName = "InteropTestsRemote.xctest"
+               BlueprintName = "InteropTestsRemote"
+               ReferencedContainer = "container:Tests.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "63DC84221BE15267000708E8"
+               BuildableName = "InteropTestsRemote.xctest"
+               BlueprintName = "InteropTestsRemote"
+               ReferencedContainer = "container:Tests.xcodeproj">
+            </BuildableReference>
+            <SkippedTests>
+               <Test
+                  Identifier = "InteropTests">
+               </Test>
+            </SkippedTests>
+         </TestableReference>
+      </Testables>
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "63DC84221BE15267000708E8"
+            BuildableName = "InteropTestsRemote.xctest"
+            BlueprintName = "InteropTestsRemote"
+            ReferencedContainer = "container:Tests.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "63DC84221BE15267000708E8"
+            BuildableName = "InteropTestsRemote.xctest"
+            BlueprintName = "InteropTestsRemote"
+            ReferencedContainer = "container:Tests.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/RxLibraryUnitTests.xcscheme b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/RxLibraryUnitTests.xcscheme
new file mode 100644
index 0000000..3abc1d4
--- /dev/null
+++ b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/RxLibraryUnitTests.xcscheme
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0700"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "NO"
+            buildForArchiving = "NO"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "63DC84121BE15179000708E8"
+               BuildableName = "RxLibraryUnitTests.xctest"
+               BlueprintName = "RxLibraryUnitTests"
+               ReferencedContainer = "container:Tests.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "63DC84121BE15179000708E8"
+               BuildableName = "RxLibraryUnitTests.xctest"
+               BlueprintName = "RxLibraryUnitTests"
+               ReferencedContainer = "container:Tests.xcodeproj">
+            </BuildableReference>
+         </TestableReference>
+      </Testables>
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "63DC84121BE15179000708E8"
+            BuildableName = "RxLibraryUnitTests.xctest"
+            BlueprintName = "RxLibraryUnitTests"
+            ReferencedContainer = "container:Tests.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "63DC84121BE15179000708E8"
+            BuildableName = "RxLibraryUnitTests.xctest"
+            BlueprintName = "RxLibraryUnitTests"
+            ReferencedContainer = "container:Tests.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/src/ruby/README.md b/src/ruby/README.md
index e6ffadc..76d9f76 100644
--- a/src/ruby/README.md
+++ b/src/ruby/README.md
@@ -1,3 +1,4 @@
+[![Gem](https://img.shields.io/gem/v/grpc.svg)](https://rubygems.org/gems/grpc/)
 gRPC Ruby
 =========
 
diff --git a/templates/gRPC.podspec.template b/templates/gRPC.podspec.template
index 3b96fe2..3885cb3 100644
--- a/templates/gRPC.podspec.template
+++ b/templates/gRPC.podspec.template
@@ -88,8 +88,12 @@
       ss.header_mappings_dir = '.'
       # This isn't officially supported in Cocoapods. We've asked for an alternative:
       # https://github.com/CocoaPods/CocoaPods/issues/4386
-      ss.xcconfig = { 'HEADER_SEARCH_PATHS' => '"$(PODS_ROOT)/Headers/Private/gRPC" ' +
-                                               '"$(PODS_ROOT)/Headers/Private/gRPC/include"' }
+      ss.xcconfig = {
+        'USE_HEADERMAP' => 'NO',
+        'ALWAYS_SEARCH_USER_PATHS' => 'NO',
+        'USER_HEADER_SEARCH_PATHS' => '"$(PODS_ROOT)/Headers/Private/gRPC"',
+        'HEADER_SEARCH_PATHS' => '"$(PODS_ROOT)/Headers/Private/gRPC/include"'
+      }
 
       ss.requires_arc = false
       ss.libraries = 'z'
diff --git a/test/core/client_config/lb_policies_test.c b/test/core/client_config/lb_policies_test.c
index a9edf38..3eb6f11 100644
--- a/test/core/client_config/lb_policies_test.c
+++ b/test/core/client_config/lb_policies_test.c
@@ -211,29 +211,32 @@
   gpr_free(f);
 }
 
+typedef struct request_data {
+  grpc_metadata_array initial_metadata_recv;
+  grpc_metadata_array trailing_metadata_recv;
+  char *details;
+  size_t details_capacity;
+  grpc_status_code status;
+  grpc_call_details *call_details;
+} request_data;
+
 /** Returns connection sequence (server indices), which must be freed */
 int *perform_request(servers_fixture *f, grpc_channel *client,
-                     const test_spec *spec) {
+                     request_data *rdata, const test_spec *spec) {
   grpc_call *c;
   int s_idx;
   int *s_valid;
   gpr_timespec deadline;
   grpc_op ops[6];
   grpc_op *op;
-  grpc_status_code status;
-  char *details;
-  size_t details_capacity;
   int was_cancelled;
-  grpc_call_details *call_details;
   size_t i, iter_num;
   grpc_event ev;
   int read_tag;
   int *connection_sequence;
-  grpc_metadata_array initial_metadata_recv;
-  grpc_metadata_array trailing_metadata_recv;
 
   s_valid = gpr_malloc(sizeof(int) * f->num_servers);
-  call_details = gpr_malloc(sizeof(grpc_call_details) * f->num_servers);
+  rdata->call_details = gpr_malloc(sizeof(grpc_call_details) * f->num_servers);
   connection_sequence = gpr_malloc(sizeof(int) * spec->num_iters);
 
   /* Send a trivial request. */
@@ -241,8 +244,8 @@
 
   for (iter_num = 0; iter_num < spec->num_iters; iter_num++) {
     cq_verifier *cqv = cq_verifier_create(f->cq);
-    details = NULL;
-    details_capacity = 0;
+    rdata->details = NULL;
+    rdata->details_capacity = 0;
     was_cancelled = 2;
 
     for (i = 0; i < f->num_servers; i++) {
@@ -255,11 +258,11 @@
     }
 
     connection_sequence[iter_num] = -1;
-    grpc_metadata_array_init(&initial_metadata_recv);
-    grpc_metadata_array_init(&trailing_metadata_recv);
+    grpc_metadata_array_init(&rdata->initial_metadata_recv);
+    grpc_metadata_array_init(&rdata->trailing_metadata_recv);
 
     for (i = 0; i < f->num_servers; i++) {
-      grpc_call_details_init(&call_details[i]);
+      grpc_call_details_init(&rdata->call_details[i]);
     }
     memset(s_valid, 0, f->num_servers * sizeof(int));
 
@@ -278,15 +281,15 @@
     op->reserved = NULL;
     op++;
     op->op = GRPC_OP_RECV_INITIAL_METADATA;
-    op->data.recv_initial_metadata = &initial_metadata_recv;
+    op->data.recv_initial_metadata = &rdata->initial_metadata_recv;
     op->flags = 0;
     op->reserved = NULL;
     op++;
     op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
-    op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
-    op->data.recv_status_on_client.status = &status;
-    op->data.recv_status_on_client.status_details = &details;
-    op->data.recv_status_on_client.status_details_capacity = &details_capacity;
+    op->data.recv_status_on_client.trailing_metadata = &rdata->trailing_metadata_recv;
+    op->data.recv_status_on_client.status = &rdata->status;
+    op->data.recv_status_on_client.status_details = &rdata->details;
+    op->data.recv_status_on_client.status_details_capacity = &rdata->details_capacity;
     op->flags = 0;
     op->reserved = NULL;
     op++;
@@ -299,7 +302,7 @@
       if (f->servers[i] != NULL) {
         GPR_ASSERT(GRPC_CALL_OK ==
                    grpc_server_request_call(f->servers[i], &f->server_calls[i],
-                                            &call_details[i],
+                                            &rdata->call_details[i],
                                             &f->request_metadata_recv[i], f->cq,
                                             f->cq, tag(1000 + (int)i)));
       }
@@ -348,11 +351,12 @@
       cq_expect_completion(cqv, tag(1), 1);
       cq_verify(cqv);
 
-      GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED);
-      GPR_ASSERT(0 == strcmp(details, "xyz"));
-      GPR_ASSERT(0 == strcmp(call_details[s_idx].method, "/foo"));
-      GPR_ASSERT(0 == strcmp(call_details[s_idx].host, "foo.test.google.fr"));
+      GPR_ASSERT(rdata->status == GRPC_STATUS_UNIMPLEMENTED);
+      GPR_ASSERT(0 == strcmp(rdata->details, "xyz"));
+      GPR_ASSERT(0 == strcmp(rdata->call_details[s_idx].method, "/foo"));
+      GPR_ASSERT(0 == strcmp(rdata->call_details[s_idx].host, "foo.test.google.fr"));
       GPR_ASSERT(was_cancelled == 1);
+    } else {
     }
 
     for (i = 0; i < f->num_servers; i++) {
@@ -361,20 +365,20 @@
       }
       grpc_metadata_array_destroy(&f->request_metadata_recv[i]);
     }
-    grpc_metadata_array_destroy(&initial_metadata_recv);
-    grpc_metadata_array_destroy(&trailing_metadata_recv);
+    grpc_metadata_array_destroy(&rdata->initial_metadata_recv);
+    grpc_metadata_array_destroy(&rdata->trailing_metadata_recv);
 
     cq_verifier_destroy(cqv);
 
     grpc_call_destroy(c);
 
     for (i = 0; i < f->num_servers; i++) {
-      grpc_call_details_destroy(&call_details[i]);
+      grpc_call_details_destroy(&rdata->call_details[i]);
     }
-    gpr_free(details);
+    gpr_free(rdata->details);
   }
 
-  gpr_free(call_details);
+  gpr_free(rdata->call_details);
   gpr_free(s_valid);
 
   return connection_sequence;
@@ -436,6 +440,7 @@
   char *client_hostport;
   char *servers_hostports_str;
   int *actual_connection_sequence;
+  request_data rdata;
   servers_fixture *f = setup_servers("127.0.0.1", spec->num_servers);
 
   /* Create client. */
@@ -448,7 +453,7 @@
   gpr_log(GPR_INFO, "Testing '%s' with servers=%s client=%s", spec->description,
           servers_hostports_str, client_hostport);
 
-  actual_connection_sequence = perform_request(f, client, spec);
+  actual_connection_sequence = perform_request(f, client, &rdata, spec);
 
   spec->verifier(f, client, actual_connection_sequence, spec->num_iters);
 
diff --git a/test/core/support/cpu_test.c b/test/core/support/cpu_test.c
index 6559c1b..fa83878 100644
--- a/test/core/support/cpu_test.c
+++ b/test/core/support/cpu_test.c
@@ -81,9 +81,9 @@
   gpr_uint32 cpu;
   int r = 12345678;
   int i, j;
-  for (i = 0; i < 1000; i++) {
+  for (i = 0; i < 1000 / GRPC_TEST_SLOWDOWN_FACTOR; i++) {
     /* run for a bit - just calculate something random. */
-    for (j = 0; j < 1000000; j++) {
+    for (j = 0; j < 1000000 / GRPC_TEST_SLOWDOWN_FACTOR; j++) {
       r = (r * 17) & ((r - i) | (r * i));
     }
     cpu = gpr_cpu_current_cpu();
diff --git a/tools/run_tests/jobset.py b/tools/run_tests/jobset.py
index 8343441..a8ff9f6 100755
--- a/tools/run_tests/jobset.py
+++ b/tools/run_tests/jobset.py
@@ -182,6 +182,7 @@
     self.state = 'UNKNOWN'
     self.returncode = -1
     self.elapsed_time = 0
+    self.num_failures = 0
     self.retries = 0
     self.message = ''
     
@@ -243,6 +244,7 @@
             self._spec.shortname, self._process.returncode, self._process.pid),
             stdout, do_newline=True)
           self._retries += 1
+          self.result.num_failures += 1
           self.result.retries = self._timeout_retries + self._retries
           self.start()
         else:
@@ -252,6 +254,7 @@
                 self._spec.shortname, self._process.returncode, self._process.pid),
                 stdout, do_newline=True)
           self.result.state = 'FAILED'
+          self.result.num_failures += 1
           self.result.returncode = self._process.returncode
           if self._xml_test is not None:
             ET.SubElement(self._xml_test, 'failure', message='Failure')
@@ -271,6 +274,7 @@
       if self._timeout_retries < self._spec.timeout_retries:
         message('TIMEOUT_FLAKE', self._spec.shortname, stdout, do_newline=True)
         self._timeout_retries += 1
+        self.result.num_failures += 1
         self.result.retries = self._timeout_retries + self._retries
         if self._spec.kill_handler:
           self._spec.kill_handler(self)
@@ -280,6 +284,7 @@
         message('TIMEOUT', self._spec.shortname, stdout, do_newline=True)
         self.kill()
         self.result.state = 'TIMEOUT'
+        self.result.num_failures += 1
         if self._xml_test is not None:
           ET.SubElement(self._xml_test, 'system-out').text = filtered_stdout
           ET.SubElement(self._xml_test, 'error', message='Timeout')
@@ -294,7 +299,7 @@
 
   def suppress_failure_message(self):
     self._suppress_failure_message = True
-
+    
 
 class Jobset(object):
   """Manages one run of jobs."""
@@ -347,7 +352,7 @@
                 self._add_env,
                 self._xml_report)
       self._running.add(job)
-      self.resultset[job.GetSpec().shortname] = None
+      self.resultset[job.GetSpec().shortname] = []
     return True
 
   def reap(self):
@@ -367,7 +372,7 @@
         break
       for job in dead:
         self._completed += 1
-        self.resultset[job.GetSpec().shortname] = job.result
+        self.resultset[job.GetSpec().shortname].append(job.result)
         self._running.remove(job)
       if dead: return
       if (not self._travis):
diff --git a/tools/run_tests/run_interop_tests.py b/tools/run_tests/run_interop_tests.py
index e1d60b2..729f962 100755
--- a/tools/run_tests/run_interop_tests.py
+++ b/tools/run_tests/run_interop_tests.py
@@ -474,7 +474,9 @@
 # TODO(adelez): Use mako template.
 def fill_one_test_result(shortname, resultset, html_str):
   if shortname in resultset:
-    result = resultset[shortname]
+    # Because interop tests does not have runs_per_test flag, each test is run
+    # once. So there should only be one element for each result.
+    result = resultset[shortname][0] 
     if result.state == 'PASSED':
       html_str = '%s<td bgcolor=\"green\">PASS</td>\n' % html_str
     else:
@@ -483,7 +485,8 @@
         if result.returncode > 0:
           tooltip = 'returncode: %d ' % result.returncode
         if result.message:
-          tooltip = '%smessage: %s' % (tooltip, result.message)     
+          escaped_msg = result.message.replace('"', '&quot;')
+          tooltip = '%smessage: %s' % (tooltip, escaped_msg)     
       if result.state == 'FAILED':
         html_str = '%s<td bgcolor=\"red\">' % html_str
         if tooltip:  
diff --git a/tools/run_tests/run_tests.py b/tools/run_tests/run_tests.py
index 8482b2f..4232637 100755
--- a/tools/run_tests/run_tests.py
+++ b/tools/run_tests/run_tests.py
@@ -812,6 +812,23 @@
         raise
 
 
+def _calculate_num_runs_failures(list_of_results):
+  """Caculate number of runs and failures for a particular test.
+
+  Args:
+    list_of_results: (List) of JobResult object.
+  Returns:
+    A tuple of total number of runs and failures.
+  """
+  num_runs = len(list_of_results)  # By default, there is 1 run per JobResult.
+  num_failures = 0
+  for jobresult in list_of_results:
+    if jobresult.retries > 0:
+      num_runs += jobresult.retries
+    if jobresult.num_failures > 0:
+      num_failures += jobresult.num_failures
+  return num_runs, num_failures
+
 def _build_and_run(
     check_cancelled, newline_on_success, travis, cache, xml_report=None):
   """Do one pass of building & running tests."""
@@ -853,13 +870,24 @@
     root = ET.Element('testsuites') if xml_report else None
     testsuite = ET.SubElement(root, 'testsuite', id='1', package='grpc', name='tests') if xml_report else None
 
-    number_failures, _ = jobset.run(
-        all_runs, check_cancelled, newline_on_success=newline_on_success,
+    number_failures, resultset = jobset.run(
+        all_runs, check_cancelled, newline_on_success=newline_on_success, 
         travis=travis, infinite_runs=infinite_runs, maxjobs=args.jobs,
         stop_on_failure=args.stop_on_failure,
         cache=cache if not xml_report else None,
         xml_report=testsuite,
         add_env={'GRPC_TEST_PORT_SERVER': 'localhost:%d' % port_server_port})
+    if resultset:
+      for k, v in resultset.iteritems():
+        num_runs, num_failures = _calculate_num_runs_failures(v)
+        if num_failures == num_runs:  # what about infinite_runs???
+          jobset.message('FAILED', k, do_newline=True)
+        elif num_failures > 0:
+          jobset.message(
+              'FLAKE', '%s [%d/%d runs flaked]' % (k, num_failures, num_runs),
+              do_newline=True)
+        else:
+          jobset.message('PASSED', k, do_newline=True)
     if number_failures:
       return 2
   finally: