react-native呼叫ios native方法-回撥

姜家志發表於2016-08-09

上一篇中介紹了使用react-native呼叫ios native方法,在真實的使用場景中,不僅僅只是呼叫下Native的方法,還需要對結果進行處理,Native處理完之後返回結果再回撥會JavaScript中進行操作和處理。
這樣就需要使用JavaSctipt的回撥函式,對結果進行處理。在React Native中Object-c有兩種方式的回撥:RCTReponseSenderBlockPromises

RCTReponseSenderBlock

在JavaScript和Object-C的引數列表,有一類引數叫做RCTReponseSenderBlock對於JavaScript的Function,這個就是JavaScript呼叫Object-C的Callback(回撥函式)。

  • RCTReponseSenderBlock是在RCTBridgeModule.h定義的block.
    完整的定義:

typedef void (^RCTResponseSenderBlock)(NSArray *response);

RCTReponseSenderBlock定義個一個Object-C Bridge的操作,返回給JavaScript一個callback的方法。

他的引數是一個NSArray。其中第一個引數是error代表著錯誤資訊,如果沒有錯誤傳入null,後面的引數傳入自定義的內容。
具體例項:
先給UIAlertView新增兩個按鈕,在點選按鈕之後使用RCTResponseSenderBlock返回JavaScript。
先實現UIAlertViewDelegate

@interface RNIOSAlert : NSObject<RCTBridgeModule,UIAlertViewDelegate>
@end

定義一個變數用來儲存引數:

@implementation RNIOSAlert{
  RCTResponseSenderBlock _alertCallback;
}
@end

clickedButtonAtIndex方法中處理結果並呼叫回撥函式:


-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
  if (buttonIndex==0) {
    _alertCallback(@[@"cancel",]);
  }else{
    _alertCallback(@[[NSNull null],@1]);
  }
}

最後定義帶有回撥引數的Native方法:


RCT_EXPORT_METHOD(showAlertAndCallback:(RCTResponseSenderBlock)callback){
  _alertCallback=callback;
  UIAlertView * alertView=[[UIAlertView alloc] initWithTitle:@"react-native" message:@"是否繼續?" delegate:self cancelButtonTitle:@"關閉" otherButtonTitles:@"繼續", nil];
  [alertView show];

  
}

最在JavaScript中呼叫Native方法,並處理回撥:

 _alertCallback() {

        RNIOSAlert.showAlertAndCallback(function (err, datas) {
            if (err) {
                console.warn(`err`, `已取消`);
            } else {
                console.warn(`data`, `請繼續`);
            }

        });
    }

每次關閉UIAlertView都可以看到JavaScript處理的結果。

Promises

PromisesES6中的特性,它的目的是統一為JavaScript提供非同步程式設計的介面,避免Callback地獄,解決了Callback的層層巢狀。更加容易的對非同步操作進行控制。
在React Native中對Promises有很完善的支援,呼叫Object-C 的Native方法的時候,也可以Promise的方式讓程式碼執行從Object-C 回到JavaScript中。
先看Promises的兩個狀態。

  • Resolve和Reject

ResolveReject分量是Promise的兩種狀態,表示已解決和已拒絕,Resolve是正常的執行結果,而Reject會觸發catch操作。
在Object-C與之相對應的是:RCTPromiseResolveBlockRCTPromiseRejectBlock,兩個都是定義好的Object-C bridge。

RCTPromiseResolveBlock的實現:

typedef void (^RCTPromiseResolveBlock)(id result);

id為引數,當然傳參必須是Object-c和JavaScript定義好的引數。
RCTPromiseRejectBlock的實現:

typedef void (^RCTPromiseRejectBlock)(NSString *code, NSString *message, NSError *error);

RCTPromiseRejectBlock使用了NSError,還可以傳入自定義的code以及message

  • 使用Promise實現回撥
    首先要定義兩個變數用來儲存引數:

@implementation RNIOSAlert{
  RCTPromiseResolveBlock _resolveBlock;
  RCTPromiseRejectBlock _rejectBlock;
}

在定義提供給JavaScript呼叫的Native函式:

RCT_REMAP_METHOD(alertUserPromise, resolver:(RCTPromiseResolveBlock)resolver rejecter:(RCTPromiseRejectBlock)reject){
  _resolveBlock=resolver;
  _rejectBlock=reject;
  UIAlertView * alertView=[[UIAlertView alloc] initWithTitle:@"react-native" message:@"使用Promise?" delegate:self cancelButtonTitle:@"關閉" otherButtonTitles:@"繼續", nil];
  [alertView show];
}

這裡使用RCT_REMAP_METHOD巨集定義Native,他的第一個引數是方法名,後面的引數是方法的實現,在JavaScript呼叫Promise時並不需要在方法名中體現。
處理結果並使用回撥:

-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
  if (buttonIndex==0) {
    NSError * err=[NSError errorWithDomain:@"test" code:0 userInfo:nil];
    _rejectBlock(@"0",@"cancel",err);
  }else{
    _resolveBlock(@[@1]);
  }
}

JavaScript中呼叫該方法:

 _alertUsePromise() {
        RNIOSAlert.alertUserPromise().then((datas)=> {
            console.warn(`data`, datas);
        }).catch((err)=> {
            console.warn(`err`, err);
        });
    }

這裡的then處理的是Resovle狀態的結果,而catch處理的是Reject狀態的結果。
也可以使用async/await實現

 async  _alertPromise() {
        try {
            var datas = await RNIOSAlert.alertUserPromise();
            console.warn(`data`, datas);
        } catch (e) {
            console.warn(`err`, e);
        }
    }

async/await是兩個關鍵詞,用來把Promises的思想融入到語言本身。使用他們就不再需要寫catch這樣的偽同步的程式碼,直接使用try/catch/return這樣的關鍵詞就可以了.
Promises對於回撥的使用很便利,儘量避免了JavaScript的回撥地獄。

程式碼地址:https://github.com/jjz/react-…

相關文章