swift - que - sfsafariviewcontroller example
SafariViewController: ¿Cómo obtener el token OAuth de la URL? (2)
Tratando de usar Facebook OAuth con el SafariViewController. Primero abro el authURL con SafariViewController, que si el usuario ha iniciado sesión en Facebook en Safari, los redireccionará y devolverá una URL de OAuth con el token para ese servicio específico, por ejemplo, Instagram.
Cuando SafariViewController se haya redirigido, quiero capturar la URL de respuesta y almacenarla para poder agarrar el token. Aquí está mi código:
import SafariServices
let kSafariViewControllerCloseNotification = "kSafariViewControllerCloseNotification"
import UIKit
// facebook OAuth URL for service
let authURL = NSURL(string: "https://www.facebook.com/dialog/oauth?client_id=3627644767&redirect_uri=https://www.facebook.com/connect/login_success.html&scope=basic_info,email,public_profile,user_about_me,user_activities,user_birthday,user_education_history,user_friends,user_interests,user_likes,user_location,user_photos,user_relationship_details&response_type=token")
class ViewController: UIViewController, SFSafariViewControllerDelegate {
var safariVC: SFSafariViewController?
@IBOutlet weak var loginButton: UIButton!
@IBAction func loginButtonTapped(sender: UIButton) {
safariVC = SFSafariViewController(URL: authURL!)
safariVC!.delegate = self
self.presentViewController(safariVC!, animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
// not firing the safariLogin function below
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(ViewController.safariLogin(_:)), name: kSafariViewControllerCloseNotification, object: nil)
}
func safariLogin(notification: NSNotification) {
print("Safari Login call")
// get the url form the auth callback
let url = notification.object as! NSURL
print(url)
self.safariVC!.dismissViewControllerAnimated(true, completion: nil)
}
func application(application: UIApplication, openURL url: NSURL, sourceApplication: String?, annotation: AnyObject) -> Bool {
print("application call")
// just making sure we send the notification when the URL is opened in SFSafariViewController
if (sourceApplication == "com.application.SafariViewTest") {
NSNotificationCenter.defaultCenter().postNotificationName(kSafariViewControllerCloseNotification, object: url)
return true
}
return true
}
}
Abre el authURL y redirige a la URL de respuesta correcta, pero el observador no activa la función safariLogin para capturar la URL. Cualquier ayuda sería muy apreciada!
¡Muchas gracias!
iOS 12
iOS 12 Beta ya resta SFAuthenticationSession (ver más abajo) a favor de ASWebAuthenticationSession . Parece que se usa exactamente de la misma manera pero requiere el nuevo marco de AuthenticationServices .
iOS 11
iOS 11 introdujo SFAuthenticationSession que es mucho más fácil de manejar. Dada su naturaleza, esta API beta aún puede cambiar, pero ya hay un par de ejemplos ( 1 , 2 ) en Internet. Primero, necesita un controlador de finalización que se llame con el resultado de la solicitud de autenticación:
let completion : SFAuthenticationSession.CompletionHandler = { (callBack:URL?, error:Error?) in
guard error == nil, let successURL = callBack else {
return
}
let oauthToken = NSURLComponents(string: (successURL.absoluteString))?.queryItems?.filter({$0.name == "oauth_token"}).first
// Do what you have to do...
}
Luego simplemente crea una SFAuthenticationSession y lo inicia.
let authURL = "https://the.service.you/want/toAuthorizeWith?..."
let scheme = "YOURSCHEME://"
let authSession = SFAuthenticationSession(url: authURL, callbackURLScheme: scheme, completionHandler: completion)
authSession.start()
iOS 10 y antes
Como algunos han señalado en los comentarios, la respuesta aceptada es incompleta y no funcionará por sí sola. Al pasear por el blog de Strawberry Code, puede encontrar un enlace al proyecto relacionado de GitHub . El README.MD
ese proyecto explica una parte crucial de la configuración, a saber, agregar el URI de redireccionamiento a Info.plist
. Así que todo se reduce así:
AppDelegate.swift
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
if let sourceApplication = options[.sourceApplication] {
if (String(describing: sourceApplication) == "com.apple.SafariViewService") {
NotificationCenter.default.post(name: Notification.Name("CallbackNotification"), object: url)
return true
}
}
return false
}
ViewController.swift
Regístrese para recibir la notificación de llamar a su manejador en algún lugar sensible. Recomiendo no hacerlo en viewDidLoad()
pero solo antes de que realmente estés presentando el SFSafariViewController
.
NotificationCenter.default.addObserver(self, selector: #selector(safariLogin(_:)), name: Notification.Name("CallbackNotification"), object: nil)
let safariVC = SFSafariViewController(URL: authURL)
safariVC.delegate = self
self.present(safariVC, animated: true, completion: nil)
Y luego quitar la observancia en el controlador:
@objc func safariLogin(_ notification : Notification) {
NotificationCenter.default.removeObserver(self, name: Notification.Name("CallbackNotification"), object: nil)
guard let url = notification.object as? URL else {
return
}
// Parse url ...
}
Recuerde que el usuario puede descartar el SFSafariViewController
tocando el botón Done
, así que asegúrese de adoptar el protocolo SFSafariViewControllerDelegate
y elimine la observación de esta manera también:
func safariViewControllerDidFinish(_ controller: SFSafariViewController) {
NotificationCenter.default.removeObserver(self, name: Notification.Name("CallbackNotification"), object: nil)
}
Info.plist
Para que todo funcione, debe agregar su esquema de redirección de URI a Info.plist
:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>com.YOUR.BUNDLE.IDENTIFIER</string>
<key>CFBundleURLSchemes</key>
<array>
<string>YOURSCHEME</string>
</array>
</dict>
</array>
Por supuesto, YOURSCHEME tiene que coincidir con el esquema de URI de redireccionamiento que registró con el servicio web con el que está tratando de autorizar.
Lo averigué. Algunos de los métodos fueron pre iOS 9 y ahora están en desuso. También tuve la función de application
en el ViewController que creé cuando debería haberse definido en el AppDelagate.swift
. Por ejemplo
Añadido al final de AppDelegate.swift
func application(app: UIApplication, openURL url: NSURL, options: [String : AnyObject]) -> Bool {
print("app: /(app)")
// print OAuth response URL
print("url: /(url)")
print("options: /(options)")
if let sourceApplication = options["UIApplicationOpenURLOptionsSourceApplicationKey"] {
if (String(sourceApplication) == "com.testApp.Incognito") {
NSNotificationCenter.defaultCenter().postNotificationName(kSafariViewControllerCloseNotification, object: url)
return true
}
}
return true
}
ViewController.swift
import SafariServices
let kSafariViewControllerCloseNotification = "kSafariViewControllerCloseNotification"
// facebook OAuth URL
let authURL = NSURL(string: "https://www.facebook.com/dialog/oauth?client_id=3627644767&redirect_uri=https://www.facebook.com/connect/login_success.html&scope=basic_info,email,public_profile,user_about_me,user_activities,user_birthday,user_education_history,user_friends,user_interests,user_likes,user_location,user_photos,user_relationship_details&response_type=token")
class ViewController: UIViewController, SFSafariViewControllerDelegate {
var safariVC: SFSafariViewController?
@IBOutlet weak var loginButton: UIButton!
@IBAction func loginButtonTapped(sender: AnyObject) {
safariVC = SFSafariViewController(URL: authURL!)
safariVC!.delegate = self
self.presentViewController(safariVC!, animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(ViewController.safariLogin(_:)), name: kSafariViewControllerCloseNotification, object: nil)
}
func safariLogin(notification: NSNotification) {
// get the url from the auth callback
let url = notification.object as! NSURL
// Finally dismiss the Safari View Controller with:
self.safariVC!.dismissViewControllerAnimated(true, completion: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func safariViewControllerDidFinish(controller: SFSafariViewController) {
controller.dismissViewControllerAnimated(true) { () -> Void in
print("You just dismissed the login view.")
}
}
func safariViewController(controller: SFSafariViewController, didCompleteInitialLoad didLoadSuccessfully: Bool) {
print("didLoadSuccessfully: /(didLoadSuccessfully)")
}
}