应用程序试图在 React Native Objective-C 扩展中的目标上呈现一个 nil 模态视图控制器

Posted

技术标签:

【中文标题】应用程序试图在 React Native Objective-C 扩展中的目标上呈现一个 nil 模态视图控制器【英文标题】:Application tried to present a nil modal view controller on target in React Native Objective-C extension 【发布时间】:2020-08-26 06:04:23 【问题描述】:

我正在尝试将 ios 应用程序移植到 React Native。

在这个 iOS 应用程序中,需要完成的功能之一是将其与 PayPal 库集成(目前已弃用 - 我们希望在今年晚些时候离开它,但没有资源)。

我们对这个库所做的只是从 PayPal 获取一个唯一代码 - 这需要一个视图控制器弹出,接受客户端凭据并返回一个代码来授予访问权限。

我是非常Objective-C的新手。

到目前为止我已经得到了这个(注意:我没有包括所有的方法/属性,但可以包括任何缺失的):

COMPLETE PaypalSdk.h 和 PaypalSdk.m 现在位于底部

我基于这个库:

https://github.com/paypal/PayPal-iOS-SDK/blob/master/SampleApp/PayPal-iOS-SDK-Sample-App/ZZMainViewController.m

还有这个文档:

https://github.com/paypal/PayPal-iOS-SDK/blob/master/docs/profile_sharing_mobile.md

但是,当我尝试上面的操作时,我收到以下错误:

我应该如何解决这个问题?它似乎需要一个视图控制器,但我不完全确定如何在这种情况下从 React Native 启动一个。

我们试图获取的只是共享的个人资料信息。

这是堆栈跟踪之一:

callstack: (
    0   CoreFoundation                      0x00007fff23c7127e __exceptionPreprocess + 350
    1   libobjc.A.dylib                     0x00007fff513fbb20 objc_exception_throw + 48
    2   UIKitCore                           0x00007fff47a25b1a -[UIViewController _presentViewController:withAnimationController:completion:] + 5247
    3   UIKitCore                           0x00007fff47a2801b __63-[UIViewController _presentViewController:animated:completion:]_block_invoke + 98
    4   UIKitCore                           0x00007fff47a28533 -[UIViewController _performCoordinatedPresentOrDismiss:animated:] + 511
    5   UIKitCore                           0x00007fff47a27f79 -[UIViewController _presentViewController:animated:completion:] + 187
    6   UIKitCore                           0x00007fff47a281e0 -[UIViewController presentViewController:animated:completion:] + 150
    7   myappname                   0x000000010f92f8ac -[PaypalSdk getUserAuthorizationForProfileSharing] + 348
    8   myappname                   0x000000010f92fd99 -[PaypalSdk generateCode:] + 233
    9   CoreFoundation                      0x00007fff23c7820c __invoking___ + 140
    10  CoreFoundation                      0x00007fff23c753af -[NSInvocation invoke] + 319
    11  CoreFoundation                      0x00007fff23c75684 -[NSInvocation invokeWithTarget:] + 68
    12  myappname                   0x000000010f6e3902 -[RCTModuleMethod invokeWithBridge:module:arguments:] + 2658
    13  myappname                   0x000000010f6e7a37 _ZN8facebook5reactL11invokeInnerEP9RCTBridgeP13RCTModuleDatajRKN5folly7dynamicE + 791
    14  myappname                   0x000000010f6e7543 _ZZN8facebook5react15RCTNativeModule6invokeEjON5folly7dynamicEiENK3$_0clEv + 131
    15  myappname                   0x000000010f6e74b9 ___ZN8facebook5react15RCTNativeModule6invokeEjON5folly7dynamicEi_block_invoke + 25
    16  libdispatch.dylib                   0x0000000110caddd4 _dispatch_call_block_and_release + 12
    17  libdispatch.dylib                   0x0000000110caed48 _dispatch_client_callout + 8
    18  libdispatch.dylib                   0x0000000110cb55ef _dispatch_lane_serial_drain + 788
    19  libdispatch.dylib                   0x0000000110cb617f _dispatch_lane_invoke + 422
    20  libdispatch.dylib                   0x0000000110cc1a4e _dispatch_workloop_worker_thread + 719
    21  libsystem_pthread.dylib             0x00007fff5246371b _pthread_wqthread + 290
    22  libsystem_pthread.dylib             0x00007fff5246357b start_wqthread + 15
)

这是我完整的 PayPalSdk.m 文件:

#import <PayPal-iOS-SDK/PayPalMobile.h>
#import <PayPal-iOS-SDK/PayPalConfiguration.h>
#import <PayPal-iOS-SDK/PayPalOAuthScopes.h>
#import <PayPal-iOS-SDK/PayPalProfileSharingViewController.h>
#import <QuartzCore/QuartzCore.h>
#import "PaypalSdk.h"

@interface PaypalSdk ()

@property(nonatomic, strong, readwrite) IBOutlet UIButton *payNowButton;
@property(nonatomic, strong, readwrite) IBOutlet UIButton *payFutureButton;
@property(nonatomic, strong, readwrite) IBOutlet UIView *successView;

