2016-10-20 70 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 
}