[Bug][TwitterKit][iOS] - SFSafariViewController login dismisses my view controller

ios

#1

Hey,

I’m experiencing an issue where i’m using the TwitterKit SDK to sign in via the .WebBased or .WebBasedForceLogin methods however when the logInWithViewController(viewController: UIViewController?, methods: TWTRLoginMethod, completion: TWTRLogInCompletion) method completes, it seems to dismiss my own view controllers that are presented too.

I’m using TwitterKit 2.2.0 installed via Cocoapods (no fabric stuff).

Here is the view controller hierarchy prior to interacting with the TwitterSDK for login:

(lldb) expr -l objc++ -O -- [[[UIWindow keyWindow] rootViewController] _printHierarchy]

<RedactedTabBarController 0x7a9bb600>
   | <UINavigationController 0x7a9ed400>
   |    | <RedactedViewController 0x7a9e3200>
   + <RedactedLoginNavigationController 0x7b96d800>
   |    | <Redacted.LoginMethodSelectionViewController 0x7c061500>

I then call the following from within LoginMethodSelectionViewController:

Twitter.sharedInstance().logInWithViewController(self, methods: .WebBased) { session, error in            
}

Then, by the time the callback completes, both the SFSafariViewController and my presented RedactedLoginNavigationController are dismissed however this is not desired.

I put a symbolic breakpoint on [UIViewController dismissViewControllerAnimated:completion:] to try and work out why both view controllers were being dismissed and it looks like the SDK calls this method on two separate occasions:

The first time looks to be a result of me calling -[Twitter application:openURL:options:] from my UIApplicationDelegete, it looks like this is what dismisses the SFSafariViewController and it’s called prior to the callback… Here is the stack trace:

(lldb) bt
* thread #1: tid = 0x1235167, 0x03759142 UIKit`-[UIViewController dismissViewControllerAnimated:completion:], queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
  * frame #0: 0x03759142 UIKit`-[UIViewController dismissViewControllerAnimated:completion:]
    frame #1: 0x004eda5b RedactedApp`__68-[TWTRWebAuthenticationFlow presentWebAuthenticationViewController:]_block_invoke.64 + 55
    frame #2: 0x004ed472 RedactedApp`-[TWTRWebAuthenticationFlow resumeAuthenticationWithRedirectURL:] + 181
    frame #3: 0x005226e7 RedactedApp`-[Twitter application:openURL:options:] + 81
    frame #4: 0x0024de45 RedactedApp`-[AppDelegate application:openURL:options:](self=0x7a774040, _cmd="application:openURL:options:", app=0x7a66d3f0, url=@"twitterkit-zloflngcluv2m5snzxr6neghs://callback?app=ZLoFLNGCLUv2m5SNZxr6NEgHs&oauth_token=T5BFRwAAAAAAvM8TAAABVUE4JnA&oauth_verifier=CLMbokmg645Uy0JrAHGe2SNAerr903WC", options=2 key/value pairs) + 213 at AppDelegate.m:441
    frame #5: 0x03578aa4 UIKit`__45-[UIApplication _applicationOpenURL:payload:]_block_invoke + 983
    frame #6: 0x0357846f UIKit`-[UIApplication _applicationOpenURL:payload:] + 787
    frame #7: 0x03589633 UIKit`-[UIApplication _handleNonLaunchSpecificActions:forScene:withTransitionContext:completion:] + 5408
    frame #8: 0x0358f92f UIKit`__88-[UIApplication _handleApplicationLifecycleEventWithScene:transitionContext:completion:]_block_invoke + 238
    frame #9: 0x0358f7d9 UIKit`-[UIApplication _handleApplicationLifecycleEventWithScene:transitionContext:completion:] + 469
    frame #10: 0x0356a067 UIKit`__70-[UIApplication scene:didUpdateWithDiff:transitionContext:completion:]_block_invoke + 197
    frame #11: 0x03569c57 UIKit`-[UIApplication scene:didUpdateWithDiff:transitionContext:completion:] + 770
    frame #12: 0x08735177 FrontBoardServices`__80-[FBSSceneImpl updater:didUpdateSettings:withDiff:transitionContext:completion:]_block_invoke_2 + 71
    frame #13: 0x08734aae FrontBoardServices`__40-[FBSSceneImpl _performDelegateCallOut:]_block_invoke + 54
    frame #14: 0x0875827f FrontBoardServices`__FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK__ + 23
    frame #15: 0x087580ab FrontBoardServices`-[FBSSerialQueue _performNext] + 174
    frame #16: 0x087584fa FrontBoardServices`-[FBSSerialQueue _performNextFromRunLoopSource] + 52
    frame #17: 0x087577e8 FrontBoardServices`FBSSerialQueueRunLoopSourceHandler + 33
    frame #18: 0x05be8e5f CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 15
    frame #19: 0x05bdeaeb CoreFoundation`__CFRunLoopDoSources0 + 523
    frame #20: 0x05bddf08 CoreFoundation`__CFRunLoopRun + 1032
    frame #21: 0x05bdd846 CoreFoundation`CFRunLoopRunSpecific + 470
    frame #22: 0x05bdd65b CoreFoundation`CFRunLoopRunInMode + 123
    frame #23: 0x0913d664 GraphicsServices`GSEventRunModal + 192
    frame #24: 0x0913d4a1 GraphicsServices`GSEventRun + 104
    frame #25: 0x0356ceb9 UIKit`UIApplicationMain + 160
    frame #26: 0x00169c0a RedactedApp`main(argc=1, argv=0xbff9a8a8) + 138 at main.m:16
    frame #27: 0x06adfa25 libdyld.dylib`start + 1

