Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support native redirects #783

Merged
merged 5 commits into from
Sep 13, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions Stripe/STPRedirectContext.m
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,13 @@

NS_ASSUME_NONNULL_BEGIN

typedef void (^STPNativeRedirectCompletionBlock)(BOOL success);
typedef void (^STPBoolCompletionBlock)(BOOL success);

@interface STPRedirectContext () <SFSafariViewControllerDelegate, STPURLCallbackListener>
@property (nonatomic, copy) STPRedirectContextCompletionBlock completion;
@property (nonatomic, strong) STPSource *source;
@property (nonatomic, strong, nullable) SFSafariViewController *safariVC;
@property (nonatomic, assign, readwrite) STPRedirectContextState state;
@end

@implementation STPRedirectContext
Expand Down Expand Up @@ -68,7 +69,7 @@ - (void)dealloc {
[self unsubscribeFromNotificationsAndDismissPresentedViewControllers];
}

- (void)performAppRedirectIfPossibleWithCompletion:(STPNativeRedirectCompletionBlock)onCompletion {
- (void)performAppRedirectIfPossibleWithCompletion:(STPBoolCompletionBlock)onCompletion {
FAUXPAS_IGNORED_IN_METHOD(APIAvailability)

if (self.state == STPRedirectContextStateNotStarted) {
Expand All @@ -80,7 +81,7 @@ - (void)performAppRedirectIfPossibleWithCompletion:(STPNativeRedirectCompletionB

// Optimistically start listening in case we get app switched away.
// If the app switch fails we'll undo this later
_state = STPRedirectContextStateInProgress;
self.state = STPRedirectContextStateInProgress;
[self subscribeToUrlAndForegroundNotifications];

UIApplication *application = [UIApplication sharedApplication];
Expand All @@ -90,7 +91,7 @@ - (void)performAppRedirectIfPossibleWithCompletion:(STPNativeRedirectCompletionB
[application openURL:nativeUrl options:@{} completionHandler:^(BOOL success) {
if (!success) {
STRONG(self);
self->_state = STPRedirectContextStateNotStarted;
self.state = STPRedirectContextStateNotStarted;
[self unsubscribeFromNotifications];
}
onCompletion(success);
Expand All @@ -100,7 +101,7 @@ - (void)performAppRedirectIfPossibleWithCompletion:(STPNativeRedirectCompletionB
_state = STPRedirectContextStateInProgress;
BOOL opened = [application openURL:nativeUrl];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OOC, do you know if this behaves differently from the new openURL:options:completionHandler: method? E.g. does the returned success value here always match the success value in the async completion block?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I know it's always the same, it's described the same in the docs. The main difference in the new API afaict is the options and the asyncronicity.

if (!opened) {
_state = STPRedirectContextStateNotStarted;
self.state = STPRedirectContextStateNotStarted;
[self unsubscribeFromNotifications];
}
onCompletion(opened);
Expand Down Expand Up @@ -141,15 +142,15 @@ - (void)startSafariViewControllerRedirectFlowFromViewController:(UIViewControlle

- (void)startSafariAppRedirectFlow {
if (self.state == STPRedirectContextStateNotStarted) {
_state = STPRedirectContextStateInProgress;
self.state = STPRedirectContextStateInProgress;
[self subscribeToUrlAndForegroundNotifications];
[[UIApplication sharedApplication] openURL:self.source.redirect.url];
}
}

- (void)cancel {
if (self.state == STPRedirectContextStateInProgress) {
_state = STPRedirectContextStateCancelled;
self.state = STPRedirectContextStateCancelled;
[self unsubscribeFromNotificationsAndDismissPresentedViewControllers];
}
}
Expand Down Expand Up @@ -196,7 +197,7 @@ - (void)handleRedirectCompletionWithError:(nullable NSError *)error
return;
}

_state = STPRedirectContextStateCompleted;
self.state = STPRedirectContextStateCompleted;

[self unsubscribeFromNotifications];

Expand Down
2 changes: 1 addition & 1 deletion Tests/Tests/STPFixtures.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
+ (STPSource *)alipaySource;

/**
A Source object with type Alipay
A Source object with type Alipay and a native redirect url
*/
+ (STPSource *)alipaySourceWithNativeUrl;

Expand Down
14 changes: 9 additions & 5 deletions Tests/Tests/STPRedirectContextTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,7 @@ - (void)testSafariAppRedirectFlow_noNotification {
*/
- (void)testNativeRedirectSupportingSourceFlow_validNativeURL {
STPSource *source = [STPFixtures alipaySourceWithNativeUrl];
NSURL *sourceURL = [NSURL URLWithString:source.details[@"native_url"]];

STPRedirectContext *context = [[STPRedirectContext alloc] initWithSource:source
completion:^(__unused NSString *sourceID, __unused NSString *clientSecret, __unused NSError *error) {
Expand All @@ -297,15 +298,18 @@ - (void)testNativeRedirectSupportingSourceFlow_validNativeURL {

id applicationMock = OCMClassMock([UIApplication class]);
OCMStub([applicationMock sharedApplication]).andReturn(applicationMock);
OCMStub([applicationMock openURL:[OCMArg any]
options:[OCMArg any]
completionHandler:([OCMArg invokeBlockWithArgs:@YES, nil])]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice 👍

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sweet!!


id mockVC = OCMClassMock([UIViewController class]);
[context startRedirectFlowFromViewController:mockVC];

OCMReject([sut startSafariViewControllerRedirectFlowFromViewController:[OCMArg any]]);
OCMReject([sut startSafariAppRedirectFlow]);
OCMVerify([applicationMock openURL:[OCMArg any]
options:[OCMArg any]
completionHandler:[OCMArg any]]);
OCMVerify([applicationMock openURL:[OCMArg isEqual:sourceURL]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can just write sourceURL instead of [OCMArg isEqual:sourceURL] in the verify calls? Not completely sure.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah it wasn't clear to me if they were the same. I thought maybe one way did == sourceURL and one way did isEqual:sourceURL? But I'm not sure.

options:[OCMArg isEqual:@{}]
completionHandler:[OCMArg isNotNil]]);
}

/**
Expand All @@ -327,13 +331,13 @@ - (void)testNativeRedirectSupportingSourceFlow_invalidNativeURL {
id mockVC = OCMClassMock([UIViewController class]);
[context startRedirectFlowFromViewController:mockVC];

OCMVerify([sut startSafariViewControllerRedirectFlowFromViewController:[OCMArg any]]);
OCMVerify([sut startSafariViewControllerRedirectFlowFromViewController:[OCMArg isEqual:mockVC]]);
OCMReject([applicationMock openURL:[OCMArg any]
options:[OCMArg any]
completionHandler:[OCMArg any]]);
OCMVerify([mockVC presentViewController:[OCMArg isKindOfClass:[SFSafariViewController class]]
animated:YES
completion:[OCMArg any]]);
completion:[OCMArg isNil]]);
}

@end