mirror of
https://github.com/samsonjs/Osiris.git
synced 2026-03-25 08:55:48 +00:00
80 lines
3.2 KiB
Swift
80 lines
3.2 KiB
Swift
//
|
|
// Lifted from Alamofire (ParameterEncoding.swift): https://github.com/Alamofire/Alamofire
|
|
//
|
|
|
|
import Foundation
|
|
|
|
final class FormEncoder {
|
|
class func encode(_ parameters: [String: Any]) -> String {
|
|
var components: [(String, String)] = []
|
|
|
|
for key in parameters.keys.sorted(by: <) {
|
|
let value = parameters[key]!
|
|
components += pairs(from: key, value: value)
|
|
}
|
|
return components.map { "\($0)=\($1)" }.joined(separator: "&")
|
|
}
|
|
|
|
/// Creates percent-escaped, URL encoded query string components from the given key-value pair using recursion.
|
|
///
|
|
/// - parameter key: The key of the query component.
|
|
/// - parameter value: The value of the query component.
|
|
///
|
|
/// - returns: The percent-escaped, URL encoded query string components.
|
|
static func pairs(from key: String, value: Any) -> [(String, String)] {
|
|
var components: [(String, String)] = []
|
|
|
|
if let dictionary = value as? [String: Any] {
|
|
for (nestedKey, value) in dictionary {
|
|
components += pairs(from: "\(key)[\(nestedKey)]", value: value)
|
|
}
|
|
}
|
|
else if let array = value as? [Any] {
|
|
for value in array {
|
|
components += pairs(from: "\(key)[]", value: value)
|
|
}
|
|
}
|
|
else if let value = value as? NSNumber {
|
|
if value.isBool {
|
|
components.append((escape(key), escape((value.boolValue ? "1" : "0"))))
|
|
}
|
|
else {
|
|
components.append((escape(key), escape("\(value)")))
|
|
}
|
|
}
|
|
else if let bool = value as? Bool {
|
|
components.append((escape(key), escape((bool ? "1" : "0"))))
|
|
}
|
|
else {
|
|
components.append((escape(key), escape("\(value)")))
|
|
}
|
|
|
|
return components
|
|
}
|
|
|
|
/// Returns a percent-escaped string following RFC 3986 for a query string key or value.
|
|
///
|
|
/// RFC 3986 states that the following characters are "reserved" characters.
|
|
///
|
|
/// - General Delimiters: ":", "#", "[", "]", "@", "?", "/"
|
|
/// - Sub-Delimiters: "!", "$", "&", "'", "(", ")", "*", "+", ",", ";", "="
|
|
///
|
|
/// In RFC 3986 - Section 3.4, it states that the "?" and "/" characters should not be escaped to allow
|
|
/// query strings to include a URL. Therefore, all "reserved" characters with the exception of "?" and "/"
|
|
/// should be percent-escaped in the query string.
|
|
///
|
|
/// - parameter string: The string to be percent-escaped.
|
|
///
|
|
/// - returns: The percent-escaped string.
|
|
private static func escape(_ string: String) -> String {
|
|
let generalDelimitersToEncode = ":#[]@" // does not include "?" or "/" due to RFC 3986 - Section 3.4
|
|
let subDelimitersToEncode = "!$&'()*+,;="
|
|
|
|
var allowedCharacterSet = CharacterSet.urlQueryAllowed
|
|
allowedCharacterSet.remove(charactersIn: "\(generalDelimitersToEncode)\(subDelimitersToEncode)")
|
|
|
|
// FIXME: should we fail instead of falling back the unescaped string here? probably...
|
|
let escaped = string.addingPercentEncoding(withAllowedCharacters: allowedCharacterSet) ?? string
|
|
return escaped
|
|
}
|
|
}
|