简体   繁体   中英

Application tried to present a nil modal view controller on target in React Native Objective-C extension

I am trying to port an iOS app over to React Native.

In this iOS app, one of the functions that needs to be done is to integrate it with a PayPal library (which is currently deprecated - we want to move away from it at some point later this year, but do not have the resources to do so now).

All we're doing with this library is obtaining a unique code from PayPal - that requires a View Controller to pop up, accept client credentials and return a code giving access.

I am extremely new to Objective-C.

I've got this so far ( note: I have not included all the methods/properties but can include any missing ):

COMPLETE PaypalSdk.h and PaypalSdk.m are at the bottom now

I am basing this off of this library:

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

And this documentation:

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

However when trying what I am above, I get the following error:

React Native 错误屏幕

How exactly should I resolve this? It seems to need a View Controller but I'm not totally sure how to launch one from React Native in this context.

All we're trying to get is the shared profile information.

Here is one of the stack traces:

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
)

Here is my complete PayPalSdk.m file:

#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

And here is my complete PaypalSdk.h file:

#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

So you need to access the root view controller and then call presentViewController on the same. Something like below should do the trick:

- (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];
}

I haven't tried this so please check and let me know if this works. Otherwise find a view on which you can present the view controller.


Updating the answer based on the interaction with the OP:

  • For this to make it work, the view related methods had to be overriden with RCT methods and they need to be called before calling the generatecode RCT method. This along with root view controller seems to fix the issue.

Do not use that SDK. It is very old and deprecated.

If you need a native SDK to process payments with PayPal, you can use express checkout via Braintree. It also requires a web service of your own: https://developer.paypal.com/docs/accept-payments/express-checkout/ec-braintree-sdk/get-started/

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM