forked from ishkawa/APIKit
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathURLEncodedSerialization.swift
More file actions
98 lines (75 loc) · 3.44 KB
/
Copy pathURLEncodedSerialization.swift
File metadata and controls
98 lines (75 loc) · 3.44 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
import Foundation
private func escape(string: String) -> String {
// Reserved characters defined by RFC 3986
// Reference: https://www.ietf.org/rfc/rfc3986.txt
let generalDelimiters = ":#[]@"
let subDelimiters = "!$&'()*+,;="
let reservedCharacters = generalDelimiters + subDelimiters
let allowedCharacterSet = NSMutableCharacterSet()
allowedCharacterSet.formUnionWithCharacterSet(NSCharacterSet.URLQueryAllowedCharacterSet())
allowedCharacterSet.removeCharactersInString(reservedCharacters)
// Crashes due to internal bug in iOS 7 ~ iOS 8.2.
// References:
// - https://github.com/Alamofire/Alamofire/issues/206
// - https://github.com/AFNetworking/AFNetworking/issues/3028
// return string.stringByAddingPercentEncodingWithAllowedCharacters(allowedCharacterSet) ?? string
let batchSize = 50
var index = string.startIndex
var escaped = ""
while index != string.endIndex {
let startIndex = index
let endIndex = index.advancedBy(batchSize, limit: string.endIndex)
let range = Range(start: startIndex, end: endIndex)
let substring = string.substringWithRange(range)
escaped += substring.stringByAddingPercentEncodingWithAllowedCharacters(allowedCharacterSet) ?? substring
index = endIndex
}
return escaped
}
private func unescape(string: String) -> String {
return CFURLCreateStringByReplacingPercentEscapes(nil, string, nil) as String
}
public final class URLEncodedSerialization {
public enum Error: ErrorType {
case CannotGetStringFromData(NSData, NSStringEncoding)
case CannotGetDataFromString(String, NSStringEncoding)
case CannotCastObjectToDictionary(AnyObject)
case InvalidFormatString(String)
}
/// - Throws: URLEncodedSerialization.Error
public static func objectFromData(data: NSData, encoding: NSStringEncoding) throws -> [String: String] {
guard let string = String(data: data, encoding: encoding) else {
throw Error.CannotGetStringFromData(data, encoding)
}
var dictionary = [String: String]()
for pair in string.componentsSeparatedByString("&") {
let contents = pair.componentsSeparatedByString("=")
guard contents.count == 2 else {
throw Error.InvalidFormatString(string)
}
dictionary[contents[0]] = unescape(contents[1])
}
return dictionary
}
/// - Throws: URLEncodedSerialization.Error
public static func dataFromObject(object: AnyObject, encoding: NSStringEncoding) throws -> NSData {
guard let dictionary = object as? [String: AnyObject] else {
throw Error.CannotCastObjectToDictionary(object)
}
let string = stringFromDictionary(dictionary)
guard let data = string.dataUsingEncoding(encoding, allowLossyConversion: false) else {
throw Error.CannotGetDataFromString(string, encoding)
}
return data
}
public static func stringFromDictionary(dictionary: [String: AnyObject]) -> String {
let pairs = dictionary.map { key, value -> String in
if value is NSNull {
return "\(escape(key))"
}
let valueAsString = (value as? String) ?? "\(value)"
return "\(escape(key))=\(escape(valueAsString))"
}
return pairs.joinWithSeparator("&")
}
}