2016-10-20 15 views
8

Chuyển đổi sang Swift 3 Tôi nhận thấy một lỗi lạ xảy ra khi đọc trường tiêu đề từ HTTPURLResponse.HTTPURLResponse allHeaderFields Swift 3 Viết hoa

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

không còn hoạt động nữa.

Tôi đã in tất cả các từ khóa tiêu đề và tất cả các khóa tiêu đề của tôi có vẻ như trong trường hợp Câu.

Theo Charles proxy, tất cả các tiêu đề của tôi đều ở dạng chữ thường. Theo nhóm phụ trợ, trong mã của họ, các tiêu đề nằm trong Title-Case. Theo tài liệu: tiêu đề phải phân biệt chữ hoa chữ thường.

Vì vậy, tôi không biết nên tin điều gì. Có ai khác tìm thấy trong Swift 3 rằng các tiêu đề của họ hiện đang được chuyển thành trường hợp Sentence bởi iOS? Nếu vậy thì hành vi này chúng ta muốn?

Tôi có nên đăng nhập lỗi với Apple hay tôi chỉ cần tạo một danh mục trên HTTPURLResponse để cho phép bản thân không phân biệt tìm giá trị tiêu đề.

Trả lời

8

Cập nhật: đây là known issue.


allHeaderFields phải trả lại từ điển không phân biệt dạng chữ vì đó là thông số HTTP yêu cầu. Trông giống như một lỗi Swift, tôi sẽ gửi một radar hoặc một báo cáo lỗi.

Dưới đây là một số mẫu mã nhằm tái tạo vấn đề đơn giản:

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) 

(. Phỏng theo this Gist from Cédric Luthi)

1

Là một hot-sửa chữa cho Swift 3, bạn có thể làm điều này:

// 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) 
} 

CẬP NHẬT: Vấn đề này có vẻ vẫn chưa được khắc phục trong Swift 4.

1

Tôi nhấn vào nó và làm việc xung quanh nó bằng cách sử dụng một phần mở rộng trên Dictionary để tạo các bảng con tùy chỉnh.

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

Thời gian trong nhận xét là từ điểm chuẩn khác nhau (tối ưu hóa xây dựng, -Os, trung bình trên 1.000.000 lần lặp). Một truy cập tương đương của một từ điển tiêu chuẩn, xuất hiện ở 1257ns. Phải thực hiện hai lần kiểm tra hiệu quả gấp đôi, 2412ns.

Trong trường hợp cụ thể của tôi, tôi đã nhìn thấy một tiêu đề quay lại từ máy chủ là trường hợp lạc đà hoặc thấp hơn, tùy thuộc vào mạng tôi đang kết nối (điều gì khác cần điều tra). Ưu điểm của điều này là, nếu nó được cố định, tôi chỉ có thể xóa phần mở rộng và không có gì khác cần phải thay đổi. Ngoài ra, bất kỳ ai khác sử dụng mã không cần phải nhớ bất kỳ cách giải quyết nào - họ đều nhận được mã này miễn phí.

tôi đã kiểm tra và không thấy ETag được sửa đổi bởi HTTPURLResponse - nếu tôi thông qua nó ETag, hoặc Etag Tôi có những người trở lại trong allHeaderFields. Trong trường hợp đó hiệu suất là một mối quan tâm, và bạn đang gặp phải vấn đề này, bạn có thể tạo một subscript thứ hai mà có một cấu trúc Hashable chứa một mảng. Sau đó chuyển nó vào từ điển, với các thẻ bạn muốn xử lý.

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"])])" 

Đây là, như bạn mong đợi, gần như tương đương với việc tra cứu từ điển riêng lẻ.

0

Có phiên bản ngắn hơn một chút so với phiên bản nhanh hơn của Darko 3.0.

Do thực tế, trường hợp tên tiêu đề có thể khác nhau trong iOS8 và iOS10 nên cách tốt nhất là sử dụng so sánh phân biệt chữ hoa chữ thường.

response.allHeaderFields.keys.contains(where: {$0.description.caseInsensitiveCompare("CaSe-InSeNsItIvE-HeAdEr") == .orderedSame}) 

Vì vậy, tất cả các loại đang được hỗ trợ:

  • case-insensitive-header
  • Case-Insensitive-Header
  • case-insensitive-HEADER
2

Dựa trên @ Câu trả lời của Darko tôi đã thực hiện một tiện ích mở rộng Swift 3 sẽ cho phép bạn tìm bất kỳ tiêu đề nào là phân biệt chữ hoa chữ thường:

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

Đây là của tôi. Thay vì lộn xộn xung quanh với cách hoạt động của từ điển, tôi đã tạo một danh mục obj-c trên NSHTTPURLResponse. Từ điển Obj-C allHeaderFields vẫn không phân biệt chữ hoa chữ thường.

@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

Đối với trường hợp đơn giản sử dụng

if let ix = headers.index(where: {$0.key.caseInsensitiveCompare("eTag") == .orderedSame}){ 
    let tag = headers[ix].value 
}