ios http swift3 case-insensitive nshttpurlresponse

ios - HTTPURLResponse allHeaderFields Swift 3 Capitalización



swift3 case-insensitive (7)

Aquí está el mío. En lugar de meterme con la forma en que funciona el diccionario, hice una categoría obj-c en NSHTTPURLResponse . El diccionario Obj-C allHeaderFields todavía no distingue entre mayúsculas y minúsculas.

@import Foundation; @implementation NSHTTPURLResponse (CaseInsensitive) - (nullable NSString *)allHeaderFieldsValueForCaseInsensitiveKey:(nonnull NSString *)key { NSString *value = self.allHeaderFields[key]; if ([value isKindOfClass:[NSString class]]) { return value; } else { return nil; } } @end

Convirtiendo a Swift 3 Noté que se producía un error extraño al leer un campo de encabezado de HTTPURLResponse.

let id = httpResponse.allHeaderFields["eTag"] as? String

ya no funcionó.

Imprimí el diccionario de todos los encabezados y todas las teclas de mi encabezado parecen estar en el caso de Sentencia.

De acuerdo con el proxy de Charles, todos mis encabezados están en minúsculas. Según el equipo de back-end, en su código, los encabezados están en Title-Case. De acuerdo con los documentos: los encabezados deben ser insensibles a las mayúsculas y minúsculas.

Entonces no sé en qué creer. ¿Alguien más está encontrando en Swift 3 que sus encabezados ahora están siendo convertidos en Sentence case por iOS? Si es así, ¿es este comportamiento lo que queremos?

¿Debo registrar un error con Apple o debería simplemente crear una categoría en HTTPURLResponse para permitirme, caso por caso, encontrar insensiblemente un valor de encabezado?


Basado en la respuesta de @Darko, he creado una extensión de Swift 3 que te permitirá encontrar los encabezados sin distinción de mayúsculas y minúsculas:

import Foundation extension HTTPURLResponse { func find(header: String) -> String? { let keyValues = allHeaderFields.map { (String(describing: $0.key).lowercased(), String(describing: $0.value)) } if let headerValue = keyValues.filter({ $0.0 == header.lowercased() }).first { return headerValue.1 } return nil } }


Como solución para Swift 3, puedes hacer esto:

// Converting to an array of tuples of type (String, String) // The key is lowercased() let keyValues = response.allHeaderFields.map { (String(describing: $0.key).lowercased(), String(describing: $0.value)) } // Now filter the array, searching for your header-key, also lowercased if let myHeaderValue = keyValues.filter({ $0.0 == "X-MyHeaderKey".lowercased() }).first { print(myHeaderValue.1) }


Golpeé esto y trabajé alrededor usando una extensión en Dictionary para crear subíndices personalizados.

extension Dictionary { subscript(key: String) -> Value? { get { let anyKey = key as! Key if let value = self[anyKey] { return value // 1213ns } if let value = self[key.lowercased() as! Key] { return value // 2511ns } if let value = self[key.capitalized as! Key] { return value // 8928ns } for (storedKey, storedValue) in self { if let stringKey = storedKey as? String { if stringKey.caseInsensitiveCompare(key) == .orderedSame { return storedValue // 22317ns } } } return nil } set { self[key] = newValue } } }

Los tiempos en los comentarios provienen de la comparación de diferentes escenarios (compilación optimizada, -Os , promediada en más de 1,000,000 de iteraciones). Un acceso equivalente de un diccionario estándar, salió a 1257ns. Tener que hacer dos comprobaciones efectivamente duplicó eso, 2412ns.

En mi caso particular, estaba viendo un encabezado volver del servidor que era camel-case, o minúscula, dependiendo de la red a la que me conectaba (algo más para investigar). La ventaja de esto es que, si se soluciona, puedo eliminar la extensión y nada más debe cambiar. Además, cualquier otra persona que use el código no necesita recordar ninguna solución alternativa: la obtiene de forma gratuita.

Comprobé y no vi que ETag fuera modificado por HTTPURLResponse ; si lo pasaba ETag o Etag , los recuperaba en allHeaderFields . En el caso de que el rendimiento sea una preocupación y se encuentre con este problema, puede crear un segundo subíndice que tome una estructura Hashable contenga una matriz. Luego, pásalo al Diccionario, con las etiquetas que quieras manejar.

struct DictionaryKey: Hashable { let keys: [String] var hashValue: Int { return 0 } // Don''t care what is returned, not going to use it } func ==(lhs: DictionaryKey, rhs: DictionaryKey) -> Bool { return lhs.keys == rhs.keys // Just filling expectations } extension Dictionary { subscript(key: DictionaryKey) -> Value? { get { for string in key.keys { if let value = self[string as! Key] { return value } } return nil } } } print("/(allHeaderFields[DictionaryKey(keys: ["ETag", "Etag"])])"

Esto es, como era de esperar, casi equivalente a hacer búsquedas de diccionarios individuales.


Hay una versión un poco más corta que la de Darko para swift 3.0.

Debido al hecho de que los casos de nombres de encabezado pueden ser diferentes en iOS8 e iOS10, la mejor manera es usar una comparación insensible a mayúsculas y minúsculas.

response.allHeaderFields.keys.contains(where: {$0.description.caseInsensitiveCompare("CaSe-InSeNsItIvE-HeAdEr") == .orderedSame})

Entonces todos los tipos ahora son compatibles:

  • case-insensitive-header
  • Case-Insensitive-Header
  • CASE-INSENSITIVE-HEADER

Para el uso de casos simples

if let ix = headers.index(where: {$0.key.caseInsensitiveCompare("eTag") == .orderedSame}){ let tag = headers[ix].value }


Actualización : este es un problema conocido .

allHeaderFields debería devolver un diccionario insensible a mayúsculas y minúsculas porque eso es lo que requiere la especificación HTTP. Parece un error Swift, me gustaría presentar un informe de radar o un error.

Aquí hay un código de muestra que reproduce el problema simplemente:

let headerFields = ["ETag" : "12345678"] let url = URL(string: "http://www.example.com")! let response = HTTPURLResponse(url: url, statusCode: 200, httpVersion: "HTTP/1.1", headerFields: headerFields)! response.allHeaderFields["eTaG"] // nil (incorrect) headerFields["eTaG"] // nil (correct)

(Adaptado de este Gist de Cédric Luthi )