Можно ли использовать ASWebAuthenticationSession из SwiftUI?

Я хотел бы пройти аутентификацию по OAuth API, используяASWebAuthenticationSessionоднако это не похоже на использование SwiftUI. Вот что хотелось бы иметь:

struct ContentView: View: ASWebAuthenticationPresentationContextProviding {
    var body: some View {
        NavigationView {
            VStack {
                Button("Hello World", {
                    // Run oauth flow
                }
            }
        }
        .navigationBarTitle(Text("Greed of Savin"))
        .navigationViewStyle(StackNavigationViewStyle())
    }

    func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
        return BungieApi.sharedInstance.presentationAnchor ?? ASPresentationAnchor()
    }
}

}

Требуется принятие протокола ASWebAuthenticationPresentationContextProviding который несовместим с представлениями SwiftUI.

Я могу обойти это, перенаправив ViewController, который затем может предоставить ASWebAuthenticationPresentationContextProviding, но это добавляет дополнительный шаг просмотра / навигации.

Есть ли способ использовать ASWebAuthenticationSession из SwiftUI, не переходя в ViewController?

2 ответа

Решение

Я решил это в трех частях:

Сначала сделайте снимок окна в глобальном объекте во время настройки в SceneDelegate.swift:

var globalPresentationAnchor: ASPresentationAnchor? = nil
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        // ...            
        globalPresentationAnchor = window
    }
}

Затем создайте небольшой ViewController, чтобы предоставить этот объект окна для использования ASWebAuthenticationSession:

class ShimViewController: UIViewController, ASWebAuthenticationPresentationContextProviding
{
    func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
        // Perhaps I don't need the window object at all, and can just use:
        // return ASPresentationAnchor()
        return globalPresentationAnchor ?? ASPresentationAnchor()
    }
}

Наконец, вызовите API аутентификации, предоставив ShimViewController в качестве докладчика.

    let session = ASWebAuthenticationSession(/**/)
    session.presentationContextProvider = ShimViewController()
    session.start()

Используя BetterSafariView, можно использоватьASWebAuthenticationSession без крючка SceneDelegate в SwiftUI.

import SwiftUI
import BetterSafariView

struct ContentView: View {
    
    @State private var startingWebAuthenticationSession = false
    
    var body: some View {
        Button("Start WebAuthenticationSession") {
            self.startingWebAuthenticationSession = true
        }
        .webAuthenticationSession(isPresented: $startingWebAuthenticationSession) {
            WebAuthenticationSession(
                url: URL(string: "https://github.com/login/oauth/authorize?client_id=\(clientID)")!,
                callbackURLScheme: "myapp"
            ) { callbackURL, error in
                print(callbackURL, error)
            }
        }
    }
}
Другие вопросы по тегам