@property(nonatomic, strong, readwrite) PayPalConfiguration *payPalConfig;

@end

@implementation PaypalSdk

#define kPayPalEnvironment PayPalEnvironmentProduction

//int *REQUEST_CODE_PROFILE_SHARING = 3;


- (void)viewDidLoad 
  [super viewDidLoad];
  self.title = @"Pinyada PayPal";

  // Set up payPalConfig
  self.payPalConfig = [[PayPalConfiguration alloc] init];
  self.payPalConfig.acceptCreditCards = NO;
  self.payPalConfig.merchantName = @"Pinyada PayPal";
  self.payPalConfig.merchantPrivacyPolicyURL = [NSURL URLWithString:@"https://www.paypal.com/webapps/mpp/ua/privacy-full"];
  self.payPalConfig.merchantUserAgreementURL = [NSURL URLWithString:@"https://www.paypal.com/webapps/mpp/ua/useragreement-full"];

  NSLog(@"PayPal iOS SDK version: %@", [PayPalMobile libraryVersion]);



- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 

#warning "Enter your credentials"
  [PayPalMobile initializeWithClientIdsForEnvironments:@PayPalEnvironmentProduction : @"PayPalProductionID",
                                                         PayPalEnvironmentSandbox : @"YOUR_CLIENT_ID_FOR_SANDBOX"];
  return YES;


/*- (void)generateCode:()code 
NSLog(@"Test");
*/

- (void)viewWillAppear:(BOOL)animated 
  [super viewWillAppear:animated];

  // Start out working with the mock environment. When you are ready, switch to PayPalEnvironmentProduction.
  [PayPalMobile preconnectWithEnvironment:PayPalEnvironmentProduction];


- (IBAction)getUserAuthorizationForProfileSharing:(id)sender 

  NSSet *scopeValues = [NSSet setWithArray:@[kPayPalOAuth2ScopeOpenId, kPayPalOAuth2ScopeEmail, kPayPalOAuth2ScopeAddress, kPayPalOAuth2ScopePhone]];

  PayPalProfileSharingViewController *profileSharingPaymentViewController = [[PayPalProfileSharingViewController alloc] initWithScopeValues:scopeValues configuration:self.payPalConfig delegate:self];
  [self presentViewController:profileSharingPaymentViewController animated:YES completion:nil];



- (IBAction)obtainConsent 

  // Choose whichever scope-values apply in your case. See `PayPalOAuthScopes.h` for a complete list of available scope-values.
  NSSet *scopeValues = [NSSet setWithArray:@[kPayPalOAuth2ScopeOpenId, kPayPalOAuth2ScopeEmail, kPayPalOAuth2ScopeAddress, kPayPalOAuth2ScopePhone]];

  PayPalProfileSharingViewController *psViewController;
  NSLog(@"PS VIEW CONTROLLER");
  NSLog(psViewController);
  psViewController = [[PayPalProfileSharingViewController alloc] initWithScopeValues:scopeValues
                                                                       configuration:self.payPalConfig
                                                                            delegate:self];

// Access the root view controller
  UIViewController *rootviewcontroller= [UIApplication sharedApplication].keyWindow.rootViewController;



  // Present the PayPalProfileSharingViewController
  [ rootviewcontroller presentViewController:psViewController animated:YES completion:nil];


- (void)userDidCancelPayPalProfileSharingViewController:(PayPalProfileSharingViewController *)profileSharingViewController 
  // User cancelled login. Dismiss the PayPalProfileSharingViewController, breathe deeply.
  [self dismissViewControllerAnimated:YES completion:nil];


- (void)payPalProfileSharingViewController:(PayPalProfileSharingViewController *)profileSharingViewController
             userDidLogInWithAuthorization:(NSDictionary *)profileSharingAuthorization 
  // The user has successfully logged into PayPal, and has consented to profile sharing.

      NSLog(@"REACT NATIVE ENV test");

  // Be sure to dismiss the PayPalProfileSharingViewController.
  [self dismissViewControllerAnimated:YES completion:nil];


RCT_EXPORT_MODULE()

RCT_EXPORT_METHOD(generateCode: (RCTResponseSenderBlock)callback) 

    [PayPalMobile initializeWithClientIdsForEnvironments:@PayPalEnvironmentProduction : @"PayPalProductionID",
                                                           PayPalEnvironmentSandbox : @"YOUR_CLIENT_ID_FOR_SANDBOX"];
    [self obtainConsent];

    NSLog(@"REACT NATIVE ENV test");

    //code = @"this is a test!";

    // TODO: Implement some actually useful functionality
    callback(@[[NSNull null], @"test"]);


@end

这是我完整的 PaypalSdk.h 文件:

#import "RCTBridgeModule.h"
#import "PayPalMobile.h"

@interface PaypalSdk : UIViewController <RCTBridgeModule, PayPalProfileSharingDelegate>

