diff --git a/src/objective-c/GRPCClient/GRPCCall.h b/src/objective-c/GRPCClient/GRPCCall.h index 5918f8857a9..c9fda428552 100644 --- a/src/objective-c/GRPCClient/GRPCCall.h +++ b/src/objective-c/GRPCClient/GRPCCall.h @@ -50,6 +50,8 @@ #import #import +#include + #pragma mark gRPC errors /** Domain of NSError objects produced by gRPC. */ @@ -161,6 +163,9 @@ extern id const kGRPCTrailersKey; #pragma mark GRPCCall +/** Represents a single gRPC remote call. */ +@interface GRPCCall : GRXWriter + /** * 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. @@ -170,21 +175,6 @@ extern id const kGRPCTrailersKey; * 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 - -@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 - /** * These HTTP headers will be passed to the server as part of this call. Each HTTP header is a * name-value pair with string names and either string or binary values. @@ -200,7 +190,7 @@ extern id const kGRPCTrailersKey; * * The property is initialized to an empty NSMutableDictionary. */ -@property(atomic, readonly) id requestHeaders; +@property(atomic, readonly) NSMutableDictionary *requestHeaders; /** * This dictionary is populated with the HTTP headers received from the server. This happens before @@ -243,3 +233,24 @@ extern id const kGRPCTrailersKey; // TODO(jcanizales): Let specify a deadline. As a category of GRXWriter? @end + +#pragma mark Backwards compatibiity + +/** This protocol is kept for backwards compatibility with existing code. */ +DEPRECATED_MSG_ATTRIBUTE("Use NSDictionary or NSMutableDictionary instead.") +@protocol GRPCRequestHeaders +@property(nonatomic, readonly) NSUInteger count; + +- (id)objectForKeyedSubscript:(NSString *)key; +- (void)setObject:(id)obj forKeyedSubscript:(NSString *)key; + +- (void)removeAllObjects; +- (void)removeObjectForKey:(NSString *)key; +@end + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated" +/** This is only needed for backwards-compatibility. */ +@interface NSMutableDictionary (GRPCRequestHeaders) +@end +#pragma clang diagnostic pop diff --git a/src/objective-c/GRPCClient/GRPCCall.m b/src/objective-c/GRPCClient/GRPCCall.m index b6986bf59c0..f79b7d0bc0c 100644 --- a/src/objective-c/GRPCClient/GRPCCall.m +++ b/src/objective-c/GRPCClient/GRPCCall.m @@ -221,7 +221,7 @@ NSString * const kGRPCTrailersKey = @"io.grpc.TrailersKey"; #pragma mark Send headers -- (void)sendHeaders:(id)headers { +- (void)sendHeaders:(NSDictionary *)headers { // TODO(jcanizales): Add error handlers for async failures [_wrappedCall startBatchWithOperations:@[[[GRPCOpSendMetadata alloc] initWithMetadata:headers handler:nil]]]; diff --git a/src/objective-c/GRPCClient/private/GRPCRequestHeaders.h b/src/objective-c/GRPCClient/private/GRPCRequestHeaders.h index cf5a1be9d6a..b580f194061 100644 --- a/src/objective-c/GRPCClient/private/GRPCRequestHeaders.h +++ b/src/objective-c/GRPCClient/private/GRPCRequestHeaders.h @@ -32,21 +32,14 @@ */ #import -#include #import "../GRPCCall.h" -@interface GRPCRequestHeaders : NSObject - -@property(nonatomic, readonly) NSUInteger count; -@property(nonatomic, readonly) grpc_metadata *grpc_metadataArray; +@interface GRPCRequestHeaders : NSMutableDictionary - (instancetype)initWithCall:(GRPCCall *)call; -- (id)objectForKeyedSubscript:(NSString *)key; -- (void)setObject:(id)obj forKeyedSubscript:(NSString *)key; - -- (void)removeAllObjects; -- (void)removeObjectForKey:(NSString *)key; +- (instancetype)initWithCall:(GRPCCall *)call + storage:(NSMutableDictionary *)storage NS_DESIGNATED_INITIALIZER; @end diff --git a/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m b/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m index d23f21c0f9c..c6a03c145ea 100644 --- a/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m +++ b/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m @@ -68,17 +68,44 @@ static void CheckKeyValuePairIsValid(NSString *key, id value) { @implementation GRPCRequestHeaders { __weak GRPCCall *_call; + // The NSMutableDictionary superclass doesn't hold any storage (so that people can implement their + // own in subclasses). As that's not the reason we're subclassing, we just delegate storage to the + // default NSMutableDictionary subclass returned by the cluster (e.g. __NSDictionaryM on iOS 9). NSMutableDictionary *_delegate; } +- (instancetype)init { + return [self initWithCall:nil]; +} + +- (instancetype)initWithCapacity:(NSUInteger)numItems { + return [self init]; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + return [self init]; +} + - (instancetype)initWithCall:(GRPCCall *)call { + return [self initWithCall:call storage:[NSMutableDictionary dictionary]]; +} + +// Designated initializer +- (instancetype)initWithCall:(GRPCCall *)call storage:(NSMutableDictionary *)storage { + // TODO(jcanizales): Throw if call or storage are nil. if ((self = [super init])) { _call = call; - _delegate = [NSMutableDictionary dictionary]; + _delegate = storage; } return self; } +- (instancetype)initWithObjects:(const id _Nonnull __unsafe_unretained *)objects + forKeys:(const id _Nonnull __unsafe_unretained *)keys + count:(NSUInteger)cnt { + return [self init]; +} + - (void)checkCallIsNotStarted { if (_call.state != GRXWriterStateNotStarted) { [NSException raise:@"Invalid modification" @@ -86,11 +113,11 @@ static void CheckKeyValuePairIsValid(NSString *key, id value) { } } -- (id)objectForKeyedSubscript:(NSString *)key { +- (id)objectForKey:(NSString *)key { return _delegate[key.lowercaseString]; } -- (void)setObject:(id)obj forKeyedSubscript:(NSString *)key { +- (void)setObject:(id)obj forKey:(NSString *)key { [self checkCallIsNotStarted]; CheckIsNonNilASCII(@"Header name", key); key = key.lowercaseString; @@ -103,16 +130,12 @@ static void CheckKeyValuePairIsValid(NSString *key, id value) { [_delegate removeObjectForKey:key.lowercaseString]; } -- (void)removeAllObjects { - [self checkCallIsNotStarted]; - [_delegate removeAllObjects]; -} - - (NSUInteger)count { return _delegate.count; } -- (grpc_metadata *)grpc_metadataArray { - return _delegate.grpc_metadataArray; +- (NSEnumerator * _Nonnull)keyEnumerator { + return [_delegate keyEnumerator]; } + @end diff --git a/src/objective-c/GRPCClient/private/GRPCWrappedCall.h b/src/objective-c/GRPCClient/private/GRPCWrappedCall.h index 7747aa53ef0..71e7e0e54ee 100644 --- a/src/objective-c/GRPCClient/private/GRPCWrappedCall.h +++ b/src/objective-c/GRPCClient/private/GRPCWrappedCall.h @@ -45,7 +45,7 @@ @interface GRPCOpSendMetadata : GRPCOperation -- (instancetype)initWithMetadata:(GRPCRequestHeaders *)metadata +- (instancetype)initWithMetadata:(NSDictionary *)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 cea7c479e0f..fe3d51da53a 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:(GRPCRequestHeaders *)metadata handler:(void (^)())handler { +- (instancetype)initWithMetadata:(NSDictionary *)metadata handler:(void (^)())handler { if (self = [super init]) { _op.op = GRPC_OP_SEND_INITIAL_METADATA; _op.data.send_initial_metadata.count = metadata.count;