I hit this and worked around it using a Dictionary extension to create custom indexes.
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 } } }
The timings in the comments relate to the comparison of different scenarios (optimized assembly, -Os , averaged over 1,000,000 iterations). Equivalent access to the standard dictionary came out at 1257ns. To make two checks effectively doubled, that is 2412ns.
In my particular case, I saw the header coming back from a server that was a camel case or lowercase, depending on the network I was connecting to (something else to investigate). The advantage of this is that if it is fixed, I can simply remove the extension and nothing else needs to be changed. In addition, anyone who uses the code does not need to remember any workarounds - they get it for free.
I checked and did not see that the ETag was changed by HTTPURLResponse - if I passed it to ETag or ETag , I got them back to allHeaderFields . In case performance is a problem and you are facing this problem, you can create a second index that takes a Hashable structure containing an array. Then pass it to the dictionary, with the tags you want to process.
struct DictionaryKey: Hashable { let keys: [String] var hashValue: Int { return 0 }
This, as one would expect, is almost equivalent to performing separate dictionary searches.
Stepheng
source share