【问题标题】:How do I convert url.query to a dictionary in Swift?如何将 url.query 转换为 Swift 中的字典?
【发布时间】:2018-03-18 02:40:30
【问题描述】:

我有一个 URL 进入 AppDelegate 方法:

func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool {
}

网址看起来像www.wesite.com/shareplace.html?placeid=123

如何将其转换为字典以便于访问?

我在某个网站上找到了一些代码,但它在 Xcode 9 中显示错误:

 extension URL {
    var queryDictionary: [String: AnyObject]? {
        return URLComponents(url: self, resolvingAgainstBaseURL: false)?
            .queryItems?
            .reduce([:], combine: { (var result: [String: AnyObject], queryItem) -> [String: AnyObject] in
                if queryItem.value?.containsString(",") ?? false {
                    let array = queryItem.value?.componentsSeparatedByString(",")

                    result[queryItem.name] = array
                }
                else {
                    result[queryItem.name] = queryItem.value
                }

                return result
            })
    }
}

.reduce([:], combine: { (var result: [String: AnyObject], queryItem) -> [String: AnyObject] in(var 结果)参数可能没有 'var' 说明符

【问题讨论】:

    标签: ios swift nsdictionary nsurl


    【解决方案1】:

    简单扩展

    extension URL {
        var queryDictionary: [String: String]? {
            guard let query = self.query else { return nil}
    
            var queryStrings = [String: String]()
            for pair in query.components(separatedBy: "&") {
    
                let key = pair.components(separatedBy: "=")[0]
    
                let value = pair
                    .components(separatedBy:"=")[1]
                    .replacingOccurrences(of: "+", with: " ")
                    .removingPercentEncoding ?? ""
    
                queryStrings[key] = value
            }
            return queryStrings
        }
    }
    

    用法

    let urlString = "http://www.youtube.com/video/4bL4FI1Gz6s?hl=it_IT&iv_logging_level=3&ad_flags=0&endscreen_module=http://s.ytimg.com/yt/swfbin/endscreen-vfl6o3XZn.swf&cid=241&cust_gender=1&avg_rating=4.82280613104"
    let url = URL(string: urlString)
    print(url!.queryDictionary ?? "NONE")
    

    【讨论】:

    • 请考虑将let query = URLComponents(string: self.absoluteString)?.query 替换为let query = self.query。如果 URL 在参数值中包含编码的“=”,则第一个版本会产生错误的结果。
    • 建议已获批准。
    • 哈哈,我在我从事的项目中找到了完全相同的代码......它崩溃了:D 例如statckoverflow.com/ok?id&
    【解决方案2】:

    这里是一个使用 Swift reduce 函数的例子。这会将像 'key1=value1&key2=value2&key3=value3' 这样的字符串转换为字典。

    let params = queryString.components(separatedBy: "&").map({
        $0.components(separatedBy: "=")
    }).reduce(into: [String:String]()) { dict, pair in
        if pair.count == 2 {
            dict[pair[0]] = pair[1]
        }
    }
    

    【讨论】:

      【解决方案3】:

      详情

      • 斯威夫特 5
      • Xcode 版本 10.2.1 (10E1001)

      解决方案

      import Foundation
      
      // MARK: - [URLQueryItem] to [String: Any]
      
      extension Array where Element == URLQueryItem {
          func toDictionary() -> [String: Any] {
              var dictionary = [String: Any]()
              for queryItem in self {
                  guard let value = queryItem.value?.toCorrectType() else { continue }
                  if queryItem.name.contains("[]") {
                      let key = queryItem.name.replacingOccurrences(of: "[]", with: "")
                      let array = dictionary[key] as? [Any] ?? []
                      dictionary[key] = array + [value]
                  } else {
                      dictionary[queryItem.name] = value
                  }
              }
              return dictionary
          }
      }
      
      extension String {
      
          // MARK: - String to [URLQueryItem]
      
          func toURLQueryItems() -> [URLQueryItem]? {
              guard let urlString = self.removingPercentEncoding, let url = URL(string: urlString) else { return nil }
              if let querItems = url.toQueryItems() { return querItems }
              var urlComponents = URLComponents()
              urlComponents.query = urlString
              return urlComponents.queryItems
          }
      
          // MARK: - attempt to cast string to correct type (int, bool...)
      
          func toCorrectType() -> Any {
              let types: [LosslessStringConvertible.Type] = [Bool.self, Int.self, Double.self]
              func cast<T>(to: T) -> Any? { return (to.self as? LosslessStringConvertible.Type)?.init(self) }
              for type in types { if let value = cast(to: type) { return value } }
              return self
          }
      }
      
      // MARK: - URL to [URLQueryItem]
      
      extension URL {
          func toQueryItems() -> [URLQueryItem]? { return URLComponents(url: self, resolvingAgainstBaseURL: false)?.queryItems }
      }
      
      // MARK: - create [URLQueryItem] from [AnyHashable: Any] or [any]
      
      extension URLQueryItem {
          private static var _bracketsString: String { return "[]" }
          static func create(from values: [Any], with key: String) -> [URLQueryItem] {
              let _key = key.contains(_bracketsString) ? key : key + _bracketsString
              return values.compactMap { value -> URLQueryItem? in
                  if value is [Any] || value is [AnyHashable: Any] { return nil }
                  return URLQueryItem(name: _key, value: value as? String ?? "\(value)")
              }
          }
      
          static func create(from values: [AnyHashable: Any]) -> [URLQueryItem] {
              return values.flatMap { element -> [URLQueryItem] in
                  if element.value is [AnyHashable: Any] { return [] }
                  let key = element.key as? String ?? "String"
                  if let values = element.value as? [Any] { return URLQueryItem.create(from: values, with: key) }
                  return [URLQueryItem(name: key, value: element.value as? String ?? "\(element.value)")]
              }
          }
      }
      
       // MARK: - [AnyHashable: Any] to [URLQueryItem]
      
      extension Dictionary where Value: Any {
          func toURLQueryItems() -> [URLQueryItem] { return URLQueryItem.create(from: self) }
      }
      

      用法

      url.toQueryItems()
      url.toQueryItems()?.toDictionary()
      
      urlString.toURLQueryItems()
      urlString.toURLQueryItems()?.toDictionary()
      
      urlQueryString?.toURLQueryItems()
      urlQueryString?.toURLQueryItems()?.toDictionary()
      
      let dictionary = ["aaa": [1234], "bbb": ["a", "b", "c"]]
      dictionary.toURLQueryItems()
      

      完整样本

      var urlString = "https://example.com/l57?condition%5B%5D=31&brand%5B%5D=289&brand%5B%5D=291&brand%5B%5D=32&year%5B%5D=23259&year%5B%5D=23757&ships_from_region%5B%5D=23684&ships_from_region%5B%5D=23683"
      let url = URL(string: urlString)!
      var urlQueryString = url.query
      
      print("URL:\n\(urlString)")
      print("URL query string:\n\(String(describing: urlQueryString))\n")
      
      print("get [URLQueryItem] from URL:\n\(String(describing: url.toQueryItems()))\n")
      print("get [String: Any] from URL:\n\(String(describing: url.toQueryItems()?.toDictionary()))\n")
      
      print("get [URLQueryItem] from url string (absoluteString):\n\(String(describing: urlString.toURLQueryItems()))\n")
      print("get [String:Any] from url string (absoluteString):\n\(String(describing: urlString.toURLQueryItems()?.toDictionary()))\n")
      
      print("get [URLQueryItem] from url string (only query):\n\(String(describing: urlQueryString?.toURLQueryItems()))\n")
      print("get [String:Any] from url string (only query):\n\(String(describing: urlQueryString?.toURLQueryItems()?.toDictionary()))\n")
      
      var dict =  [String: Any]()
      dict = ["aaa": [1234], "bbb": [1234: 22], "ccc": ["a", "b", "c"], "ddd": [[1,2,3], [4,5,6]], "eee[]": [1,2,4], "fff": "value", "ggg": 123]
      print("Dict: \(dict)")
      print("Dict to [URLQueryItem]: \(dict.toURLQueryItems())")
      print("Dict to query oriented dictionary: \(dict.toURLQueryItems().toDictionary())")
      

      完整样本输出

      URL:
      https://example.com/l57?condition%5B%5D=31&brand%5B%5D=289&brand%5B%5D=291&brand%5B%5D=32&year%5B%5D=23259&year%5B%5D=23757&ships_from_region%5B%5D=23684&ships_from_region%5B%5D=23683
      URL query string:
      Optional("condition%5B%5D=31&brand%5B%5D=289&brand%5B%5D=291&brand%5B%5D=32&year%5B%5D=23259&year%5B%5D=23757&ships_from_region%5B%5D=23684&ships_from_region%5B%5D=23683")
      
      get [URLQueryItem] from URL:
      Optional([condition[]=31, brand[]=289, brand[]=291, brand[]=32, year[]=23259, year[]=23757, ships_from_region[]=23684, ships_from_region[]=23683])
      
      get [String: Any] from URL:
      Optional(["condition": [31], "ships_from_region": [23684, 23683], "year": [23259, 23757], "brand": [289, 291, 32]])
      
      get [URLQueryItem] from url string (absoluteString):
      Optional([condition[]=31, brand[]=289, brand[]=291, brand[]=32, year[]=23259, year[]=23757, ships_from_region[]=23684, ships_from_region[]=23683])
      
      get [String:Any] from url string (absoluteString):
      Optional(["year": [23259, 23757], "ships_from_region": [23684, 23683], "condition": [31], "brand": [289, 291, 32]])
      
      get [URLQueryItem] from url string (only query):
      Optional([condition[]=31, brand[]=289, brand[]=291, brand[]=32, year[]=23259, year[]=23757, ships_from_region[]=23684, ships_from_region[]=23683])
      
      get [String:Any] from url string (only query):
      Optional(["condition": [31], "brand": [289, 291, 32], "ships_from_region": [23684, 23683], "year": [23259, 23757]])
      
      Dict: ["ccc": ["a", "b", "c"], "eee[]": [1, 2, 4], "bbb": [1234: 22], "fff": "value", "ddd": [[1, 2, 3], [4, 5, 6]], "aaa": [1234], "ggg": 123]
      Dict to [URLQueryItem]: [ccc[]=a, ccc[]=b, ccc[]=c, ggg=123, fff=value, aaa[]=1234, eee[]=1, eee[]=2, eee[]=4]
      Dict to query oriented dictionary: ["ccc": ["a", "b", "c"], "aaa": [1234], "fff": "value", "eee": [1, 2, 4], "ggg": 123]
      

      【讨论】:

        【解决方案4】:

        使用 .first(where:)

                guard let query = components.queryItems else {
                    return
                }
                
                guard let accessToken = query.first(where: { $0.name == "access_token" }) else {
                    return
                }
        

        【讨论】:

          【解决方案5】:

          或者试试这个:

          extension URL {
              var queryDictionary: [String: AnyObject]? {
                  return URLComponents(url: self, resolvingAgainstBaseURL: false)?
                      .queryItems?
                      .reduce([:], combine: { (lastResult: [String: AnyObject], queryItem) -> [String: AnyObject] in
                          var result = lastResult
                          if queryItem.value?.containsString(",") ?? false {
                              let array = queryItem.value?.componentsSeparatedByString(",")
          
                              result[queryItem.name] = array
                          }
                          else {
                              result[queryItem.name] = queryItem.value
                          }
          
                          return result
                      })
              }
          }
          

          【讨论】:

            猜你喜欢
            • 2016-11-02
            • 2015-11-05
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-06-19
            相关资源
            最近更新 更多