OAuth 2 flow does not work in iOS app running on MacOS
Description:
I've enabled macOS compatibility for my iOS app:
But the OAuth 2 flow does not work properly on macOS - after successful login it goes to:
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool { // handle authentication mechanism if url.host == "oauth-callback" { OAuthSwift.handle(url: url) } return true } It calls OAuthSwift.handle(url: url) but never gets to the completion handler (on iOS it all works fine).
Do I need to consider something to get it working on macOS or it is a bug?
OAuth Provider? (Twitter, Github, ..):
Custom
OAuth Version:
- [ ] Version 1
- [x] Version 2
OS (Please fill the version) :
- [x] iOS :
- [ ] OSX :
- [ ] TVOS :
- [ ] WatchOS :
Installation method:
- [ ] Carthage
- [x] CocoaPods
- [ ] Swift Package Manager
- [ ] Manually
Library version:
- [x] head
- [ ] v2.1.0
- [ ] v2.0.0
- [ ] v1.4.1
- [ ] other: (Please fill in the version you are using.)
Xcode version:
-
[x] 12.2
-
[ ] 11.4 (Swift 5.2)
-
[ ] 11.x (Swift 5.1)
-
[ ] 10.x (Swift 5.0)
-
[ ] other: (Please fill in the version you are using.)
-
[ ] objective c
Temporary fixed by replacing safariViewControllerDidFinish with:
public func safariViewControllerDidFinish(_ controller: SFSafariViewController) {
// "Done" pressed
#if !targetEnvironment(macCatalyst)
self.clearObservers()
#endif
self.delegate?.safariViewControllerDidFinish?(controller)
}
I have the same issue. Adding the above code suggested by MJRomka does allow the handler code to complete such that it returns to the completionHandler of my oauthSwift.authorize. However, in that completion I now get a retain error (success failure, error code -10). Please note that I do retain the instance OAuthSwift, and this same exact code works perfectly on iOS. This is a mac catalyst specific issue. xCode 12.2, Big Sur, Mac Mini/M1. The error occurs both when running native on apple silicon as well as Rosetta. It is also occurring with xCode 12.2, Catalyst, MacBook Pro (Intel).
Temporary fixed by replacing safariViewControllerDidFinish with:
public func safariViewControllerDidFinish(_ controller: SFSafariViewController) { // "Done" pressed #if !targetEnvironment(macCatalyst) self.clearObservers() #endif self.delegate?.safariViewControllerDidFinish?(controller) }
Because Catalyst does not use a popovercontroller, but instead sends the request directly to Safari. It seems that this function indeed gets triggered once the app opens Safari on Mac Catalyst. Therefore, it removes the Observers before the user can log in. This code does get rid of the problem. It is probably something Apple needs to fix, but in the meantime it might not be the worst idea to implement it like this.
Thank you Collin! I had already done this. BUT your response clued me in on the issue I was having down the road with the oauth instance not being retained. Basically on iOS, safariViewControllerDidFinish is only called when the Done button on the popover is tapped. In the case of an oauth flow it is a way to gracefully handle someone cancelling out of the auth process (by tapping the Done button) without completing authorization (In my flow, I release the now defunct oauth instance, pop up an alert letting the user know they haven't completed auth, and then show a view not dependent on the auth credentials). However, since Done is called on MacOS instantly (when I must say I agree it should NOT be called at that time), my graceful code is executed when it shouldn't be. I hope this helps the next person.
Temporary fixed by replacing safariViewControllerDidFinish with:
public func safariViewControllerDidFinish(_ controller: SFSafariViewController) { // "Done" pressed #if !targetEnvironment(macCatalyst) self.clearObservers() #endif self.delegate?.safariViewControllerDidFinish?(controller) }
it works for my Mac Catalyst app build. thank you.
edit: a better approach is to just swap out the authorizeURLHandler on Mac Catalyst:
var authorizeURLHandler: OAuthSwiftURLHandlerType?
// …
#if targetEnvironment(macCatalyst)
authorizeURLHandler = OAuthSwiftOpenURLExternally.sharedInstance
#endif
#if !targetEnvironment(macCatalyst)
if authorizeURLHandler == nil {
authorizeURLHandler = SafariURLHandler(
present: { [weak self] safariViewController, _ in
self?.present(safariViewController, animated: true, completion: nil)
},
dismiss: { safariViewController, urlHandler in
safariViewController.dismiss(animated: urlHandler.animated, completion: urlHandler.dismissCompletion)
},
oauthSwift: oauthswift
)
}
#endif
oauthswift.authorizeURLHandler = authorizeURLHandler