Can iOS URL support unicode characters within top level domain? - ios

I'm building an iOS app that takes urls as input.
Unicode characters are valid for a tld but when I instantiate a valid URL that contains unicode characters NSURL returns nil.
Is this even possible?
swift eg.
URL(string: "http://➡.ws/䨹")

How to use special characters in URL (Swift 3) :
let myUrl = "http://➡.ws/䨹" as String
let url = URL(string: myUrl) // nil here .. problem !
if let encoded = myUrl.addingPercentEncoding(withAllowedCharacters: .urlFragmentAllowed){
let urlencoded = URL(string: encoded) // "http://%E2%9E%A1.ws/%E4%A8%B9" here :) no problem ^^
}

Related

encode url swift withAllowedCharacters not working encoding %20 as %2520 (means encoding % as %25)

Following is my code for URL encoding
extension String {
var encoded: String {
return self.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? ""
}
}
But I am facing issue if url contains %20. it is encoding it as %2520 although I have added urlQueryAllowed
Original url: https://mydomain.in/retailers_data_v2/retailer/320/17372-Tea%20Coffee%20Vending%20Machine.JPG
Encoded url: https://mydomain.in/retailers_data_v2/retailer/320/17372-Tea%2520Coffee%2520Vending%2520Machine.JPG
If you have an already encoded URL String, you first need to remove percent encoding before applying it again.
If you aren't sure whether the URL you have is already encoded or not, you can simply use an if let on removingPercentEncoding and depending on its result, either call addingPercentEncoding on the original URL or on the one that you removed the encoding from.
let alreadyEncodedURLString = "https://mydomain.in/retailers_data_v2/retailer/320/17372-Tea%20Coffee%20Vending%20Machine.JPG"
if let unencodedURLString = alreadyEncodedURLString.removingPercentEncoding {
unencodedURLString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)
} else {
alreadyEncodedURLString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)
}

Swift URL decoding does not decode string

when trying to decode the following encoded string, it does not work...it´s actually doing nothing...leaving the string as it is:
https://www.foodhat.app/?rOpeningTime=08:00&rClosingTime=20:00&rClosedUntil=2019-08-19&pId=557&rId=69&goTo=CUSTOMER_PRODUCT_DETAILS&cName=Duhok&pName=%D9%85%D9%86%D8%B3%D9%81+%D8%AF%D9%88%D9%84%D9%85%D8%A9&rName=Mansaf+Alzain&rMinOrderValue=15000
pName is arabic, so do not wonder the encoding of it.
I use str.removingPercentEncoding to decode the string...but as said...it doesn´t remove the "+" (e.g. in rName)!
Result is
let str = "https://www.foodhat.app/?rOpeningTime=08:00&rClosingTime=20:00&rClosedUntil=2019-08-19&pId=557&rId=69&goTo=CUSTOMER_PRODUCT_DETAILS&cName=Duhok&pName=%D9%85%D9%86%D8%B3%D9%81+%D8%AF%D9%88%D9%84%D9%85%D8%A9&rName=Mansaf+Alzain&rMinOrderValue=15000"
print(str.removingPercentEncoding!)
//https://www.foodhat.app/?rOpeningTime=08:00&rClosingTime=20:00&rClosedUntil=2019-08-19&pId=557&rId=69&goTo=CUSTOMER_PRODUCT_DETAILS&cName=Duhok&pName=منسف+دولمة&rName=Mansaf+Alzain&rMinOrderValue=15000
What am I doing wrong?
Use 'str.replacingOccurrences()' instead. A better solution would be to use 'URLComponents', 'queryItems' to perform the filtering on the targeted queryItem, to ensure that '+' removal is not done for the entire url.
When decoding an URL you should use URLComponents :
let url = URL(string:"https://www.foodhat.app/?rOpeningTime=08:00&rClosingTime=20:00&rClosedUntil=2019-08-19&pId=557&rId=69&goTo=CUSTOMER_PRODUCT_DETAILS&cName=Duhok&pName=%D9%85%D9%86%D8%B3%D9%81+%D8%AF%D9%88%D9%84%D9%85%D8%A9&rName=Mansaf+Alzain&rMinOrderValue=15000")!
let components = URLComponents(url: url, resolvingAgainstBaseURL: false)
print(components)
Output is :
[rOpeningTime=08:00, rClosingTime=20:00, rClosedUntil=2019-08-19, pId=557, rId=69, goTo=CUSTOMER_PRODUCT_DETAILS, cName=Duhok, pName=منسف+دولمة, rName=Mansaf+Alzain, rMinOrderValue=15000]

