123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976 |
- import Foundation
- public final class URLEncodedFormEncoder {
-
- public enum ArrayEncoding {
-
- case brackets
-
- case noBrackets
-
-
-
-
- func encode(_ key: String) -> String {
- switch self {
- case .brackets: return "\(key)[]"
- case .noBrackets: return key
- }
- }
- }
-
- public enum BoolEncoding {
-
- case numeric
-
- case literal
-
-
-
-
-
- func encode(_ value: Bool) -> String {
- switch self {
- case .numeric: return value ? "1" : "0"
- case .literal: return value ? "true" : "false"
- }
- }
- }
-
- public enum DataEncoding {
-
- case deferredToData
-
- case base64
-
- case custom((Data) throws -> String)
-
-
-
-
-
-
- func encode(_ data: Data) throws -> String? {
- switch self {
- case .deferredToData: return nil
- case .base64: return data.base64EncodedString()
- case let .custom(encoding): return try encoding(data)
- }
- }
- }
-
- public enum DateEncoding {
-
- private static let iso8601Formatter: ISO8601DateFormatter = {
- let formatter = ISO8601DateFormatter()
- formatter.formatOptions = .withInternetDateTime
- return formatter
- }()
-
- case deferredToDate
-
- case secondsSince1970
-
- case millisecondsSince1970
-
- case iso8601
-
- case formatted(DateFormatter)
-
- case custom((Date) throws -> String)
-
-
-
-
-
-
- func encode(_ date: Date) throws -> String? {
- switch self {
- case .deferredToDate:
- return nil
- case .secondsSince1970:
- return String(date.timeIntervalSince1970)
- case .millisecondsSince1970:
- return String(date.timeIntervalSince1970 * 1000.0)
- case .iso8601:
- return DateEncoding.iso8601Formatter.string(from: date)
- case let .formatted(formatter):
- return formatter.string(from: date)
- case let .custom(closure):
- return try closure(date)
- }
- }
- }
-
-
-
-
- public enum KeyEncoding {
-
- case useDefaultKeys
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- case convertToSnakeCase
-
-
- case convertToKebabCase
-
-
- case capitalized
-
-
- case uppercased
-
-
- case lowercased
-
- case custom((String) -> String)
- func encode(_ key: String) -> String {
- switch self {
- case .useDefaultKeys: return key
- case .convertToSnakeCase: return convertToSnakeCase(key)
- case .convertToKebabCase: return convertToKebabCase(key)
- case .capitalized: return String(key.prefix(1).uppercased() + key.dropFirst())
- case .uppercased: return key.uppercased()
- case .lowercased: return key.lowercased()
- case let .custom(encoding): return encoding(key)
- }
- }
- private func convertToSnakeCase(_ key: String) -> String {
- convert(key, usingSeparator: "_")
- }
- private func convertToKebabCase(_ key: String) -> String {
- convert(key, usingSeparator: "-")
- }
- private func convert(_ key: String, usingSeparator separator: String) -> String {
- guard !key.isEmpty else { return key }
- var words: [Range<String.Index>] = []
-
-
-
-
-
-
-
-
- var wordStart = key.startIndex
- var searchRange = key.index(after: wordStart)..<key.endIndex
-
- while let upperCaseRange = key.rangeOfCharacter(from: CharacterSet.uppercaseLetters, options: [], range: searchRange) {
- let untilUpperCase = wordStart..<upperCaseRange.lowerBound
- words.append(untilUpperCase)
-
- searchRange = upperCaseRange.lowerBound..<searchRange.upperBound
- guard let lowerCaseRange = key.rangeOfCharacter(from: CharacterSet.lowercaseLetters, options: [], range: searchRange) else {
-
- wordStart = searchRange.lowerBound
- break
- }
-
-
-
- let nextCharacterAfterCapital = key.index(after: upperCaseRange.lowerBound)
- if lowerCaseRange.lowerBound == nextCharacterAfterCapital {
-
-
- wordStart = upperCaseRange.lowerBound
- } else {
-
- let beforeLowerIndex = key.index(before: lowerCaseRange.lowerBound)
- words.append(upperCaseRange.lowerBound..<beforeLowerIndex)
-
- wordStart = beforeLowerIndex
- }
- searchRange = lowerCaseRange.upperBound..<searchRange.upperBound
- }
- words.append(wordStart..<searchRange.upperBound)
- let result = words.map { range in
- key[range].lowercased()
- }.joined(separator: separator)
- return result
- }
- }
-
- public enum SpaceEncoding {
-
- case percentEscaped
-
- case plusReplaced
-
-
-
-
-
- func encode(_ string: String) -> String {
- switch self {
- case .percentEscaped: return string.replacingOccurrences(of: " ", with: "%20")
- case .plusReplaced: return string.replacingOccurrences(of: " ", with: "+")
- }
- }
- }
-
- public enum Error: Swift.Error {
-
- case invalidRootObject(String)
- var localizedDescription: String {
- switch self {
- case let .invalidRootObject(object):
- return "URLEncodedFormEncoder requires keyed root object. Received \(object) instead."
- }
- }
- }
-
-
-
-
-
- public let alphabetizeKeyValuePairs: Bool
-
- public let arrayEncoding: ArrayEncoding
-
- public let boolEncoding: BoolEncoding
-
- public let dataEncoding: DataEncoding
-
- public let dateEncoding: DateEncoding
-
- public let keyEncoding: KeyEncoding
-
- public let spaceEncoding: SpaceEncoding
-
- public var allowedCharacters: CharacterSet
-
-
-
-
-
-
-
-
-
-
-
-
- public init(alphabetizeKeyValuePairs: Bool = true,
- arrayEncoding: ArrayEncoding = .brackets,
- boolEncoding: BoolEncoding = .numeric,
- dataEncoding: DataEncoding = .base64,
- dateEncoding: DateEncoding = .deferredToDate,
- keyEncoding: KeyEncoding = .useDefaultKeys,
- spaceEncoding: SpaceEncoding = .percentEscaped,
- allowedCharacters: CharacterSet = .afURLQueryAllowed) {
- self.alphabetizeKeyValuePairs = alphabetizeKeyValuePairs
- self.arrayEncoding = arrayEncoding
- self.boolEncoding = boolEncoding
- self.dataEncoding = dataEncoding
- self.dateEncoding = dateEncoding
- self.keyEncoding = keyEncoding
- self.spaceEncoding = spaceEncoding
- self.allowedCharacters = allowedCharacters
- }
- func encode(_ value: Encodable) throws -> URLEncodedFormComponent {
- let context = URLEncodedFormContext(.object([]))
- let encoder = _URLEncodedFormEncoder(context: context,
- boolEncoding: boolEncoding,
- dataEncoding: dataEncoding,
- dateEncoding: dateEncoding)
- try value.encode(to: encoder)
- return context.component
- }
-
-
-
-
-
-
- public func encode(_ value: Encodable) throws -> String {
- let component: URLEncodedFormComponent = try encode(value)
- guard case let .object(object) = component else {
- throw Error.invalidRootObject("\(component)")
- }
- let serializer = URLEncodedFormSerializer(alphabetizeKeyValuePairs: alphabetizeKeyValuePairs,
- arrayEncoding: arrayEncoding,
- keyEncoding: keyEncoding,
- spaceEncoding: spaceEncoding,
- allowedCharacters: allowedCharacters)
- let query = serializer.serialize(object)
- return query
- }
-
-
-
-
-
-
-
-
- public func encode(_ value: Encodable) throws -> Data {
- let string: String = try encode(value)
- return Data(string.utf8)
- }
- }
- final class _URLEncodedFormEncoder {
- var codingPath: [CodingKey]
-
- var userInfo: [CodingUserInfoKey: Any] { [:] }
- let context: URLEncodedFormContext
- private let boolEncoding: URLEncodedFormEncoder.BoolEncoding
- private let dataEncoding: URLEncodedFormEncoder.DataEncoding
- private let dateEncoding: URLEncodedFormEncoder.DateEncoding
- init(context: URLEncodedFormContext,
- codingPath: [CodingKey] = [],
- boolEncoding: URLEncodedFormEncoder.BoolEncoding,
- dataEncoding: URLEncodedFormEncoder.DataEncoding,
- dateEncoding: URLEncodedFormEncoder.DateEncoding) {
- self.context = context
- self.codingPath = codingPath
- self.boolEncoding = boolEncoding
- self.dataEncoding = dataEncoding
- self.dateEncoding = dateEncoding
- }
- }
- extension _URLEncodedFormEncoder: Encoder {
- func container<Key>(keyedBy type: Key.Type) -> KeyedEncodingContainer<Key> where Key: CodingKey {
- let container = _URLEncodedFormEncoder.KeyedContainer<Key>(context: context,
- codingPath: codingPath,
- boolEncoding: boolEncoding,
- dataEncoding: dataEncoding,
- dateEncoding: dateEncoding)
- return KeyedEncodingContainer(container)
- }
- func unkeyedContainer() -> UnkeyedEncodingContainer {
- _URLEncodedFormEncoder.UnkeyedContainer(context: context,
- codingPath: codingPath,
- boolEncoding: boolEncoding,
- dataEncoding: dataEncoding,
- dateEncoding: dateEncoding)
- }
- func singleValueContainer() -> SingleValueEncodingContainer {
- _URLEncodedFormEncoder.SingleValueContainer(context: context,
- codingPath: codingPath,
- boolEncoding: boolEncoding,
- dataEncoding: dataEncoding,
- dateEncoding: dateEncoding)
- }
- }
- final class URLEncodedFormContext {
- var component: URLEncodedFormComponent
- init(_ component: URLEncodedFormComponent) {
- self.component = component
- }
- }
- enum URLEncodedFormComponent {
- typealias Object = [(key: String, value: URLEncodedFormComponent)]
- case string(String)
- case array([URLEncodedFormComponent])
- case object(Object)
-
- var array: [URLEncodedFormComponent]? {
- switch self {
- case let .array(array): return array
- default: return nil
- }
- }
-
- var object: Object? {
- switch self {
- case let .object(object): return object
- default: return nil
- }
- }
-
-
-
-
-
-
-
- public mutating func set(to value: URLEncodedFormComponent, at path: [CodingKey]) {
- set(&self, to: value, at: path)
- }
-
- private func set(_ context: inout URLEncodedFormComponent, to value: URLEncodedFormComponent, at path: [CodingKey]) {
- guard path.count >= 1 else {
- context = value
- return
- }
- let end = path[0]
- var child: URLEncodedFormComponent
- switch path.count {
- case 1:
- child = value
- case 2...:
- if let index = end.intValue {
- let array = context.array ?? []
- if array.count > index {
- child = array[index]
- } else {
- child = .array([])
- }
- set(&child, to: value, at: Array(path[1...]))
- } else {
- child = context.object?.first { $0.key == end.stringValue }?.value ?? .object(.init())
- set(&child, to: value, at: Array(path[1...]))
- }
- default: fatalError("Unreachable")
- }
- if let index = end.intValue {
- if var array = context.array {
- if array.count > index {
- array[index] = child
- } else {
- array.append(child)
- }
- context = .array(array)
- } else {
- context = .array([child])
- }
- } else {
- if var object = context.object {
- if let index = object.firstIndex(where: { $0.key == end.stringValue }) {
- object[index] = (key: end.stringValue, value: child)
- } else {
- object.append((key: end.stringValue, value: child))
- }
- context = .object(object)
- } else {
- context = .object([(key: end.stringValue, value: child)])
- }
- }
- }
- }
- struct AnyCodingKey: CodingKey, Hashable {
- let stringValue: String
- let intValue: Int?
- init?(stringValue: String) {
- self.stringValue = stringValue
- intValue = nil
- }
- init?(intValue: Int) {
- stringValue = "\(intValue)"
- self.intValue = intValue
- }
- init<Key>(_ base: Key) where Key: CodingKey {
- if let intValue = base.intValue {
- self.init(intValue: intValue)!
- } else {
- self.init(stringValue: base.stringValue)!
- }
- }
- }
- extension _URLEncodedFormEncoder {
- final class KeyedContainer<Key> where Key: CodingKey {
- var codingPath: [CodingKey]
- private let context: URLEncodedFormContext
- private let boolEncoding: URLEncodedFormEncoder.BoolEncoding
- private let dataEncoding: URLEncodedFormEncoder.DataEncoding
- private let dateEncoding: URLEncodedFormEncoder.DateEncoding
- init(context: URLEncodedFormContext,
- codingPath: [CodingKey],
- boolEncoding: URLEncodedFormEncoder.BoolEncoding,
- dataEncoding: URLEncodedFormEncoder.DataEncoding,
- dateEncoding: URLEncodedFormEncoder.DateEncoding) {
- self.context = context
- self.codingPath = codingPath
- self.boolEncoding = boolEncoding
- self.dataEncoding = dataEncoding
- self.dateEncoding = dateEncoding
- }
- private func nestedCodingPath(for key: CodingKey) -> [CodingKey] {
- codingPath + [key]
- }
- }
- }
- extension _URLEncodedFormEncoder.KeyedContainer: KeyedEncodingContainerProtocol {
- func encodeNil(forKey key: Key) throws {
- let context = EncodingError.Context(codingPath: codingPath,
- debugDescription: "URLEncodedFormEncoder cannot encode nil values.")
- throw EncodingError.invalidValue("\(key): nil", context)
- }
- func encode<T>(_ value: T, forKey key: Key) throws where T: Encodable {
- var container = nestedSingleValueEncoder(for: key)
- try container.encode(value)
- }
- func nestedSingleValueEncoder(for key: Key) -> SingleValueEncodingContainer {
- let container = _URLEncodedFormEncoder.SingleValueContainer(context: context,
- codingPath: nestedCodingPath(for: key),
- boolEncoding: boolEncoding,
- dataEncoding: dataEncoding,
- dateEncoding: dateEncoding)
- return container
- }
- func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer {
- let container = _URLEncodedFormEncoder.UnkeyedContainer(context: context,
- codingPath: nestedCodingPath(for: key),
- boolEncoding: boolEncoding,
- dataEncoding: dataEncoding,
- dateEncoding: dateEncoding)
- return container
- }
- func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer<NestedKey> where NestedKey: CodingKey {
- let container = _URLEncodedFormEncoder.KeyedContainer<NestedKey>(context: context,
- codingPath: nestedCodingPath(for: key),
- boolEncoding: boolEncoding,
- dataEncoding: dataEncoding,
- dateEncoding: dateEncoding)
- return KeyedEncodingContainer(container)
- }
- func superEncoder() -> Encoder {
- _URLEncodedFormEncoder(context: context,
- codingPath: codingPath,
- boolEncoding: boolEncoding,
- dataEncoding: dataEncoding,
- dateEncoding: dateEncoding)
- }
- func superEncoder(forKey key: Key) -> Encoder {
- _URLEncodedFormEncoder(context: context,
- codingPath: nestedCodingPath(for: key),
- boolEncoding: boolEncoding,
- dataEncoding: dataEncoding,
- dateEncoding: dateEncoding)
- }
- }
- extension _URLEncodedFormEncoder {
- final class SingleValueContainer {
- var codingPath: [CodingKey]
- private var canEncodeNewValue = true
- private let context: URLEncodedFormContext
- private let boolEncoding: URLEncodedFormEncoder.BoolEncoding
- private let dataEncoding: URLEncodedFormEncoder.DataEncoding
- private let dateEncoding: URLEncodedFormEncoder.DateEncoding
- init(context: URLEncodedFormContext,
- codingPath: [CodingKey],
- boolEncoding: URLEncodedFormEncoder.BoolEncoding,
- dataEncoding: URLEncodedFormEncoder.DataEncoding,
- dateEncoding: URLEncodedFormEncoder.DateEncoding) {
- self.context = context
- self.codingPath = codingPath
- self.boolEncoding = boolEncoding
- self.dataEncoding = dataEncoding
- self.dateEncoding = dateEncoding
- }
- private func checkCanEncode(value: Any?) throws {
- guard canEncodeNewValue else {
- let context = EncodingError.Context(codingPath: codingPath,
- debugDescription: "Attempt to encode value through single value container when previously value already encoded.")
- throw EncodingError.invalidValue(value as Any, context)
- }
- }
- }
- }
- extension _URLEncodedFormEncoder.SingleValueContainer: SingleValueEncodingContainer {
- func encodeNil() throws {
- try checkCanEncode(value: nil)
- defer { canEncodeNewValue = false }
- let context = EncodingError.Context(codingPath: codingPath,
- debugDescription: "URLEncodedFormEncoder cannot encode nil values.")
- throw EncodingError.invalidValue("nil", context)
- }
- func encode(_ value: Bool) throws {
- try encode(value, as: String(boolEncoding.encode(value)))
- }
- func encode(_ value: String) throws {
- try encode(value, as: value)
- }
- func encode(_ value: Double) throws {
- try encode(value, as: String(value))
- }
- func encode(_ value: Float) throws {
- try encode(value, as: String(value))
- }
- func encode(_ value: Int) throws {
- try encode(value, as: String(value))
- }
- func encode(_ value: Int8) throws {
- try encode(value, as: String(value))
- }
- func encode(_ value: Int16) throws {
- try encode(value, as: String(value))
- }
- func encode(_ value: Int32) throws {
- try encode(value, as: String(value))
- }
- func encode(_ value: Int64) throws {
- try encode(value, as: String(value))
- }
- func encode(_ value: UInt) throws {
- try encode(value, as: String(value))
- }
- func encode(_ value: UInt8) throws {
- try encode(value, as: String(value))
- }
- func encode(_ value: UInt16) throws {
- try encode(value, as: String(value))
- }
- func encode(_ value: UInt32) throws {
- try encode(value, as: String(value))
- }
- func encode(_ value: UInt64) throws {
- try encode(value, as: String(value))
- }
- private func encode<T>(_ value: T, as string: String) throws where T: Encodable {
- try checkCanEncode(value: value)
- defer { canEncodeNewValue = false }
- context.component.set(to: .string(string), at: codingPath)
- }
- func encode<T>(_ value: T) throws where T: Encodable {
- switch value {
- case let date as Date:
- guard let string = try dateEncoding.encode(date) else {
- try attemptToEncode(value)
- return
- }
- try encode(value, as: string)
- case let data as Data:
- guard let string = try dataEncoding.encode(data) else {
- try attemptToEncode(value)
- return
- }
- try encode(value, as: string)
- case let decimal as Decimal:
-
- try encode(value, as: String(describing: decimal))
- default:
- try attemptToEncode(value)
- }
- }
- private func attemptToEncode<T>(_ value: T) throws where T: Encodable {
- try checkCanEncode(value: value)
- defer { canEncodeNewValue = false }
- let encoder = _URLEncodedFormEncoder(context: context,
- codingPath: codingPath,
- boolEncoding: boolEncoding,
- dataEncoding: dataEncoding,
- dateEncoding: dateEncoding)
- try value.encode(to: encoder)
- }
- }
- extension _URLEncodedFormEncoder {
- final class UnkeyedContainer {
- var codingPath: [CodingKey]
- var count = 0
- var nestedCodingPath: [CodingKey] {
- codingPath + [AnyCodingKey(intValue: count)!]
- }
- private let context: URLEncodedFormContext
- private let boolEncoding: URLEncodedFormEncoder.BoolEncoding
- private let dataEncoding: URLEncodedFormEncoder.DataEncoding
- private let dateEncoding: URLEncodedFormEncoder.DateEncoding
- init(context: URLEncodedFormContext,
- codingPath: [CodingKey],
- boolEncoding: URLEncodedFormEncoder.BoolEncoding,
- dataEncoding: URLEncodedFormEncoder.DataEncoding,
- dateEncoding: URLEncodedFormEncoder.DateEncoding) {
- self.context = context
- self.codingPath = codingPath
- self.boolEncoding = boolEncoding
- self.dataEncoding = dataEncoding
- self.dateEncoding = dateEncoding
- }
- }
- }
- extension _URLEncodedFormEncoder.UnkeyedContainer: UnkeyedEncodingContainer {
- func encodeNil() throws {
- let context = EncodingError.Context(codingPath: codingPath,
- debugDescription: "URLEncodedFormEncoder cannot encode nil values.")
- throw EncodingError.invalidValue("nil", context)
- }
- func encode<T>(_ value: T) throws where T: Encodable {
- var container = nestedSingleValueContainer()
- try container.encode(value)
- }
- func nestedSingleValueContainer() -> SingleValueEncodingContainer {
- defer { count += 1 }
- return _URLEncodedFormEncoder.SingleValueContainer(context: context,
- codingPath: nestedCodingPath,
- boolEncoding: boolEncoding,
- dataEncoding: dataEncoding,
- dateEncoding: dateEncoding)
- }
- func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer<NestedKey> where NestedKey: CodingKey {
- defer { count += 1 }
- let container = _URLEncodedFormEncoder.KeyedContainer<NestedKey>(context: context,
- codingPath: nestedCodingPath,
- boolEncoding: boolEncoding,
- dataEncoding: dataEncoding,
- dateEncoding: dateEncoding)
- return KeyedEncodingContainer(container)
- }
- func nestedUnkeyedContainer() -> UnkeyedEncodingContainer {
- defer { count += 1 }
- return _URLEncodedFormEncoder.UnkeyedContainer(context: context,
- codingPath: nestedCodingPath,
- boolEncoding: boolEncoding,
- dataEncoding: dataEncoding,
- dateEncoding: dateEncoding)
- }
- func superEncoder() -> Encoder {
- defer { count += 1 }
- return _URLEncodedFormEncoder(context: context,
- codingPath: codingPath,
- boolEncoding: boolEncoding,
- dataEncoding: dataEncoding,
- dateEncoding: dateEncoding)
- }
- }
- final class URLEncodedFormSerializer {
- private let alphabetizeKeyValuePairs: Bool
- private let arrayEncoding: URLEncodedFormEncoder.ArrayEncoding
- private let keyEncoding: URLEncodedFormEncoder.KeyEncoding
- private let spaceEncoding: URLEncodedFormEncoder.SpaceEncoding
- private let allowedCharacters: CharacterSet
- init(alphabetizeKeyValuePairs: Bool,
- arrayEncoding: URLEncodedFormEncoder.ArrayEncoding,
- keyEncoding: URLEncodedFormEncoder.KeyEncoding,
- spaceEncoding: URLEncodedFormEncoder.SpaceEncoding,
- allowedCharacters: CharacterSet) {
- self.alphabetizeKeyValuePairs = alphabetizeKeyValuePairs
- self.arrayEncoding = arrayEncoding
- self.keyEncoding = keyEncoding
- self.spaceEncoding = spaceEncoding
- self.allowedCharacters = allowedCharacters
- }
- func serialize(_ object: URLEncodedFormComponent.Object) -> String {
- var output: [String] = []
- for (key, component) in object {
- let value = serialize(component, forKey: key)
- output.append(value)
- }
- output = alphabetizeKeyValuePairs ? output.sorted() : output
- return output.joinedWithAmpersands()
- }
- func serialize(_ component: URLEncodedFormComponent, forKey key: String) -> String {
- switch component {
- case let .string(string): return "\(escape(keyEncoding.encode(key)))=\(escape(string))"
- case let .array(array): return serialize(array, forKey: key)
- case let .object(object): return serialize(object, forKey: key)
- }
- }
- func serialize(_ object: URLEncodedFormComponent.Object, forKey key: String) -> String {
- var segments: [String] = object.map { subKey, value in
- let keyPath = "[\(subKey)]"
- return serialize(value, forKey: key + keyPath)
- }
- segments = alphabetizeKeyValuePairs ? segments.sorted() : segments
- return segments.joinedWithAmpersands()
- }
- func serialize(_ array: [URLEncodedFormComponent], forKey key: String) -> String {
- var segments: [String] = array.map { component in
- let keyPath = arrayEncoding.encode(key)
- return serialize(component, forKey: keyPath)
- }
- segments = alphabetizeKeyValuePairs ? segments.sorted() : segments
- return segments.joinedWithAmpersands()
- }
- func escape(_ query: String) -> String {
- var allowedCharactersWithSpace = allowedCharacters
- allowedCharactersWithSpace.insert(charactersIn: " ")
- let escapedQuery = query.addingPercentEncoding(withAllowedCharacters: allowedCharactersWithSpace) ?? query
- let spaceEncodedQuery = spaceEncoding.encode(escapedQuery)
- return spaceEncodedQuery
- }
- }
- extension Array where Element == String {
- func joinedWithAmpersands() -> String {
- joined(separator: "&")
- }
- }
- extension CharacterSet {
-
-
-
-
-
-
-
-
-
-
- public static let afURLQueryAllowed: CharacterSet = {
- let generalDelimitersToEncode = ":#[]@"
- let subDelimitersToEncode = "!$&'()*+,;="
- let encodableDelimiters = CharacterSet(charactersIn: "\(generalDelimitersToEncode)\(subDelimitersToEncode)")
- return CharacterSet.urlQueryAllowed.subtracting(encodableDelimiters)
- }()
- }
|