Is there seriously not a way natively to URL Encode the value of a query string parameter that has a "+" character in it?
e.g.
me+blah#domain.net
to
me%2Bblah%40#domain.net?
I tried solutions like posted in these other questions, but those do not properly encode that email.
Swift - encode URL
How do I URL encode a string
A swift solution would be preferred as that is what I am working in at the moment, but I am capable of translating Objective-C Code to swift code for the most part.
Specifically I am trying to x-www-form-urlencoded encode query string values in the body of POST request.
let email = "me+blah#domain.net"
let output = CFURLCreateStringByAddingPercentEscapes(nil, email as NSString, nil, ":/?#!$&'()*+,;=" as NSString, CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncoding))
// output = "me%2Bblah%40domain.net"
CFURLCreateStringByAddingPercentEscapes doesn't escape + or # by default, but you can specify it (as I did along with other characters, in the ":/?#!$&'()*+,;=" string).
Edit: If you want output to be a Swift string:
let output = (CFURLCreateStringByAddingPercentEscapes(nil, email as NSString, nil, ":/?#!$&'()*+,;=" as NSString, CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncoding)) as NSString) as String
println(("me+blah#domain.net" as NSString)
.stringByAddingPercentEncodingWithAllowedCharacters(
NSCharacterSet.alphanumericCharacterSet()))
Output:
Optional("me%2Bblah%40domain%2Enet")
In Objective-C:
NSString *encodedString =
["me+blah#domain.net" stringByAddingPercentEncodingWithAllowedCharacters:
[NSCharacterSet alphanumericCharacterSet]];
Related
Why does converting a String to an URL in Swift 4.2 and then converting the URL back to a String using url.path change the encoding of special characters like german umlauts (ä, ö, ü), even if I use a utf-8 encoding?
I wrote some sample code to show my problem. I encoded the strings to base64 in order to show that there is a difference.
I also have a similar unsolved problem with special characters and swift here.
Sample Code
let string = "/path/to/file"
let stringUmlauts = "/path/to/file/with/umlauts/testäöü"
let base64 = Data(string.utf8).base64EncodedString()
let base64Umlauts = Data(stringUmlauts.utf8).base64EncodedString()
print(base64, base64Umlauts)
let url = URL(fileURLWithPath: string)
let urlUmlauts = URL(fileURLWithPath: stringUmlauts)
let base64Url = Data(url.path.utf8).base64EncodedString()
let base64UrlUmlauts = Data(urlUmlauts.path.utf8).base64EncodedString()
print(base64Url, base64UrlUmlauts)
Output
The base64 and base64Url string stay the same but the base64Umlauts and the base64UrlUmlauts are different.
"L3BhdGgvdG8vZmlsZQ==" for base64
"L3BhdGgvdG8vZmlsZQ==" for base64Url
"L3BhdGgvdG8vZmlsZS93aXRoL3VtbGF1dHMvdGVzdMOkw7bDvA==" for base64Umlauts
"L3BhdGgvdG8vZmlsZS93aXRoL3VtbGF1dHMvdGVzdGHMiG/MiHXMiA==" for base64UrlUmlauts
When I put the base64Umlauts and base64UrlUmlauts strings into an online Base64 decoder, they both show /path/to/file/with/umlauts/testäöü, but the ä, ö, ü are different (not visually).
stringUmlauts.utf8 uses the Unicode characters äöü.
But urlUmlauts.path.utf8 uses the Unicode characters aou each followed by the combining ¨.
This is why you get different base64 encoding - the characters look the same but are actually encoded differently.
What's really interesting is that Array(stringUmlauts) and Array(urlUmlauts.path) are the same. The difference doesn't appear until you perform the UTF-8 encoding of the otherwise exact same String values.
Since the base64 encoding is irrelevant, here's a more concise test:
let stringUmlauts = "/path/to/file/with/umlauts/testäöü"
let urlUmlauts = URL(fileURLWithPath: stringUmlauts)
print(stringUmlauts, urlUmlauts.path) // Show the same
let rawStr = stringUmlauts
let urlStr = urlUmlauts.path
print(rawStr == urlStr) // true
print(Array(rawStr) == Array(urlStr)) // true
print(Array(rawStr.utf8) == Array(urlStr.utf8)) // false!!!
So how is the UTF-8 encoding of two equal strings different?
One solution to this is to use precomposedStringWithCanonicalMapping on the result of path.
let urlStr = urlUmlauts.path.precomposedStringWithCanonicalMapping
Now you get true from:
print(Array(rawStr.utf8) == Array(urlStr.utf8)) // now true
How can a Unicode escape sequence be added to a string in Localizeable.strings file if the string is casted to NSString?
Here is one (ugly) example:
// Localized string: "\u{200F}Number %#" = "\u{200E}Number %#";
let string = NSMutableAttributedString(string: NSString(format: NSLocalizedString("Number %#", comment: "") as NSString, aNumber as NSNumber)) as String
From this question I understand that the problem is the incompatible escape sequences of Localizeable.strings and NSString.
Adding the unicode characters directly is Localizeable.strings file is not an option because I need to insert bidirectional semantics markers that are not printable characters. They would also be lost in most translation programs.
How can I work around that?
Currently i am using the following way to encode the url
urlAndMethod.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())!
After encoding
http://103.50.154.52:8383/api/master/insta-list?category=Postpaid%20Mobile%20CDMA%20&%20Landline
How do i handle the special characters including '&'
You can use .urlHostAllowed characterset.
let escapedString = someString.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)
What I am doing is shipping a JSON structured string on NSULRRequest, getting rid of the urlscheme prefix, serialize it to JSON and then whatever I can do with it.
urlscheme://{"Type":"photo","Name":"Photo13"}
So I changed NSURLRequest to string by doing
let url = request.URL
let string = "\(url)"
print(string)
but the string appears like
urlscheme://%7B%22Type%22:%22photo%22,%22Name%22:%22Photo13%22%7D
So I have to change it by invoking stringByReplacingOccurrencesOfString over and over on all possible changes.
replacedString = replacedString.stringByReplacingOccurrencesOfString("%7B",
withString: "{",
options: NSStringCompareOptions.LiteralSearch,
range: nil)
.
..
...
Is there a better way to do this in Swift?
What you've got there is a percent encode string. This is done because certain characters have special meanings within a URL. The NSString method stringByRemovingPercentEncoding will convert a percent encode string back to a normal string for you.
See this answer
I am trying to create a percent encoded string in Swift so I can safely send text as a GET request. I found some Objective C code which I am trying to convert to Swift. I've written the following Swift code:
CFURLCreateStringByAddingPercentEscapes(nil,
CFStringRef(encodedString), nil,
CFStringRef("/%&=?$#+-~#<>|\\*,.()[]{}^!"),
kCFStringEncodingUTF8)
There is no kCFStringEncodingUTF8 in Swift ... If you right click the CFStringEncodings source you see there is a million things in there but no UTF8. I don't understand. How can I use the UTF8 string encoding in this situation?
EDIT : I found a way to encode a string but I still don't understand what happened to kCFStringEncodingUTF8
The UTF-8 string encoding is defined as
enum CFStringBuiltInEncodings : CFStringEncoding {
// ...
case UTF8 /* kTextEncodingUnicodeDefault + kUnicodeUTF8Format */
// ...
}
and can be used as
let orig = "a/b(c)"
let escaped = CFURLCreateStringByAddingPercentEscapes(nil, orig, nil,
"/%&=?$#+-~#<>|\\*,.()[]{}^!",
CFStringBuiltInEncodings.UTF8.rawValue)
println(escaped)
// a%2Fb%28c%29
As mentioned by #Zaph in a comment, stringByAddingPercentEncodingWithAllowedCharacters might
be easier to use:
let charset = NSCharacterSet(charactersInString: "/%&=?$#+-~#<>|\\*,.()[]{}^!").invertedSet
let escaped = orig.stringByAddingPercentEncodingWithAllowedCharacters(charset)
or use one of the pre-defined character sets from
Creating a Character Set for URL Encoding.
kCFStringEncodingUTF8 has been replace in Swift with: UTF8.
There is a new NSString method added in iOS7 that takes a character set and a number of specialized character sets have been added and of course you can create specialized character sets. There is very little reason to use CFURLCreateStringByAddingPercentEscapes any more. See this: SO Answer