转换为Swift 3我注意到从HTTPURLResponse读取头字段时出现了一个奇怪的错误。
let id = httpResponse.allHeaderFields["eTag"] as? String
不再有效。
我打印出所有标题字典,我的所有标题键似乎都是Sentence case。
根据查尔斯代理,我的所有标题都是小写的。根据后端团队的说法,在他们的代码中,标题是Title-Case。根据文档:标题应该不区分大小写。
所以我不知道该相信哪一个。是否有其他人在Swift 3中发现他们的标题现在被iOS转变为句子案例?如果是这样,我们想要吗?
我是否应该记录Apple的错误,或者我应该在HTTPURLResponse上创建一个类别,以允许自己不区分大小写地找到标题值。
答案 0 :(得分:9)
更新:这是known issue。
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)
应返回不区分大小写的字典,因为这是HTTP规范所要求的。看起来像Swift错误,我会提交雷达或错误报告。
以下是一些简单再现问题的示例代码:
<div id="duplicater">
<label>Number:</label>
<input type="number" class="quantity" id="quantity"/>
<button id="removebutton" class='remove'>Delete</button>
<button id="add">add</button>
</div>
<div id="new_item_details" class ="new_item_details">
</div>
<script>
jQuery(document).ready(function() {
var id = 0;
jQuery(document).on('click', '#add', function(e) {
var button = jQuery('#duplicater').clone();
id++;
button.removeAttr('id');
button.insertBefore('.new_item_details');
button.attr('id', 'new_' + id).attr('data-id', id);
button.find('.remove').attr('data-id', id);
e.preventDefault();
});
jQuery(document).on('click', '.remove', function(e) {
var thisId = jQuery(this).data('id');
jQuery('div[data-id="' + thisId + '"]').remove();
e.preventDefault();
});
});
</script>
答案 1 :(得分:4)
根据@ 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
}
}
答案 2 :(得分: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)
}
更新:此问题似乎仍未在Swift 4中修复。
答案 3 :(得分: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
,平均超过1,000,000次迭代)。标准词典的等效访问出现在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"])])"
正如您所期望的那样,这几乎等同于进行单独的字典查找。
答案 4 :(得分:0)
对于swift 3.0,有一个比Darko更短的版本。
由于事实,在iOS8和iOS10中标题名称的情况可能不同,因此最好的方法是使用不区分大小写的比较。
response.allHeaderFields.keys.contains(where: {$0.description.caseInsensitiveCompare("CaSe-InSeNsItIvE-HeAdEr") == .orderedSame})
所以现在支持所有类型:
答案 5 :(得分: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
答案 6 :(得分:0)
简单使用案例
if let ix = headers.index(where: {$0.key.caseInsensitiveCompare("eTag") == .orderedSame}){
let tag = headers[ix].value
}
答案 7 :(得分:0)
在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
答案 8 :(得分:0)
更有效的解决方法:
<div
v-for="(prod, index) in products"
:key="index"
class="w-full md:w-1/3 lg:w-1/4 xl:w-1/4 mb-8 px-2
>
<promotion v-if="(index % 18 == 0) && index != 0" ></promotion>
<product-card v-else :product="prod"></product-card>
</div>
答案 9 :(得分:0)
由于有bug in Swift和新的solution in iOS 13,我做了一个扩展:
这里是link to gist。
public extension HTTPURLResponse {
func valueForHeaderField(_ headerField: String) -> String? {
if #available(iOS 13.0, *) {
return value(forHTTPHeaderField: headerField)
} else {
return (allHeaderFields as NSDictionary)[headerField] as? String
}
}
}