URLSessionUploadTask se cancela automáticamente al instante

Estoy teniendo este extraño problema en el que una URLSessionUploadTask recién creada se cancela instantáneamente. No estoy seguro de si se trata de un error con la beta actual de Xcode 8.

Sospecho que podría ser un error porque el código que estoy a punto de publicar funcionó bien exactamente una vez. No se hicieron cambios después y simplemente dejó de funcionar. Sí, literalmente funcionó una vez y luego dejó de funcionar. Publicaré el error cerca del final.

Publicaré el código a continuación, pero primero resumiré cómo funciona la lógica aquí.

Mi testing, o API expuesta por el usuario (IE para usar en Playgrounds o directamente en aplicaciones), llama al método authorize . Este método de authorize llamará a continuación a buildPOSTTask , que buildá una URL válida y devolverá una URLSessionUploadTask que utilizará el método de authorize .

Dicho esto, el código se encuentra a continuación:

La session:

 internal let urlSession = URLSession(configuration: .default) 

Función para crear una tarea de carga:

 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 } 

La function de authentication, que utiliza una tarea creada por la function anterior:

  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() } 

Atrapé este error en una testing:

 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) } } 

Siempre falla al instante con este error:

Error Domain = NSURLErrorDomain Code = -999 "canceled" UserInfo = {NSErrorFailingURLKey = https://anilist.co/api/auth/access_token?client_secret=REMOVED&grant_type=authorization_code&networkingirect_uri=genericwebsitethatshouldntexist.bo&client_id=ibanez-hod6w&code=REMOVED , NSLocalizedDescription = canceled, NSErrorFailingURLStringKey = https://anilist.co/api/auth/access_token?client_secret=REMOVED&grant_type=authorization_code&networkingirect_uri=genericwebsitethatshouldntexist.bo&client_id=ibanez-hod6w&code=REMOVED }

Aquí hay algunas cosas a tener en count:

  • La URL utilizada por la session es válida.
  • Todas las cnetworkingenciales son válidas.
  • Fallece instantáneamente con un error "cancelado", que simplemente no sucedió antes. No cancelo la tarea en ninguna parte, por lo que el sistema la cancela.
  • También falla en Playgrounds con ejecución indefinida habilitada . Esto no se limita a mis testings.

Aquí hay una list de cosas que he intentado:

  • Como sospecho que esto es un error, primero traté de limpiar mi proyecto, eliminar los datos derivados y restablecer todos los simuladores. Ninguno de ellos funcionó.
  • Incluso fue tan lejos reiniciar mi Mac …
  • Bajo la pequeña sospecha de que la tarea de carga se estaba desasignando debido a que no tenía pointers fuertes y, a su vez, cancel llamada, también reescribí authorize a devolver la tarea creada por buildPOSTTask y asignarla a una variable en mi testing. La tarea aún se cancelaba.

Cosas que todavía tengo que probar (pero aceptaré otras ideas mientras trabajo con ellas):

  • Ejecutarlo en un dispositivo físico. Actualmente descargando iOS 10 en un iPad ya que es un proyecto iOS 10. EDITAR: Acabo de intentarlo y no es posible hacerlo.

No tengo idea de qué probar. Los loggings generados no parecen tener ninguna información útil.

EDITAR:

He decidido publicar todo el proyecto aquí. La cosa será de código abierto de todos modos cuando haya terminado, y las cnetworkingenciales de la API que tengo son para una aplicación de testing.

ALCKit

Después de luchar sin parar con esto durante 6 días, y después de search en Google una solución, estoy feliz de decir que finalmente lo he descubierto.

uploadTask(with:from:completionHandler) que, por cualquier razón misteriosa, el parámetro from: parameter en uploadTask(with:from:completionHandler) no puede ser nulo. A pesar de que el parámetro se marca como un Data opcional, se cancela instantáneamente cuando falta. Probablemente esto sea un error del lado de Apple, y abrí un error cuando no pude lograr que esto funcione, por lo que actualizaré mi informe de errores con esta nueva información.

Dicho esto, todo lo que tenía que hacer era actualizar mi método buildPOSTTask para tener en count la posibilidad de que el dictionary pasado sea nulo. Con eso en su lugar, funciona bien ahora:

 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 } 

Tu server está roto …

 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 

Espera infinitamente a "escribir" para completar …

Envía la request … ¿El protocolo de enlace SSL y no recibe respuesta? Se agota y lo considera una request rota.

 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&networkingirect_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, URLCnetworkingential?) -> 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) { } } 

¿Estás usando una biblioteca de terceros como Ensighten? Tuve el mismo problema en XCode 8 beta (funciona bien en XCode 7) y todos mis bloques con parameters nulos estaban causando fallas. Resulta que fue la biblioteca la que hizo alguna encoding causando el problema.