@property(nonatomic, strong, readwrite) NSString *environment;
@property(nonatomic, strong, readwrite) NSString *resultText;
@property(nonatomic, strong) UIWindow *window;

@property(nonatomic, strong) UIViewController *rootViewController;

@end

【问题讨论】:

您能分享红色模态中显示的完整堆栈跟踪吗? @CarlaCamargo 我有两个不同的堆栈跟踪,这取决于我是否进行了您建议的更改 - 您想要哪个堆栈跟踪,在您的建议之前还是之后? 【参考方案1】:

所以你需要访问根视图控制器,然后调用presentViewController。像下面这样的东西应该可以解决问题:

- (IBAction)obtainConsent 

  // Choose whichever scope-values apply in your case. See `PayPalOAuthScopes.h` for a complete list of available scope-values.
  NSSet *scopeValues = [NSSet setWithArray:@[kPayPalOAuth2ScopeOpenId, kPayPalOAuth2ScopeEmail, kPayPalOAuth2ScopeAddress, kPayPalOAuth2ScopePhone]];

  PayPalProfileSharingViewController *psViewController;
  psViewController = [[PayPalProfileSharingViewController alloc] initWithScopeValues:scopeValues
                                                                       configuration:self.payPalConfig
                                                                            delegate:self];

// Access the root view controller
  UIViewController *rootviewcontroller= [UIApplication sharedApplication].keyWindow.rootViewController;



  // Present the PayPalProfileSharingViewController
  [ rootviewcontroller presentViewController:psViewController animated:YES completion:nil];

我还没有尝试过,所以请检查并告诉我这是否有效。否则,找到一个可以呈现视图控制器的视图。


根据与 OP 的交互更新答案:

为此,必须使用 RCT 方法覆盖与视图相关的方法,并且需要在调用 generatecode RCT 方法之前调用它们。这与根视图控制器一起似乎可以解决问题。

【讨论】:

不@manisg,用这个替换旧的getConsent时仍然有一个nil模式视图控制器 您在演示之前有一个 nil psViewController?还是在它之后? @CarlaCamargo 所以现在异常说,“应用程序试图在目标 @CecilRodriguez 你检查过 psViewController 是否为 nil 吗? @manishg 我不知道该怎么做。我对Objective C非常陌生。似乎正在设置它 - PayPalProfileSharingViewController *profileSharingPaymentViewController = [[PayPalProfileSharingViewController alloc] initWithScopeValues:scopeValues configuration:self.payPalConfig delegate:self];【参考方案2】:

请勿使用该 SDK。它非常古老且已被弃用。

如果您需要原生 SDK 来处理使用 PayPal 的付款,您可以通过 Braintree 使用快速结账。它还需要您自己的网络服务:https://developer.paypal.com/docs/accept-payments/express-checkout/ec-braintree-sdk/get-started/

【讨论】:

我不需要 SDK 来处理付款。我需要用于共享配置文件的 SDK。我知道它已被弃用,正如我之前所说,我们希望摆脱它(如果我们能找到解决方案),但我们目前没有资源来摆脱它。从字面上看,我们使用它的只是个人资料共享,而 Braintree SDK 不支持。 我不知道共享个人资料信息是什么,但 PayPal 支持 API,您可以将其用于“与 PayPal 连接”等类型的事情 我不确定它与 iOS 的集成效果如何,但我们会在中期再次研究它。我们现在迫切需要在下个月内使用这个 SDK,并且想出一种替代方法是不可能的。很好,我们不应该使用这个 SDK,但是我们当前的实现已经在使用这个 SDK。我们正在从 Swift 应用程序转变为 RN 应用程序,然后一旦我们有一个适用于所有平台的统一工作应用程序,我们将寻求替换 PayPal 身份信息。 我仍然必须提醒您不要努力让一个已有 6 年历史的解决方案发挥作用。但如果你坚持走这条路,可能相关:github.com/paypal/PayPal-iOS-SDK/issues/390 再一次,我们已经在使用这个解决方案了。我们正在将当前应用程序迁移到新的多平台应用程序,之后我们将弃用此应用程序中进行个人资料共享的最新方式。 Connect with PayPal 仅限网站。我没有看到任何将它与移动应用程序集成的方法。我没有看到任何专门针对移动应用程序的解决方案,因此需要研究。与此同时,这是我们拥有的解决方案,我们的 API 将像现在一样被弃用

以上是关于应用程序试图在 React Native Objective-C 扩展中的目标上呈现一个 nil 模态视图控制器的主要内容,如果未能解决你的问题,请参考以下文章

应用程序试图在 React Native Objective-C 扩展中的目标上呈现一个 nil 模态视图控制器

RNGoogleSignin 试图覆盖 RNGoogleSigninModule 错误

如何在 React Native 中使用 iframe?

在 React Native WebView 中加载捆绑的静态资产

使用 react-native-pdf 在 react-native 上获取准确的 x 和 y 坐标

在使用适用于 iOS 和 Android 的 React Native 时,我还能使用原生第三方库吗?