@@ -42,8 +42,14 @@ extension URL {
42
42
return
43
43
}
44
44
45
- let currentQueryItems = components. queryItems ?? [ ]
46
- components. queryItems = currentQueryItems + queryItems
45
+ let currentQueryItems = components. percentEncodedQueryItems ?? [ ]
46
+
47
+ components. percentEncodedQueryItems = currentQueryItems + queryItems. map {
48
+ URLQueryItem (
49
+ name: escape ( $0. name) ,
50
+ value: $0. value. map ( escape)
51
+ )
52
+ }
47
53
48
54
if let newURL = components. url {
49
55
self = newURL
@@ -56,3 +62,27 @@ extension URL {
56
62
return url
57
63
}
58
64
}
65
+
66
+ func escape( _ string: String ) -> String {
67
+ string. addingPercentEncoding ( withAllowedCharacters: . sbURLQueryAllowed) ?? string
68
+ }
69
+
70
+ extension CharacterSet {
71
+ /// Creates a CharacterSet from RFC 3986 allowed characters.
72
+ ///
73
+ /// RFC 3986 states that the following characters are "reserved" characters.
74
+ ///
75
+ /// - General Delimiters: ":", "#", "[", "]", "@", "?", "/"
76
+ /// - Sub-Delimiters: "!", "$", "&", "'", "(", ")", "*", "+", ",", ";", "="
77
+ ///
78
+ /// In RFC 3986 - Section 3.4, it states that the "?" and "/" characters should not be escaped to allow
79
+ /// query strings to include a URL. Therefore, all "reserved" characters with the exception of "?" and "/"
80
+ /// should be percent-escaped in the query string.
81
+ static let sbURLQueryAllowed : CharacterSet = {
82
+ let generalDelimitersToEncode = " :#[]@ " // does not include "?" or "/" due to RFC 3986 - Section 3.4
83
+ let subDelimitersToEncode = " !$&'()*+,;= "
84
+ let encodableDelimiters = CharacterSet ( charactersIn: " \( generalDelimitersToEncode) \( subDelimitersToEncode) " )
85
+
86
+ return CharacterSet . urlQueryAllowed. subtracting ( encodableDelimiters)
87
+ } ( )
88
+ }
0 commit comments