1/* 2 * 3 * Copyright 2015 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19#import "GRXWriteable.h" 20 21@implementation GRXWriteable { 22 GRXValueHandler _valueHandler; 23 GRXCompletionHandler _completionHandler; 24} 25 26+ (instancetype)writeableWithSingleHandler:(GRXSingleHandler)handler { 27 if (!handler) { 28 return [[self alloc] init]; 29 } 30 // We nilify this variable when the block is invoked, so that handler is only invoked once even if 31 // the writer tries to write multiple values. 32 __block GRXEventHandler eventHandler = ^(BOOL done, id value, NSError *error) { 33 // Nillify eventHandler before invoking handler, in case the latter causes the former to be 34 // executed recursively. Because blocks can be deallocated even during execution, we have to 35 // first retain handler locally to guarantee it's valid. 36 // TODO(jcanizales): Just turn this craziness into a simple subclass of GRXWriteable. 37 GRXSingleHandler singleHandler = handler; 38 eventHandler = nil; 39 40 if (value) { 41 singleHandler(value, nil); 42 } else if (error) { 43 singleHandler(nil, error); 44 } else { 45 NSDictionary *userInfo = 46 @{NSLocalizedDescriptionKey : @"The writer finished without producing any value."}; 47 // Even though RxLibrary is independent of gRPC, the domain and code here are, for the moment, 48 // set to the values of kGRPCErrorDomain and GRPCErrorCodeInternal. This way, the error formed 49 // is the one user of gRPC would expect if the server failed to produce a response. 50 // 51 // TODO(jcanizales): Figure out a way to keep errors of RxLibrary generic without making users 52 // of gRPC take care of two different error domains and error code enums. A possibility is to 53 // add error handling to GRXWriters or GRXWriteables, and use them to translate errors between 54 // the two domains. 55 static NSString *kGRPCErrorDomain = @"io.grpc"; 56 static NSUInteger kGRPCErrorCodeInternal = 13; 57 singleHandler( 58 nil, 59 [NSError errorWithDomain:kGRPCErrorDomain code:kGRPCErrorCodeInternal userInfo:userInfo]); 60 } 61 }; 62 return [self writeableWithEventHandler:^(BOOL done, id value, NSError *error) { 63 if (eventHandler) { 64 eventHandler(done, value, error); 65 } 66 }]; 67} 68 69+ (instancetype)writeableWithEventHandler:(GRXEventHandler)handler { 70 if (!handler) { 71 return [[self alloc] init]; 72 } 73 return [[self alloc] initWithValueHandler:^(id value) { 74 handler(NO, value, nil); 75 } 76 completionHandler:^(NSError *errorOrNil) { 77 handler(YES, nil, errorOrNil); 78 }]; 79} 80 81- (instancetype)init { 82 return [self initWithValueHandler:nil completionHandler:nil]; 83} 84 85// Designated initializer 86- (instancetype)initWithValueHandler:(GRXValueHandler)valueHandler 87 completionHandler:(GRXCompletionHandler)completionHandler { 88 if ((self = [super init])) { 89 _valueHandler = valueHandler; 90 _completionHandler = completionHandler; 91 } 92 return self; 93} 94 95- (void)writeValue:(id)value { 96 if (_valueHandler) { 97 _valueHandler(value); 98 } 99} 100 101- (void)writesFinishedWithError:(NSError *)errorOrNil { 102 if (_completionHandler) { 103 _completionHandler(errorOrNil); 104 } 105} 106@end 107