HTTPURLResponse allHeaderFields Swift 3资本化

时间:2016-10-20 10:57:38

标签: ios http swift3 case-insensitive nshttpurlresponse

转换为Swift 3我注意到从HTTPURLResponse读取头字段时出现了一个奇怪的错误。

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

不再有效。

我打印出所有标题字典,我的所有标题键似乎都是Sentence case。

根据查尔斯代理,我的所有标题都是小写的。根据后端团队的说法,在他们的代码中,标题是Title-Case。根据文档:标题应该不区分大小写。

所以我不知道该相信哪一个。是否有其他人在Swift 3中发现他们的标题现在被iOS转变为句子案例?如果是这样,我们想要吗?

我是否应该记录Apple的错误,或者我应该在HTTPURLResponse上创建一个类别,以允许自己不区分大小写地找到标题值。

10 个答案:

答案 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>

(改编自this Gist from Cédric Luthi。)

答案 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})

所以现在支持所有类型:

  • 不区分大小写的报头
  • 不区分大小写报头
  • CASE-INSENSITIVE-HEADER

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