The second time is actually called after the login callback is executed but I don’t think it should be? Anyway, here is the stack trace:

(lldb) bt
* thread #1: tid = 0x1235167, 0x03759142 UIKit`-[UIViewController dismissViewControllerAnimated:completion:], queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
  * frame #0: 0x03759142 UIKit`-[UIViewController dismissViewControllerAnimated:completion:]
    frame #1: 0x0052258e RedactedApp`__56-[Twitter performWebBasedLogin:forcingLogin:completion:]_block_invoke.269 + 81
    frame #2: 0x004edcd3 RedactedApp`-[TWTRWebAuthenticationFlow succeedWithSession:] + 114
    frame #3: 0x004edb91 RedactedApp`__41-[TWTRWebAuthenticationFlow saveSession:]_block_invoke + 71
    frame #4: 0x004bdfe8 RedactedApp`__60-[TWTRSessionStore saveSession:withVerification:completion:]_block_invoke_2 + 25
    frame #5: 0x02feaef2 ReactiveCocoa`__RACBacktraceBlock_block_invoke(.block_descriptor=0x7c6a4270) + 98 at RACBacktrace.m:67
    frame #6: 0x06a91363 libdispatch.dylib`_dispatch_call_block_and_release + 15
    frame #7: 0x06ab49cd libdispatch.dylib`_dispatch_client_callout + 14
    frame #8: 0x06a99f7c libdispatch.dylib`_dispatch_main_queue_callback_4CF + 910
    frame #9: 0x05c201be CoreFoundation`__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 14
    frame #10: 0x05bde434 CoreFoundation`__CFRunLoopRun + 2356
    frame #11: 0x05bdd846 CoreFoundation`CFRunLoopRunSpecific + 470
    frame #12: 0x05bdd65b CoreFoundation`CFRunLoopRunInMode + 123
    frame #13: 0x0913d664 GraphicsServices`GSEventRunModal + 192
    frame #14: 0x0913d4a1 GraphicsServices`GSEventRun + 104
    frame #15: 0x0356ceb9 UIKit`UIApplicationMain + 160
    frame #16: 0x00169c0a RedactedApp`main(argc=1, argv=0xbff9a8a8) + 138 at main.m:16
    frame #17: 0x06adfa25 libdyld.dylib`start + 1

I can’t find anything in the documentation to handle this scenario so it would be great if you could help me out.

Update: looks like this only happens in iOS 9 where SFSafariViewController is used. I assume this is because both the AppDelegate callback handling is dismissing the view controller and so is the logic within -[Twitter performWebBasedLogin:forcingLogin:completion:] where it is not required if SFSafariViewController is used.

Update2: rolling back to 2.1.1 fixes this issue as it doesn’t contain SFSafariViewController support.

Thanks,
Liam


TwitterKit is dismissing callee controller in airplane mode
#2

Thanks for the very detailed bug report @liamnichols. I’m looking into this with the team.


#3

Thanks @bonnell. I’ve also noticed that if an error occurs within the SDK prior to it presenting a view controller, it will also dismiss my view controller incorrectly.

In my test scenario I was using a proxy to monitor web traffic that caused one of the API calls within your SDK to fail because of invalid SSL certs (NSURLError -999 cancelled or something like that). The sign in view controller never got presented however the SDK still called dismiss view controller on one of my view controllers.

Let me know if you want me to grab the exact stack trace leading up to the dismiss as well.


#4

