Swift URL appendingPathComponent converts `?` to `%3F` - ios

let url = URL(string: "https://example.com")
let path = "/somePath?"
let urlWithPath = url?.appendingPathComponent(path)
After appending, the path /somePath? becomes somePath%3F.
The ? becomes a %3F. Question Mark is replaced with the percent-encoded escape characters.
The URL does output correctly if I use:
let urlFormString = URL(string:"https://example.com/somePath?")
Why does appendingPathComponent convert ? to %3F?
How can I use appendingPathComponent if the path component contains a question mark?

The generic format of URL is the following:
scheme:[//[userinfo#]host[:port]]path[?query][#fragment]
The thing you have to realize is that the ? is not part of the path. It is a separator between path and query.
If you try to add ? to path, it must be URL-encoded because ? is not a valid character for a path component.
The best solution would be to drop ? from path. It has no meaning there. However, if you have a partial URL which you want to append to a base URL, then you should join them as strings:
let url = URL(string: "https://example.com")
let path = "/somePath?"
let urlWithPath = url.flatMap { URL(string: $0.absoluteString + path) }
In short, appendingPathComponent is not a function that should be used to append URL query.

You should use removingPercentEncoding on URL's absoluteString,
let url = URL(string: "https://example.com")
let path = "/somePath?"
let urlWithPath = url?.appendingPathComponent(path).absoluteString.removingPercentEncoding
print(urlWithPath!)

%3F stands for ?, according to URL Encoding. So if you create an URL - it has to be that way. If, for some reason, you need your ?, create a string, not URL.
Check urlWithPath.lastPathComponent to see that everything is all right (prints: "somePath?")

First of all it is not a problem. this is a mechanism called URL encoding, for translating unprintable or special characters to a universally accepted format by web servers and browsers.
For more information you can go to https://www.techopedia.com/definition/10346/url-encoding
URL encoded chars, https://www.degraeve.com/reference/urlencoding.php

When you convert the string to URL, It'll be doing PercentEncoding in URL. So that your ? has been encoded into %3F.
If you want the url as string with ?, you can remove the PercentEncoding as like below code.
let urlString = urlWithPath?.absoluteString.removingPercentEncoding
Output: https://example.com/somePath?

Instead of URL can use URLComponents:
var url = URLComponents()
Set the scheme, host and path:
url.scheme = "https"
url.host = "example.com"
url.path = "/somePath"
Set the query as empty string and ? is added since it is a separator between path and query:
url.query = ""
// url.queryItems = [URLQueryItem(name: "nameN", value: "valueX")]
Prints: https://example.com/somePath?
print(url)

You can build your url using NSString class:
let urlStr = "https://example.com" as NSString
let path = "/somePath?"
let urlStrWithPath = urlStr.appendingPathComponent(path)
let url = URL(string: urlStrWithPath)
This way, special characters are not url-encoded in the final url.

Related

Get the value of URL Parameters in WKWebView and create new URL

I'm trying to get the current URL of the WKWebView, search/copy one value out of it, and create a new URL.
I use Swift and have this an URL like that https://example.org/bundle-572974a2.html and try to get the following value 572974a2.
My intention is to create a new URL with that value. The numbers can change from time to time.
I use this code to get the current WKWebView URL. I think I have to convert this to a string first.
let oldURL = webView.url
And I use this code to create my new URL.
var components = URLComponents()
components.scheme = "https"
components.host = "new-example.org"
components.path = "/g"
components.queryItems = [
URLQueryItem(name: "newbundle", value: "XXXXXXXX")
]
let newURL = components.url
How can I change/paste the value: "XXXXXXXX" with the value 572974a2 of the old URL?
Some simple URL manipulation and string handling will get you the number from the old URL:
let oldURL = URL(string: "https://example.org/bundle-572974a2.html")! // An example
let name = oldURL.deletingPathExtension().lastPathComponent // bundle-572974a2
let number = name.dropFirst(7) // remove "bundle-"
This assumes the number will always be preceded by "bundle-" (or any seven-letter sequence really. If that can vary then the use of dropFirst(7) will need to be replaced with code that can handle strings like "bundle-572974a2" and remove the part you don't want.
Then building the new URL becomes:
var components = URLComponents()
components.scheme = "https"
components.host = "new-example.org"
components.path = "/g"
components.queryItems = [
URLQueryItem(name: "newbundle", value: String(number))
]
let newURL = components.url
In a case where you might have both a prefix and a suffix you need to remove, the following would be one way to remove them. This code assumes there will be a prefix to remove if there is a suffix to remove. If there is a suffix and no prefix to remove then this code gives the wrong result.
var number = url.deletingPathExtension().lastPathComponent
if let hyphen = number.firstIndex(of: "-") {
number = String(number[number.index(after: hyphen)...])
}
if let hyphen = number.lastIndex(of: "-") {
number = String(number[..<hyphen])
}
P.S. String manipulation in Swift is a pain.

URL is nil or percentage encoded

I have this URL
https://apps.apple.com/developer/john-doe/id32123123#see-all/mac-apps
I do this
let path = "https://apps.apple.com/developer/john-doe/id32123123#see-all/mac-apps"
let url = URL(string: path!)
the resulting url is nil.
I do this:
var components = URLComponents()
components.scheme = "https"
components.host = "apps.apple.com"
components.path = "/developer/john-doe/id32123123#see-all/mac-apps"
let url = components.url!
The resulting url percentage encoded, like this and, as expect, an URLRequest done with that URL fails.
https://apps.apple.com/developer/john-doe/id32123123%23see-all/mac-apps
Is there a way to get a normal URL without any percentage encoding?
How do I do a URL that works with URLRequest?
This code works just fine:
let path = "https://apps.apple.com/developer/john-doe/id32123123#see-all/mac-apps"
let url = URL(string: path)
I just had to remove the !. Path is not optional, so there's nothing to unwrap.
Your latter technique isn't something you should bother using for literal URLs like this, but I can explain why it's "not working" to your expectations anyway. The # marks the beginning of the url's fragment. It's a special character, which is why the system is percent-encoding it for you when you try to use it as part of the path. Here's the fixed code:
var components = URLComponents()
components.scheme = "https"
components.host = "apps.apple.com"
components.path = "/developer/john-doe/id32123123"
components.fragment = "see-all/mac-apps"
let url = components.url! // => "https://apps.apple.com/developer/john-doe/id32123123#see-all/mac-apps"
You should read up on the URL standard.

Should I use CharacterSet or URLQueryItem?

I have some problem when try to call a url string request with some special character inside. For example with CharacterSet, when in URL have the %20 that mean spacebar and I use the addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) function it's replaced to %2520. Can you guys try this code:
let url = "http://analytics.abc.io/acct?start_date=2017-11-15&aff=ABC%20Telecom"
url.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)
print(URL(string: url.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!)!)
Actually it's request sucessfully but the response data is wrong. Any guys can give me some ideas and how to solve that clearly.
Beside that please give me some comment about the URLQueryItem I not use that, so I need your help. Thank and cheer!
Honestly, is it 100% much better if you decompose using URLComponents. It will facilitate the work for you and the next dev to read the code.
let host = "analytics.abc.io"
let scheme = "http"
let path = "/acct"
var urlQueryItems : [URLQueryItem] = []
for (key, value) in [("start_date", "2017-11-15"), ("aff", "ABC Telecom")] {
urlQueryItems.append(URLQueryItem(name: key, value: value))
}
var result = URLComponents()
result.host = host
result.scheme = scheme
result.path = path
result.queryItems = urlQueryItems
print(result.url)
//prints : Optional(http://analytics.abc.io/acct?start_date=2017-11-15&aff=ABC%20Telecom)
Please Change Your Line with Below
(url.addingPercentEncoding( withAllowedCharacters: .urlUserAllowed))!
Hope it Helps.
I was facing the same issue and i solve using followint trick
let url = "http://analytics.abc.io/acct?start_date=2017-11-15&aff=ABC%20Telecom"
if url.contains(" ") {
url = urlSTR.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
}
sometimes we get url already encoded from server then it creates some problem.so the best solution will be to ask your server guy to provide url without encoding and manage it from app end.
Hope it helps.
Don't force unwrap anything, use if..let or guard
let url = "http://analytics.abc.io/acct?start_date=2017-11-15&aff=ABC%20Telecom"
if let finalURL = url.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) {
//Do your stuff
}

