转换为斯威夫特3,我注意到一个奇怪的错误发生,从HTTPURLResponse读取头字段。
let id = httpResponse.allHeaderFields["eTag"] as? String
不再奏效。
我打印出所有头,字典和我的所有头键似乎是句首字母大写。
据查尔斯代理所有我的头是小写。根据后端团队,在他们的代码中的头是在标题案例。根据文档:标题应该是不区分大小写。
所以,我不知道该相信哪个。是任何人都在斯威夫特3发现他们的头现在正在变成句首字母大写,内部监督办公室?如果是的话是这样的行为,我们想要的吗?
我应该记录与苹果的错误或我应该就HTTPURLResponse一个类别,让自己不区分大小写区分找到一个标头值。
更新:这是一个known issue。
allHeaderFields
应该返回一个不区分大小写的字典,因为这是HTTP规范要求。看起来像雨燕的错误,我会提起雷达或错误报告。
下面是简单地再现问题的一些示例代码:
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)
根据@达科的答案我已经作出了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
}
}
我打了这一点,并使用上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。
在我的具体情况,我看到一个标题都来自那是驼峰或小写服务器返回的,视网络上的我连接上(别的调查)。这样做的好处是,它应该得到固定,我可以删除扩展,并没有别的需要改变。此外,其他任何人使用代码不需要记住任何变通办法 - 他们得到这一个免费的。
我查了一下,并没有看到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"])])"
这是,如你所期望的,几乎等同于使个人字典查找。
作为一个热修复的斯威夫特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。
更有效的解决方法:
(response.allHeaderFields as NSDictionary)["etag"]
有一点点位短版本比达科对SWIFT 3.0。
由于这样的事实,即在头名的情况下可以在iOS 8的不同,iOS10所以最好的方法就是使用比较不敏感的情况下。
response.allHeaderFields.keys.contains(where: {$0.description.caseInsensitiveCompare("CaSe-InSeNsItIvE-HeAdEr") == .orderedSame})
因此,所有类型现在支持:
这里是我的。相反,与字典的工作方式乱搞的,我做了一个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
对于简单的情况下使用
if let ix = headers.index(where: {$0.key.caseInsensitiveCompare("eTag") == .orderedSame}){
let tag = headers[ix].value
}
在swift 4.1
它为我工作。
if let headers = httpResponse.allHeaderFields as? [String: String], let value = headers["key"] {
print("value: \(value)")
}
并且你还可以(根据您的情况),如果没有工作像下面使用.lowercased()
和.capitalized
值
httpResponse.allHeaderFields["eTag".capitalized] as? String
httpResponse.allHeaderFields["eTag". lowercased()] as? String