Add monitoring of connectivity

Move C-layer call creation into GRPCChannel, so that it always acts on a
non-destroyed channel.

Listen for connectivity in the GRPCCall, and destroy the channel.
pull/5720/head
Jorge Canizales 10 years ago
parent 180ca86350
commit f1d084a4d1
  1. 37
      src/objective-c/GRPCClient/GRPCCall.m
  2. 3
      src/objective-c/GRPCClient/private/GRPCChannel.h
  3. 14
      src/objective-c/GRPCClient/private/GRPCChannel.m
  4. 77
      src/objective-c/GRPCClient/private/GRPCConnectivityMonitor.h
  5. 188
      src/objective-c/GRPCClient/private/GRPCConnectivityMonitor.m
  6. 26
      src/objective-c/GRPCClient/private/GRPCHost.h
  7. 93
      src/objective-c/GRPCClient/private/GRPCHost.m
  8. 65
      src/objective-c/GRPCClient/private/GRPCReachabilityFlagNames.xmacro.h
  9. 2
      src/objective-c/GRPCClient/private/GRPCWrappedCall.h

@ -37,6 +37,8 @@
#include <grpc/support/time.h>
#import <RxLibrary/GRXConcurrentWriteable.h>
#import "private/GRPCConnectivityMonitor.h"
#import "private/GRPCHost.h"
#import "private/GRPCRequestHeaders.h"
#import "private/GRPCWrappedCall.h"
#import "private/NSData+GRPC.h"
@ -71,8 +73,11 @@ NSString * const kGRPCTrailersKey = @"io.grpc.TrailersKey";
@implementation GRPCCall {
dispatch_queue_t _callQueue;
NSString *_host;
NSString *_path;
GRPCWrappedCall *_wrappedCall;
dispatch_once_t _callAlreadyInvoked;
GRPCConnectivityMonitor *_connectivityMonitor;
// The C gRPC library has less guarantees on the ordering of events than we
// do. Particularly, in the face of errors, there's no ordering guarantee at
@ -115,13 +120,11 @@ NSString * const kGRPCTrailersKey = @"io.grpc.TrailersKey";
format:@"The requests writer can't be already started."];
}
if ((self = [super init])) {
_wrappedCall = [[GRPCWrappedCall alloc] initWithHost:host path:path];
if (!_wrappedCall) {
return nil;
}
_host = [host copy];
_path = [path copy];
// Serial queue to invoke the non-reentrant methods of the grpc_call object.
_callQueue = dispatch_queue_create("org.grpc.call", NULL);
_callQueue = dispatch_queue_create("io.grpc.call", NULL);
_requestWriter = requestWriter;
@ -156,7 +159,7 @@ NSString * const kGRPCTrailersKey = @"io.grpc.TrailersKey";
- (void)cancel {
[self finishWithError:[NSError errorWithDomain:kGRPCErrorDomain
code:GRPCErrorCodeCancelled
userInfo:nil]];
userInfo:@{NSLocalizedDescriptionKey: @"Canceled by app"}]];
[self cancelCall];
}
@ -354,8 +357,29 @@ NSString * const kGRPCTrailersKey = @"io.grpc.TrailersKey";
_retainSelf = self;
_responseWriteable = [[GRXConcurrentWriteable alloc] initWithWriteable:writeable];
_wrappedCall = [[GRPCWrappedCall alloc] initWithHost:_host path:_path];
NSAssert(_wrappedCall, @"Error allocating RPC objects. Low memory?");
[self sendHeaders:_requestHeaders];
[self invokeCall];
// TODO(jcanizales): Extract this logic somewhere common.
NSString *host = [NSURL URLWithString:[@"https://" stringByAppendingString:_host]].host;
if (!host) {
// TODO(jcanizales): Check this on init.
[NSException raise:NSInvalidArgumentException format:@"host of %@ is nil", _host];
}
__weak typeof(self) weakSelf = self;
_connectivityMonitor = [GRPCConnectivityMonitor monitorWithHost:host];
[_connectivityMonitor handleLossWithHandler:^{
typeof(self) strongSelf = weakSelf;
if (strongSelf) {
[strongSelf finishWithError:[NSError errorWithDomain:kGRPCErrorDomain
code:GRPCErrorCodeUnavailable
userInfo:@{NSLocalizedDescriptionKey: @"Connectivity lost."}]];
[[GRPCHost hostWithAddress:strongSelf->_host] disconnect];
}
}];
}
- (void)setState:(GRXWriterState)newState {
@ -385,4 +409,5 @@ NSString * const kGRPCTrailersKey = @"io.grpc.TrailersKey";
return;
}
}
@end

