在 SwiftUI 中使用 ASWebAuthentication
Using ASWebAuthentication in SwiftUI
在 SwiftUI 视图中进行身份验证时遇到一些问题。我正在使用 ASWebAuthentication 并且每当我 运行 我得到一个错误:
Cannot start ASWebAuthenticationSession without providing presentation context. Set presentationContextProvider before calling -start.
我正在创建一个 ViewController 并根据 but that answer doesn’t seem to be working for me. I’ve also found this reddit post 传递对场景委托 window 的引用,但我不太清楚他们是如何做到的在设置场景委托的 window 之前使用 window 初始化视图。
这是我用于 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({ [=10=].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
}
知道如何让这项工作成功吗?
在实现 ASWebAuthenticationSession
之前,我曾 运行 做过类似的事情。我没有意识到的一件事是,您必须对会话变量有一个强引用。所以我会让你的 session
变量成为你的 class 的 属性,看看是否能解决问题。我的意思的一小段:
// 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({ [=10=].name == "code" }).first
self.getSpotifyAuthToken(code)
})
session.presentationContextProvider = ShimViewController()
session.start()
}
关于 reddit post,我让它按原样工作。我的误解是 AuthView 没有被用作 'interface' 视图。我为我的身份验证视图创建了一个普通的 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()
}
}
}
使用 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
}
}
}
}
罗尼 -
我 运行 遇到了同样的问题,但最终让 ShimController() 工作并避免了警告。我陷入了解决方案,但忘记实例化 class。在下面查找我的“<<”评论。现在身份验证正在工作并且回调正在像发条一样触发。唯一需要注意的是,我授权的是其他东西——不是 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({ [=10=].name == "code" }).first
self.getSpotifyAuthToken(code)
})
session.presentationContextProvider = shimController // << then reference it here
session.start()
}
在 SwiftUI 视图中进行身份验证时遇到一些问题。我正在使用 ASWebAuthentication 并且每当我 运行 我得到一个错误:
Cannot start ASWebAuthenticationSession without providing presentation context. Set presentationContextProvider before calling -start.
我正在创建一个 ViewController 并根据
这是我用于 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({ [=10=].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
}
知道如何让这项工作成功吗?
在实现 ASWebAuthenticationSession
之前,我曾 运行 做过类似的事情。我没有意识到的一件事是,您必须对会话变量有一个强引用。所以我会让你的 session
变量成为你的 class 的 属性,看看是否能解决问题。我的意思的一小段:
// 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({ [=10=].name == "code" }).first
self.getSpotifyAuthToken(code)
})
session.presentationContextProvider = ShimViewController()
session.start()
}
关于 reddit post,我让它按原样工作。我的误解是 AuthView 没有被用作 'interface' 视图。我为我的身份验证视图创建了一个普通的 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()
}
}
}
使用 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
}
}
}
}
罗尼 - 我 运行 遇到了同样的问题,但最终让 ShimController() 工作并避免了警告。我陷入了解决方案,但忘记实例化 class。在下面查找我的“<<”评论。现在身份验证正在工作并且回调正在像发条一样触发。唯一需要注意的是,我授权的是其他东西——不是 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({ [=10=].name == "code" }).first
self.getSpotifyAuthToken(code)
})
session.presentationContextProvider = shimController // << then reference it here
session.start()
}