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.***.comhttps://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 立即自动取消的主要内容,如果未能解决你的问题,请参考以下文章

自动创建立即购买按钮贝宝?

在插入中立即使用自动增量值

Javascript 自动执行函数(立即调用函数)

立即抓取mySql中自动生成的字段以供使用

Paypal 交易完成后立即自动返回(无需 10 秒)到我们的网站

jQuery select2打开后立即自动关闭