How to append an array in URL request type GET in Swift?

I am using Xcode7.3 with Swift2.2.
I want to append an Array in url request.For example my array like
[“jeevan”,”jeejo”]. I want to append this array with key(arrayKey) in url request like must be the following pattern
https://api.com/pre/ws/ch/roo?arrayKey=jeevan%2Cjeejo
How to solve this issue? Please help me
You need to use encode your URL instead of join Array with separator, but if you want to merge Array with URL you can try like this.
let str = ["jeevan","jeejo"]
let join = str.joinWithSeparator("%2C")
let url = "https://api.com/pre/ws/ch/roo?arrayKey=\(join)"
If you want to encode url encode this way.
let str = ["jeevan","jeejo"]
let join = str.joinWithSeparator(",")
let url = "https://api.com/pre/ws/ch/roo?arrayKey=\(join)"
let encoded = url.stringByAddingPercentEncodingWithAllowedCharacters(.URLFragmentAllowedCharacterSet())
Note : The reason I have used , is because %2C is encode for , you can confirm it here on W3School URL Encoding.
easy solution can be like this
var URIString = ""
for item in array {
URIString +=\(item)%2C
}
after subtract last 3 characters and make URL string
Simple code like this
var array: [String] = ["jeevan","jeejo"]
var myString = ""
for i in 0..<array.count {
myString += array[i]
if (i+1)<array.count { mystring+="%2C" }
}
Can give you result like this:
jeevan%2Cjeejo

