我想到了。事实证明,在 iOS 上做 OAuth2 时,RedirectURI 不起作用!它总是返回不受支持的 URL。所以你要做的是创建一个自定义 URLProtocol 并在那里处理你的 redirectURI 。
//
// URLHandler.swift
// XIO
//
// Created by Brandon Anthony on 2018-10-01.
// Copyright © 2018 SO. All rights reserved.
//
import Foundation
class URLHandler: URLProtocol {
private static let requestIdentifier = "com.xio.uri.handled"
override class func canInit(with request: URLRequest) -> Bool {
guard request.url?.scheme == "myscheme" else {
return false
}
guard let handled = URLProtocol.property(forKey: URLHandler.requestIdentifier, in: request) as? Bool else {
return true
}
return !handled
}
override func startLoading() {
guard let request = (self.request as NSURLRequest).mutableCopy() as? NSMutableURLRequest else {
return
}
URLProtocol.setProperty(true, forKey: URLHandler.requestIdentifier, in: request)
DispatchQueue.global(qos: .background).async {
guard let url = request.url, let headers = request.allHTTPHeaderFields else {
self.client?.urlProtocol(self, didFailWithError: RuntimeError("URLHandler - Invalid URL."))
return
}
guard let response = HTTPURLResponse(url: url, statusCode: 200, httpVersion: "HTTP/1.1", headerFields: headers) else {
self.client?.urlProtocol(self, didFailWithError: RuntimeError("URLHandler - Invalid Response."))
return
}
let json: [String: Any] = ["key": "value"]
do {
let data = try JSONSerialization.data(withJSONObject: json, options: .prettyPrinted)
self.client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed)
self.client?.urlProtocol(self, didLoad: data as Data)
self.client?.urlProtocolDidFinishLoading(self)
} catch {
self.client?.urlProtocol(self, didFailWithError: error)
}
}
}
override func stopLoading() {
}
override class func canonicalRequest(for request: URLRequest) -> URLRequest {
return request
}
override class func requestIsCacheEquivalent(_ a: URLRequest, to b: URLRequest) -> Bool {
return super.requestIsCacheEquivalent(a, to: b)
}
}
或者,您可以等待调用失败,然后从NSError
对象中解析出响应。另一种解决方案是启动请求,然后自己处理响应:
//
// URLHandler.swift
// XIO
//
// Created by Brandon Anthony on 2018-10-01.
// Copyright © 2018 SO. All rights reserved.
//
import Foundation
class URLHandler: URLProtocol, URLSessionDataDelegate {
private var session: URLSession?
private var dataTask: URLSessionDataTask?
private static let requestIdentifier = "com.xio.uri.handled"
override class func canInit(with request: URLRequest) -> Bool {
guard request.url?.scheme == "myscheme" else {
return false
}
guard let handled = URLProtocol.property(forKey: URLHandler.requestIdentifier, in: request) as? Bool else {
return true
}
return !handled
}
override func startLoading() {
guard let request = (self.request as NSURLRequest).mutableCopy() as? NSMutableURLRequest else {
return
}
URLProtocol.setProperty(true, forKey: URLHandler.requestIdentifier, in: request)
var headers = request.allHTTPHeaderFields
headers?["Accept"] = "application/json"
headers?["Content-Type"] = "application/json"
request.allHTTPHeaderFields = headers
let configuration = URLSessionConfiguration.default
self.session = URLSession(configuration: configuration, delegate: self, delegateQueue: nil)
self.dataTask = self.session?.dataTask(with: request as URLRequest)
self.dataTask?.resume()
}
override func stopLoading() {
self.dataTask?.cancel()
self.dataTask = nil
self.session?.invalidateAndCancel()
self.session = nil
}
override class func canonicalRequest(for request: URLRequest) -> URLRequest {
return request
}
override class func requestIsCacheEquivalent(_ a: URLRequest, to b: URLRequest) -> Bool {
return super.requestIsCacheEquivalent(a, to: b)
}
// MARK: NSURLSessionTaskDelegate
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
if let error = error as NSError? {
if error.code == NSURLErrorUnsupportedURL {
guard let request = task.currentRequest else {
self.client?.urlProtocol(self, didFailWithError: error)
return
}
guard let url = request.url, let headers = request.allHTTPHeaderFields else {
self.client?.urlProtocol(self, didFailWithError: error)
return
}
guard let response = HTTPURLResponse(url: url, statusCode: 200, httpVersion: "HTTP/1.1", headerFields: headers) else {
self.client?.urlProtocol(self, didFailWithError: error)
return
}
OktaTokenManager(Okta.shared).parseCode(url: url) { state, code in
guard let state = state, let code = code else {
self.client?.urlProtocol(self, didFailWithError: error)
return
}
let json: [String: Any] = ["key": "value"]
]
do {
let data = try JSONSerialization.data(withJSONObject: json, options: .prettyPrinted)
self.client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed)
self.client?.urlProtocol(self, didLoad: data as Data)
self.client?.urlProtocolDidFinishLoading(self)
} catch let err {
print(err)
self.client?.urlProtocol(self, didFailWithError: error)
}
}
return
}
}
if let response = task.response {
client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed)
}
if let error = error { //&& error.code != NSURLErrorCancelled {
self.client?.urlProtocol(self, didFailWithError: error)
} else {
self.client?.urlProtocolDidFinishLoading(self) //cacheResponse
}
}
}
最后,您可以不执行上述任何操作,当您的请求失败时,解析错误(类似于上面)以获得真正的响应。虽然我更喜欢这种方法.. 尤其是第一种方法而不是发起请求。