@ -35,6 +35,7 @@
#include <grpc/grpc.h>
@class GRPCCompletionQueue;
struct grpc_channel_credentials;
@ -80,4 +81,6 @@ struct grpc_channel_credentials;
+ (nonnull GRPCChannel *)insecureChannelWithHost:(nonnull NSString *)host
channelArgs:(nullable NSDictionary *)channelArgs;
- (nullable grpc_call *)unmanagedCallWithPath:(nonnull NSString *)path
completionQueue:(nonnull GRPCCompletionQueue *)queue;
@end

@ -38,6 +38,8 @@
#include <grpc/support/log.h>
#include <grpc/support/string_util.h>
#import "GRPCCompletionQueue.h"
/**
* Returns @c grpc_channel_credentials from the specified @c path. If the file at the path could not
* be read then NULL is returned. If NULL is returned, @c errorPtr may not be NULL if there are
@ -205,4 +207,16 @@ grpc_channel_args * buildChannelArgs(NSDictionary *dictionary) {
channelArgs:channelArgs];
}
- (grpc_call *)unmanagedCallWithPath:(NSString *)path
completionQueue:(GRPCCompletionQueue *)queue {
return grpc_channel_create_call(_unmanagedChannel,
NULL, GRPC_PROPAGATE_DEFAULTS,
queue.unmanagedQueue,
path.UTF8String,
// Get "host" from "host:port"
// TODO(jcanizales): Use NSURLs throughout, to clarify these.
[_host componentsSeparatedByString:@":"][0].UTF8String,
gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
}
@end

@ -0,0 +1,77 @@
/*
*
* Copyright 2016, 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>
#import <SystemConfiguration/SystemConfiguration.h>
@interface GRPCReachabilityFlags : NSObject
+ (nonnull instancetype)flagsWithFlags:(SCNetworkReachabilityFlags)flags;
/**
* One accessor method to query each of the different flags. Example:
@property(nonatomic, readonly) BOOL isCell;
*/
#define GRPC_XMACRO_ITEM(methodName, FlagName) \
@property(nonatomic, readonly) BOOL methodName;
#include "GRPCReachabilityFlagNames.xmacro.h"
#undef GRPC_XMACRO_ITEM
@property(nonatomic, readonly) BOOL isHostReachable;
@end
@interface GRPCConnectivityMonitor : NSObject
+ (nullable instancetype)monitorWithHost:(nonnull NSString *)hostName;
- (nonnull instancetype)init NS_UNAVAILABLE;
/**
* Queue on which callbacks will be dispatched. Default is the main queue. Set it before calling
* handleLossWithHandler:.
*/
// TODO(jcanizales): Default to a serial background queue instead.
@property(nonatomic, strong, null_resettable) dispatch_queue_t queue;
/**
* Calls handler the next time the connectivity to this instance's host is lost. If this instance is
* released before that happens, the handler won't be called.
* Only one handler is active at a time, so if this method is called again before the previous
* handler has been called, it might never be called at all (or yes, if it has already been queued).
*/
- (void)handleLossWithHandler:(nonnull void (^)())handler;
@end