Add query string to local HTML files

I have codes to load local html files (html files are inside the app, not from server), but how would I go about adding query string to it. I basically want to pass data from swift into html webview. Is this even possible? Alot of examples I've found is related to html files from server, but haven't found any for html files stored inside the app.
let url = NSBundle.mainBundle().URLForResource("index", withExtension:"html", subdirectory: "www")
let request = NSURLRequest(URL: url!)
webView.loadRequest(request)
To add query string parameters to local URL for WebView you can use code below. It is valid for Xcode 9 and Swift 4.
let bundleMainUrl = Bundle.main.url(forResource: "index", withExtension: "html", subdirectory: "www");
let fullUrl = URL(string: "?os=ios", relativeTo: bundleMainUrl);
let request = URLRequest(url: fullUrl!);
webView?.load(request);
Yes you can do it by using Javascript. Check out stringByEvaluatingJavaScriptFromString
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIWebView_Class/#//apple_ref/occ/instm/UIWebView/stringByEvaluatingJavaScriptFromString:
If you have a full URL string with the queries, you can just initialise NSURL with that string.
let url = NSURL(string: "www.stackoverflow.com")
You can use NSURLComponents and NSURLQueryItem to construct a URL with query parameters:
var urlComponents = NSURLComponents(string: "www/index.html")!
urlComponents.queryItems = [
NSURLQueryItem(name: "key1", value: "value1"),
NSURLQueryItem(name: "key2", value: "value2")
]
urlComponents.URL // returns www/index.html?key1=value1&key2=value2
I've solved this by using pathForResource instead of pathForResource as I was able to append string toward the end.
var url = NSBundle.mainBundle().pathForResource("index", ofType: "html", inDirectory: "www").stringByAppendingString("?os=ios")

Resources