URLSessionUploadTask 立即自动取消
Posted
技术标签:
【中文标题】URLSessionUploadTask 立即自动取消【英文标题】:URLSessionUploadTask getting automatically cancelled instantly 【发布时间】:2016-07-15 23:32:15 【问题描述】:我遇到了一个奇怪的问题,新创建的URLSessionUploadTask
立即被取消。我不确定这是否是当前 Xcode 8 测试版的错误。
我怀疑这可能是一个错误,因为我要发布的代码恰好运行了一次。之后没有对其进行任何更改,然后它就停止了工作。是的,它确实运行了一次,然后就停止了工作。我将在接近尾声时发布错误。
我将在下面发布代码,但首先我将总结这里的逻辑是如何工作的。
我的测试或用户公开的 API(IE 用于 Playgrounds 或直接在应用程序上使用)调用 authorize
方法。这个authorize
方法将依次调用buildPOSTTask
,它将构造一个有效的URL 并返回一个URLSessionUploadTask
以供authorize
方法使用。
话虽如此,代码如下:
会议:
internal let urlSession = URLSession(configuration: .default)
创建上传任务的函数:
internal func buildPOSTTask(onURLSession urlSession: URLSession, appendingPath path: String, withPostParameters postParams: [String : String]?, getParameters getParams: [String : String]?, httpHeaders: [String : String]?, completionHandler completion: URLSessionUploadTaskCompletionHandler) -> URLSessionUploadTask
let fullURL: URL
if let gets = getParams
fullURL = buildURL(appendingPath: path, withGetParameters: gets)
else
fullURL = URL(string: path, relativeTo: baseURL)!
var request = URLRequest(url: fullURL)
request.httpMethod = "POST"
var postParameters: Data? = nil
if let posts = postParams
do
postParameters = try JSONSerialization.data(withJSONObject: posts, options: [])
catch let error as NSError
fatalError("[\(#function) \(#line)]: Could not build POST task: \(error.localizedDescription)")
let postTask = urlSession.uploadTask(with: request, from: postParameters, completionHandler: completion)
return postTask
身份验证函数,它使用由上述函数创建的任务:
public func authorize(withCode code: String?, completion: AccessTokenExchangeCompletionHandler)
// I have removed a lot of irrelevant code here, such as the dictionary building code, to make this snippet shorter.
let obtainTokenTask = buildPOSTTask(onURLSession: self.urlSession, appendingPath: "auth/access_token", withPostParameters: nil, getParameters: body, httpHeaders: nil) (data, response, error) in
if let err = error
completion(error: err)
else
print("Response is \(response)")
completion(error: nil)
obtainTokenTask.resume()
我在测试中发现了这个错误:
let testUser = Anilist(grantType: grant, name: "Test Session")
let exp = expectation(withDescription: "Waiting for authorization")
testUser.authorize(withCode: "a valid code") (error) in
if let er = error
XCTFail("Authentication error: \(er.localizedDescription)")
exp.fulfill()
self.waitForExpectations(withTimeout: 5) (err) in
if let error = err
XCTFail(error.localizedDescription)
它总是因为这个错误而立即失败:
错误域=NSURLErrorDomain 代码=-999 “取消” UserInfo=NSErrorFailingURLKey=https://anilist.co/api/auth/access_token?client_secret=REMOVED&grant_type=authorization_code&redirect_uri=genericwebsitethatshouldntexist.bo&client_id=ibanez-hod6w&code=REMOVED, NSLocalizedDescription=取消, NSErrorFailingURLStringKey=https://anilist.co/api/auth/access_token?client_secret=REMOVED&grant_type=authorization_code&redirect_uri=genericwebsitethatshouldntexist.bo&client_id=ibanez-hod6w&code=REMOVED
请注意以下几点:
会话使用的 URL 有效。 所有凭据均有效。 它立即失败并出现“已取消”错误,这在以前根本没有发生过。我没有在任何地方取消任务,所以它被系统取消了。 在启用了无限期执行的 Playgrounds 上也会失败。这不仅限于我的测试。这是我尝试过的事情的清单:
因为我怀疑这是一个错误,我首先尝试清理我的项目,删除派生数据,并重置所有模拟器。他们都没有工作。 甚至重启我的 Mac... 由于上传任务没有任何强指针而被释放,并反过来调用cancel
,我也重写了authorize
以返回由buildPOSTTask
创建的任务并将其分配给我的测试中的一个变量。任务仍在取消。
我还没有尝试过的事情(但我会接受任何其他想法,因为我正在研究这些):
在物理设备上运行它。目前在 iPad 上下载 ios 10,因为这是一个 iOS 10 项目。编辑:我刚试过,这是不可能的。我不知道该尝试什么。生成的日志似乎没有任何有用的信息。
编辑:
我决定在这里发布整个项目。无论如何,这东西完成后都会开源,我得到的 API 凭据是用于测试应用程序的。
ALCKit
【问题讨论】:
对,对不起,这很重要,哈哈。是的,测试确实有一个 waitForExpectationsWithTimeout 调用。我将编辑我的问题以添加它。我还没有在实际的应用程序中尝试过(这是我正在构建的框架,但稍后将构建一个使用的应用程序),但它在启用无限期执行的 Swift 游乐场中确实失败了。 你可以试试:pastebin.com/B7nZFY1n 它会打印响应吗?报错? 感谢布兰登的意见。我在操场上运行它,它运行良好。按预期打印响应。 为你给我的代码写一个测试也很好。我的方法调用之间一定发生了什么事。 pastebin.com/ijshDH6Q 似乎与您使用的代码相同。我能够根据您的错误代码推断出它。 【参考方案1】:在为此苦苦挣扎了 6 天,并在谷歌上不停地寻找解决方案之后,我真的很高兴说我终于弄明白了。
事实证明,无论出于什么神秘原因,uploadTask(with:from:completionHandler)
中的 from:
参数都不能为 nil。尽管该参数被标记为可选Data
,但它在丢失时会立即取消。这可能是 Apple 方面的一个错误,当我无法让它工作时我打开了一个错误,所以我将使用这些新信息更新我的错误报告。
话虽如此,我所要做的就是更新我的buildPOSTTask
方法,以说明传递的字典可能为零。有了它,它现在可以正常工作了:
internal func buildPOSTTask(onURLSession urlSession: URLSession, appendingPath path: String, withPostParameters postParams: [String : String]?, getParameters getParams: [String : String]?, httpHeaders: [String : String]?, completionHandler completion: URLSessionUploadTaskCompletionHandler) -> URLSessionUploadTask
let fullURL: URL
if let gets = getParams
fullURL = buildURL(appendingPath: path, withGetParameters: gets)
else
fullURL = URL(string: path, relativeTo: baseURL)!
var request = URLRequest(url: fullURL)
request.httpMethod = "POST"
var postParameters: Data
if let posts = postParams
do
postParameters = try JSONSerialization.data(withJSONObject: posts, options: [])
catch let error as NSError
fatalError("[\(#function) \(#line)]: Could not build POST task: \(error.localizedDescription)")
else
postParameters = Data()
let postTask = urlSession.uploadTask(with: request, from: postParameters, completionHandler: completion)
return postTask
【讨论】:
刚刚为此苦苦挣扎了两个多小时。苹果有没有回复过你的错误报告?由于没有发布参数而被取消的错误是荒谬的(而且令人愤怒) 他们没有回复。它也没有被标记为重复。它自 2016 年 7 月 19 日起开放。 现在是 2019 年。仍然需要传递非零数据。 @Fitsyu 是的。该错误报告也被忽略了 3 年多。【参考方案2】:你的服务器坏了..
tcp_connection_cancel 1
nw_socket_handle_socket_event Event mask: 0x4
nw_socket_handle_socket_event Socket received WRITE_CLOSE event
nw_endpoint_handler_cancel [1 anilist.co:443 ready resolver (satisfied)]
nw_endpoint_handler_cancel [1.1 104.28.1.44:443 ready socket-flow (satisfied)]
__nw_socket_service_writes_block_invoke sendmsg(fd 9, 31 bytes): socket has been closed
nw_endpoint_flow_protocol_error [1.1
104.28.1.44:443 cancelled socket-flow (null)] Socket protocol sent error: [32] Broken pipe
nw_endpoint_flow_protocol_disconnected [1.1 104.28.1.44:443 cancelled socket-flow (null)] Output protocol disconnected
nw_endpoint_handler_cancel [1.2 104.28.0.44:443 initial path (null)]
nw_resolver_cancel_on_queue 0x60800010da40
[NWConcrete_tcp_connection dealloc] 1
[User Defaults] CFPrefsPlistSource<0x6180000f8700> (Domain: XIO.PrivateAPITest, User: kCFPreferencesCurrentUser, ByHost: No, Container: (null)) is waiting for writes to complete so it can determine if new data is available
它无限等待“写入”完成..
它发送请求.. 进行 SSL 握手并且没有得到响应。它超时并认为它是一个损坏的请求..
class WTF : NSObject, URLSessionDelegate
var urlSession: URLSession!
override init()
super.init()
urlSession = URLSession(configuration: .default, delegate: self, delegateQueue: nil)
var request = URLRequest(url: URL(string: "https://anilist.co/api/auth/access_token?client_secret=REMOVED&grant_type=authorization_code&redirect_uri=genericwebsitethatshouldntexist.bo&client_id=ibanez-hod6w&code=REMOVED")!)
request.httpMethod = "POST"
let data = try! JSONSerialization.data(withJSONObject: ["Test":"Test"], options: [])
urlSession.uploadTask(with: request, from: data).resume()
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
completionHandler(.performDefaultHandling, nil)
func urlSession(_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64)
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: NSError?)
func urlSession(_ session: URLSession, task: URLSessionTask, willPerformHTTPRedirection response: HTTPURLResponse, newRequest request: URLRequest, completionHandler: (URLRequest?) -> Void)
completionHandler(request)
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: (URLSession.ResponseDisposition) -> Void)
completionHandler(.allow)
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data)
【讨论】:
好吧,就像我在问题的 cmets 中所说的那样,当我用任何通用 URL 替换 URL 时,我遇到了同样的错误。我用http://www.***.com
和https://www.google.com
替换了buildPOSTTask
中的fullURL
,同样的事情正在发生。您在此答案中提供的代码也在 Playground 中成功完成,我能够看到响应(对于 Anilist URL)。看起来服务器没有损坏(顺便说一句,我不管理它),并且没有关于人们在授权应用程序时遇到问题的报告。这很令人困惑,我将在实际应用中尝试一下,看看效果如何。
好的,刚刚在单视图应用程序中测试了我的整个代码。无论 URL 是 AniList 还是 StatckOverflow 或 Google,我都会收到相同的错误。这绝对是我的代码错误。
更多信息,整个is waiting for writes to complete so it can determine if new data is available
似乎与请求本身没有任何关系。它似乎是当前 beta 版本中某些激进日志记录的一部分。我只能找到this,但你可以看到他们甚至没有使用 URLSession 或等效的 API。
尝试在全新的机器上运行代码。同样的错误。哈哈。好吧,至少我知道我的主计算机表现得不好。
从 Xcode 8 Beta 3 开始,我们可以正式放弃 is waiting for writes to complete so it can determine if new data is available
日志记录,因为它毫无意义。谢谢你的意见。会尝试找出问题所在。【参考方案3】:
您是否有机会使用 Ensighten 等第三方库?我在 XCode 8 beta 中遇到了完全相同的问题(在 XCode 7 中运行良好),并且我所有带有 nil 参数的块都导致了崩溃。原来是图书馆做了一些编码导致了这个问题。
【讨论】:
你是如何确定的? 我通过反复试验。我禁用了图书馆,一切都运行良好。在我的特殊情况下,他们已经更新了库并且一切运行良好。【参考方案4】:对我来说,这是一个导致问题的弱引用,所以我改变了
completion: [weak self] (response: Result<ResponseType, Error>)
到
completion: [self] (response: Result<ResponseType, Error>)
【讨论】:
以上是关于URLSessionUploadTask 立即自动取消的主要内容,如果未能解决你的问题,请参考以下文章