2016-10-20 6 views
8

Преобразование в Swift 3 Я заметил, что возникает странная ошибка чтения поля заголовка из HTTPURLResponse.HTTPURLResponse allHeaderFields Swift 3 Капитализация

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

больше не работал.

Я распечатал словарь всех заголовков, и все мои заголовочные ключи, похоже, находятся в случае предложения.

Согласно прокси-серверу Charles все мои заголовки в нижнем регистре. По словам бэкэнд-команды, в своем коде заголовки находятся в Title-Case. Согласно документам: заголовки должны быть нечувствительны к регистру.

Так что я не знаю, во что верить. Кто-нибудь еще находит в Swift 3, что их заголовки теперь превращаются в Sentence case iOS? Если это так, мы хотим?

Должен ли я регистрировать ошибку с Apple или я должен просто создать категорию в HTTPURLResponse, чтобы позволить мне случай незаметно найти значение заголовка.

ответ

8

Обновление: это known issue.


allHeaderFields должен возвращать регистронезависимый словарь, потому что это то, что HTTP спецификации требуется. Похоже на ошибку Swift, я бы опубликовал радар или отчет об ошибке.

Ниже приведен пример кода, который воспроизводит этот вопрос просто:

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) 

(. Адаптировано this Gist from Cédric Luthi)

1

В стразы для Swift 3, вы можете сделать это:

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

ОБНОВЛЕНИЕ: Этот вопрос по-прежнему не исправляется в Свифт 4.

1

Я ударил это и работал вокруг него, используя расширение на Dictionary для создания пользовательских индексов.

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

Тайминги в комментариях, из сравнительного анализа различных сценариев (оптимизированная сборка, -Os, в среднем более 1000000 итераций). Эквивалентный доступ к стандартному словарю вышел на 1257ns. Чтобы сделать две проверки эффективно удвоенной, 2412ns.

В моем конкретном случае, я видел, как заголовок возвращается с сервера, который был верблюжьим футляром или в нижнем регистре, в зависимости от сети, с которой я подключался (что-то еще для расследования). Преимущество этого в том, что, если он будет исправлен, я могу просто удалить расширение, и ничего больше не нужно изменять. Кроме того, кому-либо еще, используя код, не нужно запоминать какие-либо обходные пути - они получают этот бесплатный.

Я проверил и не видел ETag быть модифицирована HTTPURLResponse - если я передал ему ETag или Etag я получил их обратно в allHeaderFields. В случае, если производительность является проблемой, и вы сталкиваетесь с этой проблемой, вы можете создать второй индекс, который принимает структуру Hashable, содержащую массив. Затем передайте его в словарь, с тегами, которые вы хотите обработать.

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"])])" 

Это, как и следовало ожидать, почти эквивалентно индивидуальному поиску словарей.

0

Существует немного более короткая версия, чем Darko's for swift 3.0.

В связи с тем, что случаи имен заголовков могут отличаться в iOS8 и iOS10, поэтому наилучшим способом является использование нечувствительного к регистру сравнения.

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

Таким образом, все типы теперь поддерживаются:

  • чувствительны к регистру заголовка
  • Case-Нечувствительность-Header
  • CASE-INSENSITIVE-HEADER
2

на основе @ Ответ Darko Я сделал расширение Swift 3, которое позволит вам находить любые заголовки как нечувствительные к регистру:

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 
    } 

} 
0

Это мое. Вместо того, чтобы возиться с тем, как работает словарь, я создал категорию obj-c на NSHTTPURLResponse. Словарь Obj-C allHeaderFields по-прежнему не учитывает регистр.

@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 
0

Для простого случая использования

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

 Смежные вопросы

  • Нет связанных вопросов^_^