aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/objective-c
diff options
context:
space:
mode:
Diffstat (limited to 'src/objective-c')
-rw-r--r--src/objective-c/GRPCClient/GRPCCall.h107
-rw-r--r--src/objective-c/GRPCClient/GRPCCall.m21
-rw-r--r--src/objective-c/GRPCClient/private/GRPCHost.m5
-rw-r--r--src/objective-c/GRPCClient/private/GRPCRequestHeaders.h52
-rw-r--r--src/objective-c/GRPCClient/private/GRPCRequestHeaders.m119
-rw-r--r--src/objective-c/GRPCClient/private/GRPCWrappedCall.h3
-rw-r--r--src/objective-c/GRPCClient/private/GRPCWrappedCall.m2
-rw-r--r--src/objective-c/GRPCClient/private/NSDictionary+GRPC.m47
-rw-r--r--src/objective-c/GRPCClient/private/NSError+GRPC.h23
-rw-r--r--src/objective-c/ProtoRPC/ProtoRPC.m33
-rw-r--r--src/objective-c/examples/RemoteTestClient/RemoteTest.podspec28
-rw-r--r--src/objective-c/examples/RemoteTestClient/empty.proto44
-rw-r--r--src/objective-c/examples/RemoteTestClient/messages.proto133
-rw-r--r--src/objective-c/examples/RemoteTestClient/test.proto73
-rw-r--r--src/objective-c/examples/SwiftSample/AppDelegate.swift39
-rw-r--r--src/objective-c/examples/SwiftSample/Base.lproj/Main.storyboard25
-rw-r--r--src/objective-c/examples/SwiftSample/Bridging-Header.h45
-rw-r--r--src/objective-c/examples/SwiftSample/Images.xcassets/AppIcon.appiconset/Contents.json68
-rw-r--r--src/objective-c/examples/SwiftSample/Info.plist47
-rw-r--r--src/objective-c/examples/SwiftSample/Podfile9
-rw-r--r--src/objective-c/examples/SwiftSample/SwiftSample.xcodeproj/project.pbxproj354
-rw-r--r--src/objective-c/examples/SwiftSample/SwiftSample.xcodeproj/project.xcworkspace/contents.xcworkspacedata7
-rw-r--r--src/objective-c/examples/SwiftSample/ViewController.swift99
-rw-r--r--src/objective-c/generated_libraries/RouteGuideClient/route_guide.proto2
-rw-r--r--src/objective-c/tests/LocalClearTextTests.m2
25 files changed, 1305 insertions, 82 deletions
diff --git a/src/objective-c/GRPCClient/GRPCCall.h b/src/objective-c/GRPCClient/GRPCCall.h
index 4eda499b1a..35f7e16af7 100644
--- a/src/objective-c/GRPCClient/GRPCCall.h
+++ b/src/objective-c/GRPCClient/GRPCCall.h
@@ -48,11 +48,112 @@
#import <Foundation/Foundation.h>
#import <RxLibrary/GRXWriter.h>
+#pragma mark gRPC errors
+
+// Domain of NSError objects produced by gRPC.
+extern NSString *const kGRPCErrorDomain;
+
+// gRPC error codes.
+// Note that a few of these are never produced by the gRPC libraries, but are of general utility for
+// server applications to produce.
+typedef NS_ENUM(NSUInteger, GRPCErrorCode) {
+ // The operation was cancelled (typically by the caller).
+ GRPCErrorCodeCancelled = 1,
+
+ // Unknown error. Errors raised by APIs that do not return enough error information may be
+ // converted to this error.
+ GRPCErrorCodeUnknown = 2,
+
+ // The client specified an invalid argument. Note that this differs from FAILED_PRECONDITION.
+ // INVALID_ARGUMENT indicates arguments that are problematic regardless of the state of the
+ // server (e.g., a malformed file name).
+ GRPCErrorCodeInvalidArgument = 3,
+
+ // Deadline expired before operation could complete. For operations that change the state of the
+ // server, this error may be returned even if the operation has completed successfully. For
+ // example, a successful response from the server could have been delayed long enough for the
+ // deadline to expire.
+ GRPCErrorCodeDeadlineExceeded = 4,
+
+ // Some requested entity (e.g., file or directory) was not found.
+ GRPCErrorCodeNotFound = 5,
+
+ // Some entity that we attempted to create (e.g., file or directory) already exists.
+ GRPCErrorCodeAlreadyExists = 6,
+
+ // The caller does not have permission to execute the specified operation. PERMISSION_DENIED isn't
+ // used for rejections caused by exhausting some resource (RESOURCE_EXHAUSTED is used instead for
+ // those errors). PERMISSION_DENIED doesn't indicate a failure to identify the caller
+ // (UNAUTHENTICATED is used instead for those errors).
+ GRPCErrorCodePermissionDenied = 7,
+
+ // The request does not have valid authentication credentials for the operation (e.g. the caller's
+ // identity can't be verified).
+ GRPCErrorCodeUnauthenticated = 16,
+
+ // Some resource has been exhausted, perhaps a per-user quota.
+ GRPCErrorCodeResourceExhausted = 8,
+
+ // The RPC was rejected because the server is not in a state required for the procedure's
+ // execution. For example, a directory to be deleted may be non-empty, etc.
+ // The client should not retry until the server state has been explicitly fixed (e.g. by
+ // performing another RPC). The details depend on the service being called, and should be found in
+ // the NSError's userInfo.
+ GRPCErrorCodeFailedPrecondition = 9,
+
+ // The RPC was aborted, typically due to a concurrency issue like sequencer check failures,
+ // transaction aborts, etc. The client should retry at a higher-level (e.g., restarting a read-
+ // modify-write sequence).
+ GRPCErrorCodeAborted = 10,
+
+ // The RPC was attempted past the valid range. E.g., enumerating past the end of a list.
+ // Unlike INVALID_ARGUMENT, this error indicates a problem that may be fixed if the system state
+ // changes. For example, an RPC to get elements of a list will generate INVALID_ARGUMENT if asked
+ // to return the element at a negative index, but it will generate OUT_OF_RANGE if asked to return
+ // the element at an index past the current size of the list.
+ GRPCErrorCodeOutOfRange = 11,
+
+ // The procedure is not implemented or not supported/enabled in this server.
+ GRPCErrorCodeUnimplemented = 12,
+
+ // Internal error. Means some invariant expected by the server application or the gRPC library has
+ // been broken.
+ GRPCErrorCodeInternal = 13,
+
+ // The server is currently unavailable. This is most likely a transient condition and may be
+ // corrected by retrying with a backoff.
+ GRPCErrorCodeUnavailable = 14,
+
+ // Unrecoverable data loss or corruption.
+ GRPCErrorCodeDataLoss = 15,
+};
+
// Keys used in |NSError|'s |userInfo| dictionary to store the response headers and trailers sent by
// the server.
extern id const kGRPCHeadersKey;
extern id const kGRPCTrailersKey;
+#pragma mark GRPCCall
+
+// The container of the request headers of an RPC conforms to this protocol, which is a subset of
+// NSMutableDictionary's interface. It will become a NSMutableDictionary later on.
+// The keys of this container are the header names, which per the HTTP standard are case-
+// insensitive. They are stored in lowercase (which is how HTTP/2 mandates them on the wire), and
+// can only consist of ASCII characters.
+// A header value is a NSString object (with only ASCII characters), unless the header name has the
+// suffix "-bin", in which case the value has to be a NSData object.
+@protocol GRPCRequestHeaders <NSObject>
+
+@property(nonatomic, readonly) NSUInteger count;
+
+- (id)objectForKeyedSubscript:(NSString *)key;
+- (void)setObject:(id)obj forKeyedSubscript:(NSString *)key;
+
+- (void)removeAllObjects;
+- (void)removeObjectForKey:(NSString *)key;
+
+@end
+
// Represents a single gRPC remote call.
@interface GRPCCall : GRXWriter
@@ -68,10 +169,8 @@ extern id const kGRPCTrailersKey;
//
// After the call is started, trying to modify this property is an error.
//
-// For convenience, the property is initialized to an empty NSMutableDictionary, and the setter
-// accepts (and copies) both mutable and immutable dictionaries.
-- (NSMutableDictionary *)requestHeaders; // nonatomic
-- (void)setRequestHeaders:(NSDictionary *)requestHeaders; // nonatomic, copy
+// The property is initialized to an empty NSMutableDictionary.
+@property(atomic, readonly) id<GRPCRequestHeaders> requestHeaders;
// This dictionary is populated with the HTTP headers received from the server. This happens before
// any response message is received from the server. It has the same structure as the request
diff --git a/src/objective-c/GRPCClient/GRPCCall.m b/src/objective-c/GRPCClient/GRPCCall.m
index ff5d1c5aaf..b6986bf59c 100644
--- a/src/objective-c/GRPCClient/GRPCCall.m
+++ b/src/objective-c/GRPCClient/GRPCCall.m
@@ -37,6 +37,7 @@
#include <grpc/support/time.h>
#import <RxLibrary/GRXConcurrentWriteable.h>
+#import "private/GRPCRequestHeaders.h"
#import "private/GRPCWrappedCall.h"
#import "private/NSData+GRPC.h"
#import "private/NSDictionary+GRPC.h"
@@ -93,7 +94,7 @@ NSString * const kGRPCTrailersKey = @"io.grpc.TrailersKey";
// the response arrives.
GRPCCall *_retainSelf;
- NSMutableDictionary *_requestHeaders;
+ GRPCRequestHeaders *_requestHeaders;
}
@synthesize state = _state;
@@ -124,21 +125,11 @@ NSString * const kGRPCTrailersKey = @"io.grpc.TrailersKey";
_requestWriter = requestWriter;
- _requestHeaders = [NSMutableDictionary dictionary];
+ _requestHeaders = [[GRPCRequestHeaders alloc] initWithCall:self];
}
return self;
}
-#pragma mark Metadata
-
-- (NSMutableDictionary *)requestHeaders {
- return _requestHeaders;
-}
-
-- (void)setRequestHeaders:(NSDictionary *)requestHeaders {
- _requestHeaders = [NSMutableDictionary dictionaryWithDictionary:requestHeaders];
-}
-
#pragma mark Finish
- (void)finishWithError:(NSError *)errorOrNil {
@@ -230,10 +221,10 @@ NSString * const kGRPCTrailersKey = @"io.grpc.TrailersKey";
#pragma mark Send headers
-- (void)sendHeaders:(NSDictionary *)headers {
+- (void)sendHeaders:(id<GRPCRequestHeaders>)headers {
// TODO(jcanizales): Add error handlers for async failures
- [_wrappedCall startBatchWithOperations:@[[[GRPCOpSendMetadata alloc]
- initWithMetadata:headers ?: @{} handler:nil]]];
+ [_wrappedCall startBatchWithOperations:@[[[GRPCOpSendMetadata alloc] initWithMetadata:headers
+ handler:nil]]];
}
#pragma mark GRXWriteable implementation
diff --git a/src/objective-c/GRPCClient/private/GRPCHost.m b/src/objective-c/GRPCClient/private/GRPCHost.m
index a7142d0f00..a8cd3a0e74 100644
--- a/src/objective-c/GRPCClient/private/GRPCHost.m
+++ b/src/objective-c/GRPCClient/private/GRPCHost.m
@@ -57,13 +57,16 @@
// Default initializer.
- (instancetype)initWithAddress:(NSString *)address {
+ if (!address) {
+ return nil;
+ }
// To provide a default port, we try to interpret the address. If it's just a host name without
// scheme and without port, we'll use port 443. If it has a scheme, we pass it untouched to the C
// gRPC library.
// TODO(jcanizales): Add unit tests for the types of addresses we want to let pass untouched.
NSURL *hostURL = [NSURL URLWithString:[@"https://" stringByAppendingString:address]];
- if (hostURL && !hostURL.port) {
+ if (hostURL.host && !hostURL.port) {
address = [hostURL.host stringByAppendingString:@":443"];
}
diff --git a/src/objective-c/GRPCClient/private/GRPCRequestHeaders.h b/src/objective-c/GRPCClient/private/GRPCRequestHeaders.h
new file mode 100644
index 0000000000..1391b5725f
--- /dev/null
+++ b/src/objective-c/GRPCClient/private/GRPCRequestHeaders.h
@@ -0,0 +1,52 @@
+/*
+ *
+ * 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 <Foundation/Foundation.h>
+#include <grpc/grpc.h>
+
+#import "GRPCCall.h"
+
+@interface GRPCRequestHeaders : NSObject<GRPCRequestHeaders>
+
+@property(nonatomic, readonly) NSUInteger count;
+@property(nonatomic, readonly) grpc_metadata *grpc_metadataArray;
+
+- (instancetype)initWithCall:(GRPCCall *)call;
+
+- (id)objectForKeyedSubscript:(NSString *)key;
+- (void)setObject:(id)obj forKeyedSubscript:(NSString *)key;
+
+- (void)removeAllObjects;
+- (void)removeObjectForKey:(NSString *)key;
+
+@end
diff --git a/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m b/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m
new file mode 100644
index 0000000000..761677ce50
--- /dev/null
+++ b/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m
@@ -0,0 +1,119 @@
+/*
+ *
+ * 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 "GRPCRequestHeaders.h"
+
+#import <Foundation/Foundation.h>
+
+#import "../GRPCCall.h"
+#import "NSDictionary+GRPC.h"
+
+// Used by the setter.
+static void CheckIsNonNilASCII(NSString *name, NSString* value) {
+ if (!value) {
+ [NSException raise:NSInvalidArgumentException format:@"%@ cannot be nil", name];
+ }
+ if (![value canBeConvertedToEncoding:NSASCIIStringEncoding]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"%@ %@ contains non-ASCII characters", name, value];
+ }
+}
+
+// Precondition: key isn't nil.
+static void CheckKeyValuePairIsValid(NSString *key, id value) {
+ if ([key hasSuffix:@"-bin"]) {
+ if (![value isKindOfClass:NSData.class]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Expected NSData value for header %@ ending in \"-bin\", "
+ @"instead got %@", key, value];
+ }
+ } else {
+ if (![value isKindOfClass:NSString.class]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Expected NSString value for header %@ not ending in \"-bin\", "
+ @"instead got %@", key, value];
+ }
+ CheckIsNonNilASCII(@"Text header value", (NSString *)value);
+ }
+}
+
+@implementation GRPCRequestHeaders {
+ __weak GRPCCall *_call;
+ NSMutableDictionary *_delegate;
+}
+
+- (instancetype)initWithCall:(GRPCCall *)call {
+ if ((self = [super init])) {
+ _call = call;
+ _delegate = [NSMutableDictionary dictionary];
+ }
+ return self;
+}
+
+- (void)checkCallIsNotStarted {
+ if (_call.state != GRXWriterStateNotStarted) {
+ [NSException raise:@"Invalid modification"
+ format:@"Cannot modify request headers after call is started"];
+ }
+}
+
+- (id)objectForKeyedSubscript:(NSString *)key {
+ return _delegate[key.lowercaseString];
+}
+
+- (void)setObject:(id)obj forKeyedSubscript:(NSString *)key {
+ [self checkCallIsNotStarted];
+ CheckIsNonNilASCII(@"Header name", key);
+ key = key.lowercaseString;
+ CheckKeyValuePairIsValid(key, obj);
+ _delegate[key] = obj;
+}
+
+- (void)removeObjectForKey:(NSString *)key {
+ [self checkCallIsNotStarted];
+ [_delegate removeObjectForKey:key.lowercaseString];
+}
+
+- (void)removeAllObjects {
+ [self checkCallIsNotStarted];
+ [_delegate removeAllObjects];
+}
+
+- (NSUInteger)count {
+ return _delegate.count;
+}
+
+- (grpc_metadata *)grpc_metadataArray {
+ return _delegate.grpc_metadataArray;
+}
+@end
diff --git a/src/objective-c/GRPCClient/private/GRPCWrappedCall.h b/src/objective-c/GRPCClient/private/GRPCWrappedCall.h
index da11cbb761..4ca2766147 100644
--- a/src/objective-c/GRPCClient/private/GRPCWrappedCall.h
+++ b/src/objective-c/GRPCClient/private/GRPCWrappedCall.h
@@ -35,6 +35,7 @@
#include <grpc/grpc.h>
#import "GRPCChannel.h"
+#import "GRPCRequestHeaders.h"
@interface GRPCOperation : NSObject
@property(nonatomic, readonly) grpc_op op;
@@ -44,7 +45,7 @@
@interface GRPCOpSendMetadata : GRPCOperation
-- (instancetype)initWithMetadata:(NSDictionary *)metadata
+- (instancetype)initWithMetadata:(GRPCRequestHeaders *)metadata
handler:(void(^)())handler NS_DESIGNATED_INITIALIZER;
@end
diff --git a/src/objective-c/GRPCClient/private/GRPCWrappedCall.m b/src/objective-c/GRPCClient/private/GRPCWrappedCall.m
index fe3d51da53..cea7c479e0 100644
--- a/src/objective-c/GRPCClient/private/GRPCWrappedCall.m
+++ b/src/objective-c/GRPCClient/private/GRPCWrappedCall.m
@@ -65,7 +65,7 @@
return [self initWithMetadata:nil handler:nil];
}
-- (instancetype)initWithMetadata:(NSDictionary *)metadata handler:(void (^)())handler {
+- (instancetype)initWithMetadata:(GRPCRequestHeaders *)metadata handler:(void (^)())handler {
if (self = [super init]) {
_op.op = GRPC_OP_SEND_INITIAL_METADATA;
_op.data.send_initial_metadata.count = metadata.count;
diff --git a/src/objective-c/GRPCClient/private/NSDictionary+GRPC.m b/src/objective-c/GRPCClient/private/NSDictionary+GRPC.m
index 99c890e4ee..7477da7619 100644
--- a/src/objective-c/GRPCClient/private/NSDictionary+GRPC.m
+++ b/src/objective-c/GRPCClient/private/NSDictionary+GRPC.m
@@ -40,8 +40,8 @@
@interface NSData (GRPCMetadata)
+ (instancetype)grpc_dataFromMetadataValue:(grpc_metadata *)metadata;
-// Fill a metadata object with the binary value in this NSData and the given key.
-- (void)grpc_initMetadata:(grpc_metadata *)metadata withKey:(NSString *)key;
+// Fill a metadata object with the binary value in this NSData.
+- (void)grpc_initMetadata:(grpc_metadata *)metadata;
@end
@implementation NSData (GRPCMetadata)
@@ -50,9 +50,7 @@
return [self dataWithBytes:metadata->value length:metadata->value_length];
}
-- (void)grpc_initMetadata:(grpc_metadata *)metadata withKey:(NSString *)key {
- // TODO(jcanizales): Encode Unicode chars as ASCII.
- metadata->key = [key stringByAppendingString:@"-bin"].UTF8String;
+- (void)grpc_initMetadata:(grpc_metadata *)metadata {
metadata->value = self.bytes;
metadata->value_length = self.length;
}
@@ -63,8 +61,8 @@
@interface NSString (GRPCMetadata)
+ (instancetype)grpc_stringFromMetadataValue:(grpc_metadata *)metadata;
-// Fill a metadata object with the textual value in this NSString and the given key.
-- (void)grpc_initMetadata:(grpc_metadata *)metadata withKey:(NSString *)key;
+// Fill a metadata object with the textual value in this NSString.
+- (void)grpc_initMetadata:(grpc_metadata *)metadata;
@end
@implementation NSString (GRPCMetadata)
@@ -74,22 +72,8 @@
encoding:NSASCIIStringEncoding];
}
-- (void)grpc_initMetadata:(grpc_metadata *)metadata withKey:(NSString *)key {
- if ([key hasSuffix:@"-bin"]) {
- // Disallow this, as at best it will confuse the server. If the app really needs to send a
- // textual header with a name ending in "-bin", it can be done by removing the suffix and
- // encoding the NSString as a NSData object.
- //
- // Why raise an exception: In the most common case, the developer knows this won't happen in
- // their code, so the exception isn't triggered. In the rare cases when the developer can't
- // tell, it's easy enough to add a sanitizing filter before the header is set. There, the
- // developer can choose whether to drop such a header, or trim its name. Doing either ourselves,
- // silently, would be very unintuitive for the user.
- [NSException raise:NSInvalidArgumentException
- format:@"Metadata keys ending in '-bin' are reserved for NSData values."];
- }
- // TODO(jcanizales): Encode Unicode chars as ASCII.
- metadata->key = key.UTF8String;
+// Precondition: This object contains only ASCII characters.
+- (void)grpc_initMetadata:(grpc_metadata *)metadata {
metadata->value = self.UTF8String;
metadata->value_length = self.length;
}
@@ -105,8 +89,6 @@
+ (instancetype)grpc_dictionaryFromMetadata:(grpc_metadata *)entries count:(size_t)count {
NSMutableDictionary *metadata = [NSMutableDictionary dictionaryWithCapacity:count];
for (grpc_metadata *entry = entries; entry < entries + count; entry++) {
- // TODO(jcanizales): Verify in a C library test that it's converting header names to lower case
- // automatically.
NSString *name = [NSString stringWithCString:entry->key encoding:NSASCIIStringEncoding];
if (!name || metadata[name]) {
// Log if name is nil?
@@ -114,7 +96,6 @@
}
id value;
if ([name hasSuffix:@"-bin"]) {
- name = [name substringToIndex:name.length - 4];
value = [NSData grpc_dataFromMetadataValue:entry];
} else {
value = [NSString grpc_stringFromMetadataValue:entry];
@@ -124,19 +105,21 @@
return metadata;
}
+// Preconditions: All keys are ASCII strings. Keys ending in -bin have NSData values; the others
+// have NSString values.
- (grpc_metadata *)grpc_metadataArray {
grpc_metadata *metadata = gpr_malloc([self count] * sizeof(grpc_metadata));
- int i = 0;
- for (id key in self) {
+ grpc_metadata *current = metadata;
+ for (NSString* key in self) {
id value = self[key];
- grpc_metadata *current = &metadata[i];
- if ([value respondsToSelector:@selector(grpc_initMetadata:withKey:)]) {
- [value grpc_initMetadata:current withKey:key];
+ current->key = key.UTF8String;
+ if ([value respondsToSelector:@selector(grpc_initMetadata:)]) {
+ [value grpc_initMetadata:current];
} else {
[NSException raise:NSInvalidArgumentException
format:@"Metadata values must be NSString or NSData."];
}
- i += 1;
+ ++current;
}
return metadata;
}
diff --git a/src/objective-c/GRPCClient/private/NSError+GRPC.h b/src/objective-c/GRPCClient/private/NSError+GRPC.h
index e712791271..f4729dc8a1 100644
--- a/src/objective-c/GRPCClient/private/NSError+GRPC.h
+++ b/src/objective-c/GRPCClient/private/NSError+GRPC.h
@@ -34,29 +34,6 @@
#import <Foundation/Foundation.h>
#include <grpc/grpc.h>
-// TODO(jcanizales): Make the domain string public.
-extern NSString *const kGRPCErrorDomain;
-
-// TODO(jcanizales): Make this public and document each code.
-typedef NS_ENUM(NSInteger, GRPCErrorCode) {
- GRPCErrorCodeCancelled = 1,
- GRPCErrorCodeUnknown = 2,
- GRPCErrorCodeInvalidArgument = 3,
- GRPCErrorCodeDeadlineExceeded = 4,
- GRPCErrorCodeNotFound = 5,
- GRPCErrorCodeAlreadyExists = 6,
- GRPCErrorCodePermissionDenied = 7,
- GRPCErrorCodeUnauthenticated = 16,
- GRPCErrorCodeResourceExhausted = 8,
- GRPCErrorCodeFailedPrecondition = 9,
- GRPCErrorCodeAborted = 10,
- GRPCErrorCodeOutOfRange = 11,
- GRPCErrorCodeUnimplemented = 12,
- GRPCErrorCodeInternal = 13,
- GRPCErrorCodeUnavailable = 14,
- GRPCErrorCodeDataLoss = 15
-};
-
@interface NSError (GRPC)
// Returns nil if the status code is OK. Otherwise, a NSError whose code is one of |GRPCErrorCode|
// and whose domain is |kGRPCErrorDomain|.
diff --git a/src/objective-c/ProtoRPC/ProtoRPC.m b/src/objective-c/ProtoRPC/ProtoRPC.m
index 889d71a308..9bf66f347a 100644
--- a/src/objective-c/ProtoRPC/ProtoRPC.m
+++ b/src/objective-c/ProtoRPC/ProtoRPC.m
@@ -37,6 +37,22 @@
#import <RxLibrary/GRXWriteable.h>
#import <RxLibrary/GRXWriter+Transformations.h>
+static NSError *ErrorForBadProto(id proto, Class expectedClass, NSError *parsingError) {
+ NSDictionary *info = @{
+ NSLocalizedDescriptionKey: @"Unable to parse response from the server",
+ NSLocalizedRecoverySuggestionErrorKey: @"If this RPC is idempotent, retry "
+ @"with exponential backoff. Otherwise, query the server status before "
+ @"retrying.",
+ NSUnderlyingErrorKey: parsingError,
+ @"Expected class": expectedClass,
+ @"Received value": proto,
+ };
+ // TODO(jcanizales): Use kGRPCErrorDomain and GRPCErrorCodeInternal when they're public.
+ return [NSError errorWithDomain:@"io.grpc"
+ code:13
+ userInfo:info];
+}
+
@implementation ProtoRPC {
id<GRXWriteable> _responseWriteable;
}
@@ -65,14 +81,25 @@
}
// A writer that serializes the proto messages to send.
GRXWriter *bytesWriter = [requestsWriter map:^id(GPBMessage *proto) {
- // TODO(jcanizales): Fail with an understandable error message if the requestsWriter isn't
- // sending GPBMessages.
+ if (![proto isKindOfClass:GPBMessage.class]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Request must be a proto message: %@", proto];
+ }
return [proto data];
}];
if ((self = [super initWithHost:host path:method.HTTPPath requestsWriter:bytesWriter])) {
+ __weak ProtoRPC *weakSelf = self;
+
// A writeable that parses the proto messages received.
_responseWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
- [responsesWriteable writeValue:[responseClass parseFromData:value error:NULL]];
+ // TODO(jcanizales): This is done in the main thread, and needs to happen in another thread.
+ NSError *error = nil;
+ id parsed = [responseClass parseFromData:value error:&error];
+ if (parsed) {
+ [responsesWriteable writeValue:parsed];
+ } else {
+ [weakSelf finishWithError:ErrorForBadProto(value, responseClass, error)];
+ }
} completionHandler:^(NSError *errorOrNil) {
[responsesWriteable writesFinishedWithError:errorOrNil];
}];
diff --git a/src/objective-c/examples/RemoteTestClient/RemoteTest.podspec b/src/objective-c/examples/RemoteTestClient/RemoteTest.podspec
new file mode 100644
index 0000000000..d4f8084cb5
--- /dev/null
+++ b/src/objective-c/examples/RemoteTestClient/RemoteTest.podspec
@@ -0,0 +1,28 @@
+Pod::Spec.new do |s|
+ s.name = "RemoteTest"
+ s.version = "0.0.1"
+ s.license = "New BSD"
+
+ s.ios.deployment_target = "6.0"
+ s.osx.deployment_target = "10.8"
+
+ # Run protoc with the Objective-C and gRPC plugins to generate protocol messages and gRPC clients.
+ s.prepare_command = <<-CMD
+ protoc --objc_out=. --objcgrpc_out=. *.proto
+ CMD
+
+ s.subspec "Messages" do |ms|
+ ms.source_files = "*.pbobjc.{h,m}"
+ ms.header_mappings_dir = "."
+ ms.requires_arc = false
+ ms.dependency "Protobuf", "~> 3.0.0-alpha-4"
+ end
+
+ s.subspec "Services" do |ss|
+ ss.source_files = "*.pbrpc.{h,m}"
+ ss.header_mappings_dir = "."
+ ss.requires_arc = true
+ ss.dependency "gRPC", "~> 0.7"
+ ss.dependency "#{s.name}/Messages"
+ end
+end
diff --git a/src/objective-c/examples/RemoteTestClient/empty.proto b/src/objective-c/examples/RemoteTestClient/empty.proto
new file mode 100644
index 0000000000..a678048289
--- /dev/null
+++ b/src/objective-c/examples/RemoteTestClient/empty.proto
@@ -0,0 +1,44 @@
+// 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.
+
+syntax = "proto3";
+
+package grpc.testing;
+
+option objc_class_prefix = "RMT";
+
+// An empty message that you can re-use to avoid defining duplicated empty
+// messages in your project. A typical example is to use it as argument or the
+// return value of a service API. For instance:
+//
+// service Foo {
+// rpc Bar (grpc.testing.Empty) returns (grpc.testing.Empty) { };
+// };
+//
+message Empty {}
diff --git a/src/objective-c/examples/RemoteTestClient/messages.proto b/src/objective-c/examples/RemoteTestClient/messages.proto
new file mode 100644
index 0000000000..85d93c2ff9
--- /dev/null
+++ b/src/objective-c/examples/RemoteTestClient/messages.proto
@@ -0,0 +1,133 @@
+// 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.
+
+// Message definitions to be used by integration test service definitions.
+
+syntax = "proto3";
+
+package grpc.testing;
+
+option objc_class_prefix = "RMT";
+
+// The type of payload that should be returned.
+enum PayloadType {
+ // Compressable text format.
+ COMPRESSABLE = 0;
+
+ // Uncompressable binary format.
+ UNCOMPRESSABLE = 1;
+
+ // Randomly chosen from all other formats defined in this enum.
+ RANDOM = 2;
+}
+
+// A block of data, to simply increase gRPC message size.
+message Payload {
+ // The type of data in body.
+ PayloadType type = 1;
+ // Primary contents of payload.
+ bytes body = 2;
+}
+
+// Unary request.
+message SimpleRequest {
+ // Desired payload type in the response from the server.
+ // If response_type is RANDOM, server randomly chooses one from other formats.
+ PayloadType response_type = 1;
+
+ // Desired payload size in the response from the server.
+ // If response_type is COMPRESSABLE, this denotes the size before compression.
+ int32 response_size = 2;
+
+ // Optional input payload sent along with the request.
+ Payload payload = 3;
+
+ // Whether SimpleResponse should include username.
+ bool fill_username = 4;
+
+ // Whether SimpleResponse should include OAuth scope.
+ bool fill_oauth_scope = 5;
+}
+
+// Unary response, as configured by the request.
+message SimpleResponse {
+ // Payload to increase message size.
+ Payload payload = 1;
+ // The user the request came from, for verifying authentication was
+ // successful when the client expected it.
+ string username = 2;
+ // OAuth scope.
+ string oauth_scope = 3;
+}
+
+// Client-streaming request.
+message StreamingInputCallRequest {
+ // Optional input payload sent along with the request.
+ Payload payload = 1;
+
+ // Not expecting any payload from the response.
+}
+
+// Client-streaming response.
+message StreamingInputCallResponse {
+ // Aggregated size of payloads received from the client.
+ int32 aggregated_payload_size = 1;
+}
+
+// Configuration for a particular response.
+message ResponseParameters {
+ // Desired payload sizes in responses from the server.
+ // If response_type is COMPRESSABLE, this denotes the size before compression.
+ int32 size = 1;
+
+ // Desired interval between consecutive responses in the response stream in
+ // microseconds.
+ int32 interval_us = 2;
+}
+
+// Server-streaming request.
+message StreamingOutputCallRequest {
+ // Desired payload type in the response from the server.
+ // If response_type is RANDOM, the payload from each response in the stream
+ // might be of different types. This is to simulate a mixed type of payload
+ // stream.
+ PayloadType response_type = 1;
+
+ // Configuration for each expected response message.
+ repeated ResponseParameters response_parameters = 2;
+
+ // Optional input payload sent along with the request.
+ Payload payload = 3;
+}
+
+// Server-streaming response, as configured by the request and parameters.
+message StreamingOutputCallResponse {
+ // Payload to increase response size.
+ Payload payload = 1;
+}
diff --git a/src/objective-c/examples/RemoteTestClient/test.proto b/src/objective-c/examples/RemoteTestClient/test.proto
new file mode 100644
index 0000000000..2f5a5489b3
--- /dev/null
+++ b/src/objective-c/examples/RemoteTestClient/test.proto
@@ -0,0 +1,73 @@
+// 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.
+
+// An integration test service that covers all the method signature permutations
+// of unary/streaming requests/responses.
+syntax = "proto3";
+
+import "empty.proto";
+import "messages.proto";
+
+package grpc.testing;
+
+option objc_class_prefix = "RMT";
+
+// A simple service to test the various types of RPCs and experiment with
+// performance with various types of payload.
+service TestService {
+ // One empty request followed by one empty response.
+ rpc EmptyCall(grpc.testing.Empty) returns (grpc.testing.Empty);
+
+ // One request followed by one response.
+ // TODO(Issue 527): Describe required server behavior.
+ rpc UnaryCall(SimpleRequest) returns (SimpleResponse);
+
+ // One request followed by a sequence of responses (streamed download).
+ // The server returns the payload with client desired type and sizes.
+ rpc StreamingOutputCall(StreamingOutputCallRequest)
+ returns (stream StreamingOutputCallResponse);
+
+ // A sequence of requests followed by one response (streamed upload).
+ // The server returns the aggregated size of client payload as the result.
+ rpc StreamingInputCall(stream StreamingInputCallRequest)
+ returns (StreamingInputCallResponse);
+
+ // A sequence of requests with each request served by the server immediately.
+ // As one request could lead to multiple responses, this interface
+ // demonstrates the idea of full duplexing.
+ rpc FullDuplexCall(stream StreamingOutputCallRequest)
+ returns (stream StreamingOutputCallResponse);
+
+ // A sequence of requests followed by a sequence of responses.
+ // The server buffers all the client requests and then serves them in order. A
+ // stream of responses are returned to the client when the server starts with
+ // first request.
+ rpc HalfDuplexCall(stream StreamingOutputCallRequest)
+ returns (stream StreamingOutputCallResponse);
+}
diff --git a/src/objective-c/examples/SwiftSample/AppDelegate.swift b/src/objective-c/examples/SwiftSample/AppDelegate.swift
new file mode 100644
index 0000000000..88027d997b
--- /dev/null
+++ b/src/objective-c/examples/SwiftSample/AppDelegate.swift
@@ -0,0 +1,39 @@
+/*
+ *
+ * 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
+
+@UIApplicationMain
+class AppDelegate: UIResponder, UIApplicationDelegate {
+ var window: UIWindow?
+}
diff --git a/src/objective-c/examples/SwiftSample/Base.lproj/Main.storyboard b/src/objective-c/examples/SwiftSample/Base.lproj/Main.storyboard
new file mode 100644
index 0000000000..3a2a49bad8
--- /dev/null
+++ b/src/objective-c/examples/SwiftSample/Base.lproj/Main.storyboard
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6211" systemVersion="14A298i" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
+ <dependencies>
+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6204"/>
+ </dependencies>
+ <scenes>
+ <!--View Controller-->
+ <scene sceneID="tne-QT-ifu">
+ <objects>
+ <viewController id="BYZ-38-t0r" customClass="ViewController" customModuleProvider="target" sceneMemberID="viewController">
+ <layoutGuides>
+ <viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
+ <viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
+ </layoutGuides>
+ <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
+ <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
+ </view>
+ </viewController>
+ <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
+ </objects>
+ </scene>
+ </scenes>
+</document>
diff --git a/src/objective-c/examples/SwiftSample/Bridging-Header.h b/src/objective-c/examples/SwiftSample/Bridging-Header.h
new file mode 100644
index 0000000000..65f768a760
--- /dev/null
+++ b/src/objective-c/examples/SwiftSample/Bridging-Header.h
@@ -0,0 +1,45 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#ifndef SwiftSample_Bridging_Header_h
+#define SwiftSample_Bridging_Header_h
+
+#import <RxLibrary/GRXWriteable.h>
+#import <RxLibrary/GRXWriter.h>
+#import <RxLibrary/GRXWriter+Immediate.h>
+#import <GRPCClient/GRPCCall.h>
+#import <ProtoRPC/ProtoMethod.h>
+#import <ProtoRPC/ProtoRPC.h>
+#import <RemoteTest/Test.pbrpc.h>
+
+#endif
diff --git a/src/objective-c/examples/SwiftSample/Images.xcassets/AppIcon.appiconset/Contents.json b/src/objective-c/examples/SwiftSample/Images.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000000..36d2c80d88
--- /dev/null
+++ b/src/objective-c/examples/SwiftSample/Images.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,68 @@
+{
+ "images" : [
+ {
+ "idiom" : "iphone",
+ "size" : "29x29",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "29x29",
+ "scale" : "3x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "40x40",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "40x40",
+ "scale" : "3x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "60x60",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "60x60",
+ "scale" : "3x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "29x29",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "29x29",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "40x40",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "40x40",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "76x76",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "76x76",
+ "scale" : "2x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+} \ No newline at end of file
diff --git a/src/objective-c/examples/SwiftSample/Info.plist b/src/objective-c/examples/SwiftSample/Info.plist
new file mode 100644
index 0000000000..10f0450b34
--- /dev/null
+++ b/src/objective-c/examples/SwiftSample/Info.plist
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>en</string>
+ <key>CFBundleExecutable</key>
+ <string>$(EXECUTABLE_NAME)</string>
+ <key>CFBundleIdentifier</key>
+ <string>io.grpc.$(PRODUCT_NAME:rfc1034identifier)</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>$(PRODUCT_NAME)</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1</string>
+ <key>LSRequiresIPhoneOS</key>
+ <true/>
+ <key>UILaunchStoryboardName</key>
+ <string>Main</string>
+ <key>UIMainStoryboardFile</key>
+ <string>Main</string>
+ <key>UIRequiredDeviceCapabilities</key>
+ <array>
+ <string>armv7</string>
+ </array>
+ <key>UISupportedInterfaceOrientations</key>
+ <array>
+ <string>UIInterfaceOrientationPortrait</string>
+ <string>UIInterfaceOrientationLandscapeLeft</string>
+ <string>UIInterfaceOrientationLandscapeRight</string>
+ </array>
+ <key>UISupportedInterfaceOrientations~ipad</key>
+ <array>
+ <string>UIInterfaceOrientationPortrait</string>
+ <string>UIInterfaceOrientationPortraitUpsideDown</string>
+ <string>UIInterfaceOrientationLandscapeLeft</string>
+ <string>UIInterfaceOrientationLandscapeRight</string>
+ </array>
+</dict>
+</plist>
diff --git a/src/objective-c/examples/SwiftSample/Podfile b/src/objective-c/examples/SwiftSample/Podfile
new file mode 100644
index 0000000000..3611b00863
--- /dev/null
+++ b/src/objective-c/examples/SwiftSample/Podfile
@@ -0,0 +1,9 @@
+source 'https://github.com/CocoaPods/Specs.git'
+platform :ios, '8.0'
+
+pod 'Protobuf', :path => "../../../../third_party/protobuf"
+pod 'gRPC', :path => "../../../.."
+pod 'RemoteTest', :path => "../RemoteTestClient"
+
+target 'SwiftSample' do
+end
diff --git a/src/objective-c/examples/SwiftSample/SwiftSample.xcodeproj/project.pbxproj b/src/objective-c/examples/SwiftSample/SwiftSample.xcodeproj/project.pbxproj
new file mode 100644
index 0000000000..cfccdd453f
--- /dev/null
+++ b/src/objective-c/examples/SwiftSample/SwiftSample.xcodeproj/project.pbxproj
@@ -0,0 +1,354 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 46;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 253D3A297105CA46DA960A11 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DC58ACA18DCCB1553531B885 /* libPods.a */; };
+ 633BFFC81B950B210007E424 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 633BFFC71B950B210007E424 /* AppDelegate.swift */; };
+ 633BFFCA1B950B210007E424 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 633BFFC91B950B210007E424 /* ViewController.swift */; };
+ 633BFFCD1B950B210007E424 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 633BFFCB1B950B210007E424 /* Main.storyboard */; };
+ 633BFFCF1B950B210007E424 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 633BFFCE1B950B210007E424 /* Images.xcassets */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+ 12C7B447AA80E624D93B5C54 /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = "<group>"; };
+ 633BFFC21B950B210007E424 /* SwiftSample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftSample.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 633BFFC61B950B210007E424 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+ 633BFFC71B950B210007E424 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
+ 633BFFC91B950B210007E424 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
+ 633BFFCC1B950B210007E424 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
+ 633BFFCE1B950B210007E424 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
+ 6367AD231B951655007FD3A4 /* Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Bridging-Header.h"; sourceTree = "<group>"; };
+ C335CBC4C160E0D9EDEE646B /* 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>"; };
+ DC58ACA18DCCB1553531B885 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 633BFFBF1B950B210007E424 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 253D3A297105CA46DA960A11 /* libPods.a in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 31F283C976AE97586C17CCD9 /* Pods */ = {
+ isa = PBXGroup;
+ children = (
+ 12C7B447AA80E624D93B5C54 /* Pods.debug.xcconfig */,
+ C335CBC4C160E0D9EDEE646B /* Pods.release.xcconfig */,
+ );
+ name = Pods;
+ sourceTree = "<group>";
+ };
+ 633BFFB91B950B210007E424 = {
+ isa = PBXGroup;
+ children = (
+ 633BFFC41B950B210007E424 /* SwiftSample */,
+ 633BFFC31B950B210007E424 /* Products */,
+ 31F283C976AE97586C17CCD9 /* Pods */,
+ 9D63A7F6423989BA306810CA /* Frameworks */,
+ );
+ sourceTree = "<group>";
+ };
+ 633BFFC31B950B210007E424 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 633BFFC21B950B210007E424 /* SwiftSample.app */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ 633BFFC41B950B210007E424 /* SwiftSample */ = {
+ isa = PBXGroup;
+ children = (
+ 633BFFC71B950B210007E424 /* AppDelegate.swift */,
+ 633BFFC91B950B210007E424 /* ViewController.swift */,
+ 633BFFCB1B950B210007E424 /* Main.storyboard */,
+ 633BFFCE1B950B210007E424 /* Images.xcassets */,
+ 633BFFC51B950B210007E424 /* Supporting Files */,
+ 6367AD231B951655007FD3A4 /* Bridging-Header.h */,
+ );
+ name = SwiftSample;
+ sourceTree = SOURCE_ROOT;
+ };
+ 633BFFC51B950B210007E424 /* Supporting Files */ = {
+ isa = PBXGroup;
+ children = (
+ 633BFFC61B950B210007E424 /* Info.plist */,
+ );
+ name = "Supporting Files";
+ sourceTree = "<group>";
+ };
+ 9D63A7F6423989BA306810CA /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ DC58ACA18DCCB1553531B885 /* libPods.a */,
+ );
+ name = Frameworks;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 633BFFC11B950B210007E424 /* SwiftSample */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 633BFFE11B950B210007E424 /* Build configuration list for PBXNativeTarget "SwiftSample" */;
+ buildPhases = (
+ 6BEEB33CA2705D7D2F2210E6 /* Check Pods Manifest.lock */,
+ 633BFFBE1B950B210007E424 /* Sources */,
+ 633BFFBF1B950B210007E424 /* Frameworks */,
+ 633BFFC01B950B210007E424 /* Resources */,
+ AC2F6F9AB1C090BB0BEE6E4D /* Copy Pods Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = SwiftSample;
+ productName = SwiftSample;
+ productReference = 633BFFC21B950B210007E424 /* SwiftSample.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 633BFFBA1B950B210007E424 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 0640;
+ ORGANIZATIONNAME = gRPC;
+ TargetAttributes = {
+ 633BFFC11B950B210007E424 = {
+ CreatedOnToolsVersion = 6.4;
+ };
+ };
+ };
+ buildConfigurationList = 633BFFBD1B950B210007E424 /* Build configuration list for PBXProject "SwiftSample" */;
+ compatibilityVersion = "Xcode 3.2";
+ developmentRegion = English;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 633BFFB91B950B210007E424;
+ productRefGroup = 633BFFC31B950B210007E424 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 633BFFC11B950B210007E424 /* SwiftSample */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 633BFFC01B950B210007E424 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 633BFFCD1B950B210007E424 /* Main.storyboard in Resources */,
+ 633BFFCF1B950B210007E424 /* Images.xcassets in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 6BEEB33CA2705D7D2F2210E6 /* 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;
+ };
+ AC2F6F9AB1C090BB0BEE6E4D /* 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 */
+ 633BFFBE1B950B210007E424 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 633BFFCA1B950B210007E424 /* ViewController.swift in Sources */,
+ 633BFFC81B950B210007E424 /* AppDelegate.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+ 633BFFCB1B950B210007E424 /* Main.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 633BFFCC1B950B210007E424 /* Base */,
+ );
+ name = Main.storyboard;
+ sourceTree = "<group>";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 633BFFDF1B950B210007E424 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_SYMBOLS_PRIVATE_EXTERN = NO;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 8.4;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = iphoneos;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 633BFFE01B950B210007E424 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 8.4;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ 633BFFE21B950B210007E424 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 12C7B447AA80E624D93B5C54 /* Pods.debug.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ INFOPLIST_FILE = Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OBJC_BRIDGING_HEADER = "Bridging-Header.h";
+ USER_HEADER_SEARCH_PATHS = "Pods/**";
+ };
+ name = Debug;
+ };
+ 633BFFE31B950B210007E424 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = C335CBC4C160E0D9EDEE646B /* Pods.release.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ INFOPLIST_FILE = Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OBJC_BRIDGING_HEADER = "Bridging-Header.h";
+ USER_HEADER_SEARCH_PATHS = "Pods/**";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 633BFFBD1B950B210007E424 /* Build configuration list for PBXProject "SwiftSample" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 633BFFDF1B950B210007E424 /* Debug */,
+ 633BFFE01B950B210007E424 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 633BFFE11B950B210007E424 /* Build configuration list for PBXNativeTarget "SwiftSample" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 633BFFE21B950B210007E424 /* Debug */,
+ 633BFFE31B950B210007E424 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 633BFFBA1B950B210007E424 /* Project object */;
+}
diff --git a/src/objective-c/examples/SwiftSample/SwiftSample.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/src/objective-c/examples/SwiftSample/SwiftSample.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000000..3b0f1c15b2
--- /dev/null
+++ b/src/objective-c/examples/SwiftSample/SwiftSample.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+ version = "1.0">
+ <FileRef
+ location = "self:SwiftSample.xcodeproj">
+ </FileRef>
+</Workspace>
diff --git a/src/objective-c/examples/SwiftSample/ViewController.swift b/src/objective-c/examples/SwiftSample/ViewController.swift
new file mode 100644
index 0000000000..76dad9e132
--- /dev/null
+++ b/src/objective-c/examples/SwiftSample/ViewController.swift
@@ -0,0 +1,99 @@
+/*
+ *
+ * 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
+
+class ViewController: UIViewController {
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+
+ let RemoteHost = "grpc-test.sandbox.google.com"
+
+ let request = RMTSimpleRequest()
+ request.responseSize = 10
+ request.fillUsername = true
+ request.fillOauthScope = true
+
+
+ // Example gRPC call using a generated proto client library:
+
+ let service = RMTTestService(host: RemoteHost)
+ service.unaryCallWithRequest(request) { response, error in
+ if let response = response {
+ NSLog("1. Finished successfully with response:\n\(response)")
+ } else {
+ NSLog("1. Finished with error: \(error!)")
+ }
+ }
+
+
+ // Same but manipulating headers:
+
+ var RPC : ProtoRPC! // Needed to convince Swift to capture by reference (__block)
+ RPC = service.RPCToUnaryCallWithRequest(request) { response, error in
+ if let response = response {
+ NSLog("2. Finished successfully with response:\n\(response)")
+ } else {
+ NSLog("2. Finished with error: \(error!)")
+ }
+ NSLog("2. Response headers: \(RPC.responseHeaders)")
+ NSLog("2. Response trailers: \(RPC.responseTrailers)")
+ }
+
+ RPC.requestHeaders["My-Header"] = "My value"
+
+ RPC.start()
+
+
+ // Same example call using the generic gRPC client library:
+
+ let method = ProtoMethod(package: "grpc.testing", service: "TestService", method: "UnaryCall")
+
+ let requestsWriter = GRXWriter(value: request.data())
+
+ let call = GRPCCall(host: RemoteHost, path: method.HTTPPath, requestsWriter: requestsWriter)
+
+ call.requestHeaders["My-Header"] = "My value"
+
+ call.startWithWriteable(GRXWriteable { response, error in
+ if let response = response as? NSData {
+ NSLog("3. Received response:\n\(RMTSimpleResponse(data: response, error: nil))")
+ } else {
+ NSLog("3. Finished with error: \(error!)")
+ }
+ NSLog("3. Response headers: \(call.responseHeaders)")
+ NSLog("3. Response trailers: \(call.responseTrailers)")
+ })
+ }
+}
diff --git a/src/objective-c/generated_libraries/RouteGuideClient/route_guide.proto b/src/objective-c/generated_libraries/RouteGuideClient/route_guide.proto
index dace1a5d26..19592e2ebd 100644
--- a/src/objective-c/generated_libraries/RouteGuideClient/route_guide.proto
+++ b/src/objective-c/generated_libraries/RouteGuideClient/route_guide.proto
@@ -29,7 +29,7 @@
syntax = "proto3";
-package examples;
+package routeguide;
option objc_class_prefix = "RGD";
diff --git a/src/objective-c/tests/LocalClearTextTests.m b/src/objective-c/tests/LocalClearTextTests.m
index d01fe91afa..976fff55bc 100644
--- a/src/objective-c/tests/LocalClearTextTests.m
+++ b/src/objective-c/tests/LocalClearTextTests.m
@@ -47,7 +47,7 @@
// instructions at https://github.com/grpc/homebrew-grpc
static NSString * const kRouteGuideHost = @"http://localhost:50051";
-static NSString * const kPackage = @"examples";
+static NSString * const kPackage = @"routeguide";
static NSString * const kService = @"RouteGuide";
@interface LocalClearTextTests : XCTestCase