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(nil, [NSError errorWithDomain:kGRPCErrorDomain 58 code:kGRPCErrorCodeInternal 59 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] 74 initWithValueHandler:^(id value) { 75 handler(NO, value, nil); 76 } 77 completionHandler:^(NSError *errorOrNil) { 78 handler(YES, nil, errorOrNil); 79 }]; 80} 81 82- (instancetype)init { 83 return [self initWithValueHandler:nil completionHandler:nil]; 84} 85 86// Designated initializer 87- (instancetype)initWithValueHandler:(GRXValueHandler)valueHandler 88 completionHandler:(GRXCompletionHandler)completionHandler { 89 if ((self = [super init])) { 90 _valueHandler = valueHandler; 91 _completionHandler = completionHandler; 92 } 93 return self; 94} 95 96- (void)writeValue:(id)value { 97 if (_valueHandler) { 98 _valueHandler(value); 99 } 100} 101 102- (void)writesFinishedWithError:(NSError *)errorOrNil { 103 if (_completionHandler) { 104 _completionHandler(errorOrNil); 105 } 106} 107@end 108