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