@ -0,0 +1,188 @@
/*
*
* Copyright 2016, 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 "GRPCConnectivityMonitor.h"
#pragma mark Flags
@implementation GRPCReachabilityFlags {
SCNetworkReachabilityFlags _flags;
}
+ (instancetype)flagsWithFlags:(SCNetworkReachabilityFlags)flags {
return [[self alloc] initWithFlags:flags];
}
- (instancetype)initWithFlags:(SCNetworkReachabilityFlags)flags {
if ((self = [super init])) {
_flags = flags;
}
return self;
}
/*
* One accessor method implementation per flag. Example:
- (BOOL)isCell { \
return !!(_flags & kSCNetworkReachabilityFlagsIsWWAN); \
}
*/
#define GRPC_XMACRO_ITEM(methodName, FlagName) \
- (BOOL)methodName { \
return !!(_flags & kSCNetworkReachabilityFlags ## FlagName); \
}
#include "GRPCReachabilityFlagNames.xmacro.h"
#undef GRPC_XMACRO_ITEM
- (BOOL)isHostReachable {
// Note: connectionOnDemand means it'll be reachable only if using the CFSocketStream API or APIs
// on top of it.
// connectionRequired means we can't tell until a connection is attempted (e.g. for VPN on
// demand).
return self.reachable && !self.interventionRequired && !self.connectionOnDemand;
}
- (NSString *)description {
NSMutableArray *activeOptions = [NSMutableArray arrayWithCapacity:9];
/*
* For each flag, add its name to the array if it's ON. Example:
if (self.isCell) {
[activeOptions addObject:@"isCell"];
}
*/
#define GRPC_XMACRO_ITEM(methodName, FlagName) \
if (self.methodName) { \
[activeOptions addObject:@#methodName]; \
}
#include "GRPCReachabilityFlagNames.xmacro.h"
#undef GRPC_XMACRO_ITEM
return activeOptions.count == 0 ? @"(none)" : [activeOptions componentsJoinedByString:@", "];
}
- (BOOL)isEqual:(id)object {
return [object isKindOfClass:[GRPCReachabilityFlags class]] &&
_flags == ((GRPCReachabilityFlags *)object)->_flags;
}
- (NSUInteger)hash {
return _flags;
}
@end
#pragma mark Connectivity Monitor
// Assumes the third argument is a block that accepts SCNetworkReachabilityFlags, and passes the
// received ones to it.
static void PassFlagsToContextInfoBlock(SCNetworkReachabilityRef target,
SCNetworkReachabilityFlags flags,
void *info) {
#pragma unused (target)
((__bridge void (^)(SCNetworkReachabilityFlags))info)(flags);
}
@implementation GRPCConnectivityMonitor {
SCNetworkReachabilityRef _reachabilityRef;
}
- (nullable instancetype)initWithReachability:(nullable SCNetworkReachabilityRef)reachability {
if (!reachability) {
return nil;
}
if ((self = [super init])) {
_reachabilityRef = CFRetain(reachability);
_queue = dispatch_get_main_queue();
}
return self;
}
+ (nullable instancetype)monitorWithHost:(nonnull NSString *)host {
const char *hostName = host.UTF8String;
if (!hostName) {
[NSException raise:NSInvalidArgumentException
format:@"host.UTF8String returns NULL for %@", host];
}
SCNetworkReachabilityRef reachability =
SCNetworkReachabilityCreateWithName(NULL, hostName);
GRPCConnectivityMonitor *returnValue = [[self alloc] initWithReachability:reachability];
if (reachability) {
CFRelease(reachability);
}
return returnValue;
}
- (void)handleLossWithHandler:(void (^)())handler {
__weak typeof(self) weakSelf = self;
[self startListeningWithHandler:^(GRPCReachabilityFlags *flags) {
if (!flags.isHostReachable) {
[weakSelf stopListening];
handler();
}
}];
}
- (void)startListeningWithHandler:(void (^)(GRPCReachabilityFlags *))handler {
SCNetworkReachabilityContext context = {
.version = 0,
.info = (__bridge_retained void *)^(SCNetworkReachabilityFlags flags){
// Pass the flags as as Objective-C wrapper.
handler([[GRPCReachabilityFlags alloc] initWithFlags:flags]);
},
.release = CFRelease
};
SCNetworkReachabilitySetCallback(_reachabilityRef, PassFlagsToContextInfoBlock, &context);
SCNetworkReachabilitySetDispatchQueue(_reachabilityRef, _queue);
}
- (void)stopListening {
SCNetworkReachabilitySetCallback(_reachabilityRef, NULL, NULL);
SCNetworkReachabilitySetDispatchQueue(_reachabilityRef, NULL);
}
- (void)setQueue:(dispatch_queue_t)queue {
_queue = queue ?: dispatch_get_main_queue();
}
- (void)dealloc {
if (_reachabilityRef) {
[self stopListening];
CFRelease(_reachabilityRef);
}
}
@end

@ -33,27 +33,39 @@
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@class GRPCCompletionQueue;
struct grpc_call;
@interface GRPCHost : NSObject
@property(nonatomic, readonly) NSString *address;
@property(nonatomic, copy) NSString *userAgentPrefix;
@property(nonatomic, copy, nullable) NSString *userAgentPrefix;
/** The following properties should only be modified for testing: */
@property(nonatomic, getter=isSecure) BOOL secure;
@property(nonatomic, copy) NSString *pathToCertificates;
@property(nonatomic, copy) NSString *hostNameOverride;
@property(nonatomic, copy, nullable) NSString *pathToCertificates;
@property(nonatomic, copy, nullable) NSString *hostNameOverride;
- (nullable instancetype)init NS_UNAVAILABLE;
/** Host objects initialized with the same address are the same. */
+ (instancetype)hostWithAddress:(NSString *)address;
- (instancetype)initWithAddress:(NSString *)address NS_DESIGNATED_INITIALIZER;
+ (nullable instancetype)hostWithAddress:(NSString *)address;
- (nullable instancetype)initWithAddress:(NSString *)address NS_DESIGNATED_INITIALIZER;
/** Create a grpc_call object to the provided path on this host. */
- (struct grpc_call *)unmanagedCallWithPath:(NSString *)path
completionQueue:(GRPCCompletionQueue *)queue;
- (nullable struct grpc_call *)unmanagedCallWithPath:(NSString *)path
completionQueue:(GRPCCompletionQueue *)queue;
// TODO: There's a race when a new RPC is coming through just as an existing one is getting
// notified that there's no connectivity. If connectivity comes back at that moment, the new RPC
// will have its channel destroyed by the other RPC, and will never get notified of a problem, so
// it'll hang (the C layer logs a timeout, with exponential back off). One solution could be to pass
// the GRPCChannel to the GRPCCall, renaming this as "disconnectChannel:channel", which would only
// act on that specific channel.
- (void)disconnect;
@end
NS_ASSUME_NONNULL_END

@ -34,33 +34,30 @@
#import "GRPCHost.h"
#include <grpc/grpc.h>
#import <GRPCClient/GRPCCall.h>
#import <GRPCClient/GRPCCall+ChannelArg.h>
#import "GRPCChannel.h"
#import "GRPCCompletionQueue.h"
#import "NSDictionary+GRPC.h"
NS_ASSUME_NONNULL_BEGIN
// TODO(jcanizales): Generate the version in a standalone header, from templates. Like
// templates/src/core/surface/version.c.template .
#define GRPC_OBJC_VERSION_STRING @"0.13.0"
@interface GRPCHost ()
// TODO(mlumish): Investigate whether caching channels with strong links is a good idea.
@property(nonatomic, strong) GRPCChannel *channel;
@end
@implementation GRPCHost
+ (instancetype)hostWithAddress:(NSString *)address {
return [[self alloc] initWithAddress:address];
@implementation GRPCHost {
// TODO(mlumish): Investigate whether caching channels with strong links is a good idea.
GRPCChannel *_channel;
}
- (instancetype)init {
return [self initWithAddress:nil];
+ (nullable instancetype)hostWithAddress:(NSString *)address {
return [[self alloc] initWithAddress:address];
}
// Default initializer.
- (instancetype)initWithAddress:(NSString *)address {
- (nullable instancetype)initWithAddress:(NSString *)address {
if (!address) {
return nil;
}
@ -95,46 +92,42 @@
return self;
}
- (grpc_call *)unmanagedCallWithPath:(NSString *)path completionQueue:(GRPCCompletionQueue *)queue {
if (!queue || !path || !self.channel) {
return NULL;
- (nullable grpc_call *)unmanagedCallWithPath:(NSString *)path
completionQueue:(GRPCCompletionQueue *)queue {
@synchronized(self) {
if (!_channel) {
_channel = [self newChannel];
}
return [_channel unmanagedCallWithPath:path completionQueue:queue];
}
return grpc_channel_create_call(self.channel.unmanagedChannel,
NULL, GRPC_PROPAGATE_DEFAULTS,
queue.unmanagedQueue,
path.UTF8String,
self.hostName.UTF8String,
gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
}
- (GRPCChannel *)channel {
// Create it lazily, because we don't want to open a connection just because someone is
// configuring a host.
- (NSDictionary *)channelArgs {
NSMutableDictionary *args = [NSMutableDictionary dictionary];
if (!_channel) {
NSMutableDictionary *args = [NSMutableDictionary dictionary];
// TODO(jcanizales): Add OS and device information (see
// https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#user-agents ).
NSString *userAgent = @"grpc-objc/" GRPC_OBJC_VERSION_STRING;
if (_userAgentPrefix) {
userAgent = [_userAgentPrefix stringByAppendingFormat:@" %@", userAgent];
}
args[@GRPC_ARG_PRIMARY_USER_AGENT_STRING] = userAgent;
// TODO(jcanizales): Add OS and device information (see
// https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#user-agents ).
NSString *userAgent = @"grpc-objc/" GRPC_OBJC_VERSION_STRING;
if (_userAgentPrefix) {
userAgent = [@[_userAgentPrefix, userAgent] componentsJoinedByString:@" "];
}
args[@GRPC_ARG_PRIMARY_USER_AGENT_STRING] = userAgent;
if (_secure) {
if (_hostNameOverride) {
args[@GRPC_SSL_TARGET_NAME_OVERRIDE_ARG] = _hostNameOverride;
}
_channel = [GRPCChannel secureChannelWithHost:_address
pathToCertificates:_pathToCertificates
channelArgs:args];
} else {
_channel = [GRPCChannel insecureChannelWithHost:_address channelArgs:args];
}
if (_secure && _hostNameOverride) {
args[@GRPC_SSL_TARGET_NAME_OVERRIDE_ARG] = _hostNameOverride;
}
return args;
}
- (GRPCChannel *)newChannel {
NSDictionary *args = [self channelArgs];
if (_secure) {
return [GRPCChannel secureChannelWithHost:_address
pathToCertificates:_pathToCertificates
channelArgs:args];
} else {
return [GRPCChannel insecureChannelWithHost:_address channelArgs:args];
}
return _channel;
}
- (NSString *)hostName {
@ -142,7 +135,15 @@
return _hostNameOverride ?: _address;
}
- (void)disconnect {
@synchronized(self) {
_channel = nil;
}
}
// TODO(jcanizales): Don't let set |secure| to |NO| if |pathToCertificates| or |hostNameOverride|
// have been set. Don't let set either of the latter if |secure| has been set to |NO|.
@end
NS_ASSUME_NONNULL_END

@ -0,0 +1,65 @@
/*
*
* Copyright 2016, 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.
*
*/
/**
* "X-macro" file that lists the flags names of Apple's Network Reachability API, along with a nice
* Objective-C method name used to query each of them.
*
* Example usage: To generate a dictionary from flag value to name, one can do:
NSDictionary *flagNames = @{
#define GRPC_XMACRO_ITEM(methodName, FlagName) \
@(kSCNetworkReachabilityFlags ## FlagName): @#methodName,
#include "GRXReachabilityFlagNames.xmacro.h"
#undef GRPC_XMACRO_ITEM
};
XCTAssertEqualObjects(flagNames[@(kSCNetworkReachabilityFlagsIsWWAN)], @"isCell");
*/
#ifndef GRPC_XMACRO_ITEM
#error This file is to be used with the "X-macro" pattern: Please #define \
GRPC_XMACRO_ITEM(methodName, FlagName), then #include this file, and then #undef \
GRPC_XMACRO_ITEM.
#endif
GRPC_XMACRO_ITEM(isCell, IsWWAN)
GRPC_XMACRO_ITEM(reachable, Reachable)
GRPC_XMACRO_ITEM(transientConnection, TransientConnection)
GRPC_XMACRO_ITEM(connectionRequired, ConnectionRequired)
GRPC_XMACRO_ITEM(connectionOnTraffic, ConnectionOnTraffic)
GRPC_XMACRO_ITEM(interventionRequired, InterventionRequired)
GRPC_XMACRO_ITEM(connectionOnDemand, ConnectionOnDemand)
GRPC_XMACRO_ITEM(isLocalAddress, IsLocalAddress)
GRPC_XMACRO_ITEM(isDirect, IsDirect)

@ -34,7 +34,6 @@
#import <Foundation/Foundation.h>
#include <grpc/grpc.h>
#import "GRPCChannel.h"
#import "GRPCRequestHeaders.h"
@interface GRPCOperation : NSObject
@ -94,4 +93,5 @@
- (void)startBatchWithOperations:(NSArray *)ops;
- (void)cancel;
@end

Loading…
Cancel
Save