自定义 URI 方案 "Unsupported URL"
Custom URI Scheme "Unsupported URL"
下面是我的 info.plist 并且我已经注册了自定义应用程序查询方案 URI..
当我在模拟器或设备上使用 redirect_uri=myapplication://oauthcallback
执行 OAuth 回调时,我得到:
Task <CC539C38-4191-48BB-B126-E41BCE28151B>.<6> load failed with
error Error Domain=NSURLErrorDomain Code=-1002 "unsupported URL"
UserInfo={NSLocalizedDescription=unsupported URL,
NSErrorFailingURLStringKey=myapplication://oauthcallback?code=rGudk3a7c7&state=state-F7AF0906-984F-47C3-841B-9A55246C3784,
NSErrorFailingURLKey=myapplication://oauthcallback?code=rGudk3a7c7&state=state-F7AF0906-984F-47C3-841B-9A55246C3784,
_NSURLErrorRelatedURLSessionTaskErrorKey=(
"LocalDataTask <CC539C38-4191-48BB-B126-E41BCE28151B>.<6>"
), _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <CC539C38-4191-48BB-B126-E41BCE28151B>.<6>,
NSUnderlyingError=0x60000253e8e0 {Error Domain=kCFErrorDomainCFNetwork Code=-1002 "(null)"}} [-1002]
有什么想法吗? Info.plist 下面:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0.0</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string></string>
<key>CFBundleURLSchemes</key>
<array>
<string>myapplication</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>tel</string>
<string>myapplication</string>
</array>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
</array>
</dict>
</plist>
我明白了。事实证明,在 iOS 上执行 OAuth2 时,RedirectURI 不起作用!它总是 returns 不受支持 URL.. 所以你要做的是创建一个自定义 URL 协议并在其中处理你的 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
}
}
}
最后,您可以不执行上述任何操作,当您的请求失败时,解析错误(类似于上面)以获得真正的响应。不过我更喜欢这种方法。尤其是第一种方法发起请求。
下面是我的 info.plist 并且我已经注册了自定义应用程序查询方案 URI..
当我在模拟器或设备上使用 redirect_uri=myapplication://oauthcallback
执行 OAuth 回调时,我得到:
Task <CC539C38-4191-48BB-B126-E41BCE28151B>.<6> load failed with
error Error Domain=NSURLErrorDomain Code=-1002 "unsupported URL"
UserInfo={NSLocalizedDescription=unsupported URL,
NSErrorFailingURLStringKey=myapplication://oauthcallback?code=rGudk3a7c7&state=state-F7AF0906-984F-47C3-841B-9A55246C3784,
NSErrorFailingURLKey=myapplication://oauthcallback?code=rGudk3a7c7&state=state-F7AF0906-984F-47C3-841B-9A55246C3784,
_NSURLErrorRelatedURLSessionTaskErrorKey=(
"LocalDataTask <CC539C38-4191-48BB-B126-E41BCE28151B>.<6>"
), _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <CC539C38-4191-48BB-B126-E41BCE28151B>.<6>,
NSUnderlyingError=0x60000253e8e0 {Error Domain=kCFErrorDomainCFNetwork Code=-1002 "(null)"}} [-1002]
有什么想法吗? Info.plist 下面:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0.0</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string></string>
<key>CFBundleURLSchemes</key>
<array>
<string>myapplication</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>tel</string>
<string>myapplication</string>
</array>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
</array>
</dict>
</plist>
我明白了。事实证明,在 iOS 上执行 OAuth2 时,RedirectURI 不起作用!它总是 returns 不受支持 URL.. 所以你要做的是创建一个自定义 URL 协议并在其中处理你的 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
}
}
}
最后,您可以不执行上述任何操作,当您的请求失败时,解析错误(类似于上面)以获得真正的响应。不过我更喜欢这种方法。尤其是第一种方法发起请求。