2016-10-20 113 views
8

轉換爲Swift 3我發現一個奇怪的錯誤發生在HTTPURLResponse中讀取頭字段。HTTPURLResponse allHeaderFields Swift 3大寫

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

不再有效。

我打印出所有標題字典和所有我的標題鍵似乎在句子的情況下。

根據查爾斯代理所有我的標題是小寫。根據後端團隊,在他們的代碼中標題是Title-Case。根據文檔:頭文件應該不區分大小寫。

所以我不知道該相信什麼。是否有其他人在Swift 3中發現他們的頭文件現在被iOS變成了Sentence case?如果這是我們想要的行爲?

我應該在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

爲熱修復程序斯威夫特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) 
} 

更新:這個問題似乎仍然沒有在Swift 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,平均超過百萬次迭代)。標準字典的等效訪問時間爲1257ns。必須使兩次檢查有效地加倍,即2412ns。

在我的特殊情況下,我看到一個頭部從服務器返回,它是駝峯式或小寫的,這取決於我連接的網絡(要調查的其他網絡)。這樣做的好處是,如果它得到解決,我可以刪除擴展名,而不需要改變。此外,使用該代碼的任何人都不需要記住任何解決方法 - 他們可以免費獲得這個解決方案。

我檢查,並沒有看到ETagHTTPURLResponse修改 - 如果我通過它ETag,或EtagallHeaderFields得到了那些回來。如果性能是一個問題,並且遇到此問題,則可以創建第二個下標,該下標需要包含數組的一個Hashable結構。然後將其傳遞給Dictionary,並使用您想要處理的標籤。

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的swift 3.0短一點的版本。

由於事實,在iOS8和iOS10中標題名稱的情況可能不同,所以最好的方法是使用不區分大小寫的比較。

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

因此,所有類型現在支持:

  • 不區分大小寫頭基於@
  • 不區分大小寫的標頭
  • 不區分大小寫,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

這是我的。我沒有搞亂字典的工作方式,而是在NSHTTPURLResponse上做了一個obj-c類。 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 
}