You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI中如何获取WKWebView返回的Spotify授权响应?

解决方案:拦截授权回调并解析Spotify Token

你的问题核心是未拦截授权完成后的跳转URL并解析其中的令牌信息。Spotify隐式授权(response_type=token)完成后,会跳转到你指定的redirect_uri,并在URL的fragment部分携带access_token等授权信息。下面是修改后的完整代码及关键说明:

修改后的AuthView代码

import SwiftUI
import WebKit

struct AuthView: UIViewRepresentable {
    // 通过Binding将获取到的Token传递回父视图
    @Binding var accessToken: String?
    private let webView: WKWebView
    private let authURL = "https://accounts.spotify.com/en/authorize?client_id=...&redirect_uri=https://pie-test-login/callback&response_type=token"
    
    init(accessToken: Binding<String?>) {
        self._accessToken = accessToken
        self.webView = WKWebView(frame: .zero)
        // 加载授权页面(增加URL有效性判断)
        if let url = URL(string: authURL) {
            webView.load(URLRequest(url: url))
        }
    }
    
    // 创建协调器,处理WKWebView导航事件
    func makeCoordinator() -> Coordinator {
        Coordinator(parent: self)
    }
    
    func makeUIView(context: Context) -> WKWebView {
        // 将导航代理绑定到协调器
        webView.navigationDelegate = context.coordinator
        return webView
    }
    
    func updateUIView(_ uiView: WKWebView, context: Context) {}
    
    // 协调器类:实现WKNavigationDelegate拦截跳转
    class Coordinator: NSObject, WKNavigationDelegate {
        let parent: AuthView
        
        init(parent: AuthView) {
            self.parent = parent
        }
        
        func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
            guard let url = navigationAction.request.url else {
                decisionHandler(.allow)
                return
            }
            
            // 匹配授权回调URL
            if url.absoluteString.starts(with: "https://pie-test-login/callback") {
                // 解析URL的fragment部分提取Token
                if let fragment = url.fragment {
                    let components = fragment.components(separatedBy: "&")
                    for component in components {
                        let keyValue = component.components(separatedBy: "=")
                        if keyValue.count == 2, keyValue[0] == "access_token" {
                            parent.accessToken = keyValue[1]
                            break
                        }
                    }
                }
                // 停止跳转,已完成Token获取
                decisionHandler(.cancel)
                return
            }
            
            // 允许其他正常页面跳转
            decisionHandler(.allow)
        }
    }
}

struct AuthView_Previews: PreviewProvider {
    static var previews: some View {
        AuthView(accessToken: .constant(nil))
    }
}

关键逻辑说明

  • @Binding变量:accessToken用于将获取到的令牌传递给父视图,方便后续调用Spotify API。
  • Coordinator协调器:作为WKWebView的导航代理,拦截所有页面跳转请求,判断是否为授权回调。
  • Fragment解析:隐式授权的Token不会出现在URL的query参数中,而是在#后的fragment部分,需要拆分字符串提取access_token字段。

父视图使用示例

struct ContentView: View {
    @State private var accessToken: String?
    @State private var showAuthView = true
    
    var body: some View {
        Group {
            if let token = accessToken {
                Text("已获取Token: \(token)")
                // 此处可跳转至热门歌曲列表视图,传入Token
            } else if showAuthView {
                AuthView(accessToken: $accessToken)
            }
        }
    }
}

额外建议

  1. 错误处理:授权失败时,fragment会包含error参数,建议添加对应判断逻辑,提示用户授权失败原因。
  2. 配置解耦:不要硬编码client_id和redirect_uri,可将其存入Info.plist或单独配置文件,提升安全性和可维护性。
  3. 授权流程选择:隐式授权无refresh_token,若需长期授权,建议改用授权码流程(response_type=code),但需要后端配合完成Token交换。

内容的提问来源于stack exchange,提问作者ezhovich

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 16:05:23