Swift URL.path changes encoding of utf-8 characters

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

NSUrl swift 2 with greek and other special characters

I'm trying to make a server request with
NSUrl(string: "http://example.com/α")
In which α is a greek character. So when i request this i get an error and my app crashes. So i tried to encode the url to this
let myUrl = NSURL(string: myLink.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!)!
and now my link has become http://example.com/%CE%B1
where %CE%B1 is the α character. Although my server does not recognise it and it doesnt send me back the data.
I use node.js with io.socket connections. Is there any way with swift to send the correct url without the percent encoding?
For encode α in your url
let str = "α"
let url = str.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())
Now decode url string like this
let orgStr = url?.stringByRemovingPercentEncoding
print(orgStr)

Cyrillic symbols in URL

App crashes with following url:
let jsonUrl = "http://api.com/алматы/events"
let session = NSURLSession.sharedSession()
let shotsUrl = NSURL(string: jsonUrl)
let task = session.dataTaskWithURL(shotsUrl!)
Log:
fatal error: unexpectedly found nil while unwrapping an Optional value
It's because of cyrillic symbols in url. How can I solve this issue. Thanks for your help!
Swift 4
Using String Extension
Create a swift file named String+Extension.swift and paste this code
import UIKit
extension String{
var encodeUrl : String
{
return self.addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed)!
}
var decodeUrl : String
{
return self.removingPercentEncoding!
}
}
and Use it like so: (sample according to question):
"http://api.com/алматы/events".encodeUrl
Try this:
let encodedUrl = jsonUrl.stringByAddingPercentEncodingWithAllowedCharacters(URLQueryAllowedCharacterSet)
Something like this:
let apiHost = "http://api.com/"
let apiPath = "алматы/events"
let escapedPath = apiPath.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLHostAllowedCharacterSet())
let url = NSURL(string: "\(apiHost)\(escapedPath!)")
Obviously you should do something smarter than just force unwrap escapedPath.
Using the Wikipedia page for Swift as an example:
https://ru.wikipedia.org/wiki/Swift_(язык_программирования)
Becomes:
https://ru.wikipedia.org/wiki/Swift_(%D1%8F%D0%B7%D1%8B%D0%BA_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F)
Which when pasted into the browser takes you to the right page (and most browsers will conveniently render the UFT-8 characters for you).
Non-ASCII characters (and many special characters) need to be escaped in a URL. Chrome and other browser do it automatically. And they unescape the URLs in the address bar for a nicer display.
So if you have a static URL, just paste it into the adressbar, press enter, selected the URL again, copy and paste it to your app:
So instead of:
let jsonUrl = "http://api.com/алматы/events"
You'll get:
let jsonUrl = "http://api.com/%D0%B0%D0%BB%D0%BC%D0%B0%D1%82%D1%8B/events"
Try stringByAddingPercentEncodingWithAllowedCharacters: defined on NSString. You may see people suggesting stringByAddingPercentEscapesUsingEncoding:, but that method is deprecated in iOS 9.
There are also a few predefined NSCharacterSets in Foundation, such as URLHostAllowedCharacterSet and URLPathAllowedCharacterSet. Therefore, if you really have to parse the unescaped URL in code (using preprocessed URLs, mentioned in the accepted answer, is usually a much better idea), you can write a helper method like this:
import Foundation
func url(scheme scheme: String, host: String, path: String) -> NSURL? {
let components = NSURLComponents()
components.scheme = scheme
components.host = host.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLHostAllowedCharacterSet())
components.path = path.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLPathAllowedCharacterSet())
return components.URL
}
// evaluates to http://api.com/%25D0%25B0%25D0%25BB%25D0%25BC%25D0%25B0%25D1%2582%25D1%258B/events
url(scheme: "http", host: "api.com", path: "/алматы/events")
Note that the above documentation mentions that
This method is intended to percent-encode an URL component or subcomponent string, NOT an entire URL string.
That's because according RFC 3986, not all parts of an URL can be percent-encoded (e.g. scheme - http/https/etc.)
in xamarin:
var uri = new Uri (url);
var nsurl = new NSUrl (uri.GetComponents (UriComponents.HttpRequestUrl, UriFormat.UriEscaped));
UIApplication.SharedApplication.OpenUrl (nsurl);
URLs cannot contain Cyrillic characters. There are standards how to translate Cyrillic characters into valid URLs - you might find something if you search for "Punicode" (the P is intentional).

Resources