Hey @liamnichols if you’d be willing to send over the stack trace, that would be wonderful!


#5

Here you go @bonnell! This one is also called after completion block is called:

(lldb) bt
* thread #1: tid = 0x200c7fd, 0x0384b142 UIKit`-[UIViewController dismissViewControllerAnimated:completion:], queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
  * frame #0: 0x0384b142 UIKit`-[UIViewController dismissViewControllerAnimated:completion:]
    frame #1: 0x0053e508 RedactedApp`__43-[Twitter performWebBasedLogin:completion:]_block_invoke265 + 96
    frame #2: 0x0050b3c9 RedactedApp`-[TWTRWebAuthenticationFlow failWithError:] + 115
    frame #3: 0x0050af49 RedactedApp`__56-[TWTRWebAuthenticationFlow requestAuthenticationToken:]_block_invoke + 78
    frame #4: 0x004f6f6e RedactedApp`-[TWTRWebAuthenticationTokenRequestor handleRequestTokenResponse:error:completion:] + 185
    frame #5: 0x004f6e47 RedactedApp`__66-[TWTRWebAuthenticationTokenRequestor requestAuthenticationToken:]_block_invoke + 70
    frame #6: 0x004c7ce1 RedactedApp`__56-[TwitterNetworking sendAsynchronousRequest:completion:]_block_invoke_2 + 40
    frame #7: 0x030dcef2 ReactiveCocoa`__RACBacktraceBlock_block_invoke(.block_descriptor=0x7af6d660) + 98 at RACBacktrace.m:67
    frame #8: 0x06b83363 libdispatch.dylib`_dispatch_call_block_and_release + 15
    frame #9: 0x06ba69cd libdispatch.dylib`_dispatch_client_callout + 14
    frame #10: 0x06b8bf7c libdispatch.dylib`_dispatch_main_queue_callback_4CF + 910
    frame #11: 0x05d121be CoreFoundation`__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 14
    frame #12: 0x05cd0434 CoreFoundation`__CFRunLoopRun + 2356
    frame #13: 0x05ccf846 CoreFoundation`CFRunLoopRunSpecific + 470
    frame #14: 0x05ccf65b CoreFoundation`CFRunLoopRunInMode + 123
    frame #15: 0x0922f664 GraphicsServices`GSEventRunModal + 192
    frame #16: 0x0922f4a1 GraphicsServices`GSEventRun + 104
    frame #17: 0x0365eeb9 UIKit`UIApplicationMain + 160
    frame #18: 0x0011088a RedactedApp`main(argc=1, argv=0xbffee8a8) + 138 at main.m:16
    frame #19: 0x06bd1a25 libdyld.dylib`start + 1

Also, I believe it is directly related to this: [Bug] TWTRLoginMethod.WebBased Login causes unexpected ViewController dismiss when network is unconnectable

And on a side note, I spotted this closed topic: [Bug][TwitterKit][iOS] - Completion handler is being called too early
I also face some similar issues where the dismissal of any view controller presented by the twitter SDK interferes with us presenting another view controller (such as UIAlertController) from within the completion block (as the SDK’s view controller is dismissing at the same time).

Other SDKs like Facebook and Google wait for the any view controllers that are presented internally to be dismissed before they call the completion block.

Thanks for the prompt responses though!


#6

I am also experiencing this and cannot use the SFSafariViewController integration until this is fixed. @bonnell Is there any ETA on when this issue will be fixed?


#7

Hey @PatrickDPierson,

I don’t have an ETA yet, but the team is definitely aware of the issue.

-Mike


#8

I am looking same as this issue. This is very critical…
I want to fix quickly.


#9

Thanks for expressing interest in the fix @jinthreek89!


#10

I’m really waiting for this fix, is there an approximate ETA yet? Thanks.

  • Leo

#11

The work is being taken on so this should be fixed soon.


#12

The fix for this has now been released - upgrade to 2.4.0 for the fix.

Specifically - “Fix issue where completion block would be called too early when returning from a SFSafariViewController login redirect.”

-Mike


#13

I’ve installed 2.4.0 today and it all looks good from my end :slight_smile:

Added bonus in that it fixes [Bug][TwitterKit][iOS] - Completion handler is being called too early too!

Thanks,
Liam


#14

I’have installed 2.4.0 today,but it doesn’t work, why??


#15

Hey @LiLixinsheng,

I’d try doing a clean of your project and then a fresh build to ensure that you’re building with the latest version of the SDK. Also, Twitter Kit 2.5.0 has been released, so you may want to update to that version.

-Mike