3

在 SwiftUI 视图中进行身份验证工作时遇到了一些麻烦。我正在使用 ASWebAuthentication ,每当我运行时都会出现错误:

如果不提供演示上下文,则无法启动 ASWebAuthenticationSession。在调用 -start 之前设置presentationContextProvider。

我正在创建一个 ViewController 并基于此堆栈溢出帖子传递对 Scene Delegate 窗口的引用,但该答案似乎对我不起作用。我也发现了这个 reddit 帖子,但我有点不清楚他们是如何在设置场景委托的窗口之前用窗口初始化视图的。

这是我用于 SwiftUI 视图的代码:

import SwiftUI
import AuthenticationServices

struct Spotify: View {
  var body: some View {
    Button(action: {
        self.authWithSpotify()
    }) {
        Text("Authorize Spotify")
    }
  }

  func authWithSpotify() {

    let authUrlString = "https://accounts.spotify.com/authorize?client_id=\(spotifyID)&response_type=code&redirect_uri=http://redirectexample.com/callback&scope=user-read-private%20user-read-email"
    guard let url = URL(string: authUrlString) else { return }

    let session = ASWebAuthenticationSession(
        url: url,
        callbackURLScheme: "http://redirectexample.com/callback",
        completionHandler: { callback, error in

            guard error == nil, let success = callback else { return }

            let code = NSURLComponents(string: (success.absoluteString))?.queryItems?.filter({ $0.name == "code" }).first

            self.getSpotifyAuthToken(code)
    })

    session.presentationContextProvider = ShimViewController()
    session.start()
  }

  func getSpotifyAuthToken(_ code: URLQueryItem?) {
    // Get Token
  }

}

struct Spotify_Previews: PreviewProvider {
  static var previews: some View {
    Spotify()
  }
}

class ShimViewController: UIViewController, ASWebAuthenticationPresentationContextProviding {
  func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
    return globalPresentationAnchor ?? ASPresentationAnchor()
  }
}

在 SceneDelegate 中:

var globalPresentationAnchor: ASPresentationAnchor? = nil

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

var window: UIWindow?

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
    // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
    // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).

    // Use a UIHostingController as window root view controller
    if let windowScene = scene as? UIWindowScene {
        let window = UIWindow(windowScene: windowScene)
        window.rootViewController = UIHostingController(rootView: Spotify())
        self.window = window
        window.makeKeyAndVisible()
    }

    globalPresentationAnchor =  window
}

知道我怎样才能完成这项工作吗?

4

4 回答 4

2

在实施ASWebAuthenticationSession. 我没有意识到的一件事是,您必须对会话变量有一个强引用。所以我会让你session改变你班级的一个属性,看看是否能解决这个问题。我的意思的简短片段:

// initialize as a property of the class
var session: ASWebAuthenticationSession?

func authWithSpotify() {
    let authUrlString = "https://accounts.spotify.com/authorize?client_id=\(spotifyID)&response_type=code&redirect_uri=http://redirectexample.com/callback&scope=user-read-private%20user-read-email"
    guard let url = URL(string: authUrlString) else { return }

    // assign session here
    session = ASWebAuthenticationSession(url: url, callbackURLScheme: "http://redirectexample.com/callback", completionHandler: { callback, error in

            guard error == nil, let success = callback else { return }

            let code = NSURLComponents(string: (success.absoluteString))?.queryItems?.filter({ $0.name == "code" }).first

            self.getSpotifyAuthToken(code)
    })

    session.presentationContextProvider = ShimViewController()
    session.start()
}
于 2020-02-12T00:40:50.723 回答
2

关于reddit帖子,我让它按原样工作。我的误解是 AuthView 没有被用作“界面”视图。我为我的身份验证视图创建了一个普通的 SwiftUI 视图,并且我有一个按钮,其中包含创建 AuthView 实例并调用处理会话的函数的操作。我将 globalPositionAnchor 存储在 @EnvironmentObject 中,但您也应该能够从全局变量中使用它。希望这可以帮助!

struct SignedOutView: View {
    @EnvironmentObject var contentManager: ContentManager

    var body: some View {
        VStack {
            Text("Title")
            .font(.largeTitle)

            Spacer()

            Button(action: {AuthProviderView(window: self.contentManager.globalPresentationAnchor!).signIn()}) {
                Text("Sign In")
                    .padding()
                    .foregroundColor(.white)
                    .background(Color.orange)
                    .cornerRadius(CGFloat(5))
                    .font(.headline)
            }.padding()
        }
    }
}
于 2020-02-21T05:17:20.110 回答
1

Ronni - 我遇到了同样的问题,但最终让 ShimController() 工作并避免了警告。我陷入了解决方案,但忘记实例化类。在下面查找我的“<<”评论。现在身份验证正在工作,并且回调正在像发条一样触发。这里唯一需要注意的是我正在授权其他东西 - 而不是 Spotify。

var session: ASWebAuthenticationSession?
var shimController = ShimViewController() // << instantiate your object here

func authWithSpotify() {
    let authUrlString = "https://accounts.spotify.com/authorize?client_id=\(spotifyID)&response_type=code&redirect_uri=http://redirectexample.com/callback&scope=user-read-private%20user-read-email"
    guard let url = URL(string: authUrlString) else { return }

    // assign session here
    session = ASWebAuthenticationSession(url: url, callbackURLScheme: "http://redirectexample.com/callback", completionHandler: { callback, error in

            guard error == nil, let success = callback else { return }

            let code = NSURLComponents(string: (success.absoluteString))?.queryItems?.filter({ $0.name == "code" }).first

            self.getSpotifyAuthToken(code)
    })

    session.presentationContextProvider = shimController // << then reference it here
    session.start()
}
于 2020-12-27T02:09:14.610 回答
0

在BetterSafariView中使用.webAuthenticationSession(isPresented:content)修饰符,您可以轻松地在 SwiftUI 中启动 Web 身份验证会话。它不需要钩子。SceneDelegate

import SwiftUI
import BetterSafariView

struct SpotifyLoginView: View {
    
    @State private var showingSession = false
    
    var body: some View {
        Button("Authorize Spotify") {
            self.showingSession = true
        }
        .webAuthenticationSession(isPresented: $showingSession) {
            WebAuthenticationSession(
                url: URL(string: "https://accounts.spotify.com/authorize")!,
                callbackURLScheme: "myapp"
            ) { callbackURL, error in
                // Handle callbackURL
            }
        }
    }
}
于 2020-08-16T13:05:58.127 回答