Should I use CharacterSet or URLQueryItem? - ios

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
}

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.

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

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.

How to get a particular value from a Url in iOS - Swift 3

I have a url that is reg code attached to it. I just want to retrieve the reg code 7954741093-41547468-3 from the below url
https://mycode.funny.net/mycode/Registrations/7954741093-41547468-3?api-version=2011-04
You can use this:
let regCode:String = (url.components(separatedBy: "/").last?.components(separatedBy: "?").first)!
Where url is
let url:String = "https://mycode.funny.net/mycode/Registrations/7954741093-41547468-3?api-version=2011-04"
i think using URLComponents is a little more robust:
let url = "https://mycode.funny.net/mycode/Registrations/7954741093-41547468-3?api-version=2011-04"
let components = URLComponents(string: url)
components?.path.components(separatedBy: "/").last

The '#' char gets converted to %23 causing the GET Request to fail

I am a new Swift developer using Swift 3 developing an iOS app. I need to make a URL Request to get some data from the web. That URL contains a # character.
I use URLComponents with URLQueryItems to build the request URL. During this process the # char gets converted to %23 which I think is valid utf8 encoding. Unfortunately, this causes the GET to fail.
To test I pasted the URL into my browser and changed %23 back to # and it worked just fine.
How can I fix this so it does not change # to URL. I did find a post from a couple years ago but it was using old framework items so it does not apply to me.
Below is the playground I made to illustrate and test this.
// ------------------------------------------------------------------
//: Playground - TestBuildURLWithParameters
//
// I am using this playground to build the proper
// URL for the GET request to get the detailed
// rtesults for a specific athlete where the "Key"
// is their Bib Nbr. If the GET cant find the specific
// Atlete with that URL it redirects you to the list
// of athlete results (which is no go to me in this case)
//
// Currently, there is a big "gotcha". In building the URL
// using component and query items, the foundation classes
// replace the '#' sign in the URL with the %23 which represents
// the pound sign. Unfortunately, the causes the GET to fail
// and redirects to athlete list which is not helpful
// I am still trying to figure out how to FORCE it to keep
// the '#" character in the URL so it will work
//
// ------------------------------------------------------------------
import Foundation
import UIKit
let baseURLString = "http://www.ironman.com/triathlon/events/americas/ironman-70.3/augusta/results.aspx"
let rd = "20150927"
let race = "augusta70.3"
let bibID = "93"
var detail = "1#axzz4FGGcjBOn"
print("Detail w/o unicocde: \(detail)")
detail = "1\u{0023}axzz4FGGcjBOn"
print("Detail with unicocde: \(detail)")
var components = URLComponents(string: baseURLString)!
var queryItems: [URLQueryItem] = [] // All Items after the "?"
let baseParams =
[
"rd": rd,
"race": race,
"bidID": bibID, // Note: HTML mispelled bib with bid so "bidID" is the URL search
"detail": detail
]
for (key, value) in baseParams
{
let item = URLQueryItem(name: key, value: value)
queryItems.append(item)
}
components.queryItems = queryItems // what does this look like
print("components: \(components)") // see it
It is not a good way to include fragment part of URL into query items.
Try this:
import Foundation
let baseURLString = "http://www.ironman.com/triathlon/events/americas/ironman-70.3/augusta/results.aspx"
let rd = "20150927"
let race = "augusta70.3"
let bibID = "93"
var detail = "1"
//# split the last query item and the fragment
let fragment = "axzz4FGGcjBOn"
var components = URLComponents(string: baseURLString)!
var queryItems: [URLQueryItem] = []
let baseParams =
[
"rd": rd,
"race": race,
"bidID": bibID,
"detail": detail
]
for (key, value) in baseParams
{
let item = URLQueryItem(name: key, value: value)
queryItems.append(item)
}
components.queryItems = queryItems
components.fragment = fragment
print("components: \(components)")
If you need you can choose the character that will receive the encoding.
In the charactersIn: you put all characters you want to encode.
Then you use the .inverted so all the others characters will go normal like this:
let customAllowedSet = NSCharacterSet(charactersIn:"=\"%/<>?#\\^`{|}").inverted
let encondedString = originalString.addingPercentEncoding(withAllowedCharacters: customAllowedSet)
print("enconded string: \(encondedString)")
Encode your parameters and then add it to URL, this will encode # before hitting API and you'll get desired result.
To encode parameters, you can use below code.
var parameterString = "your parameter string"
var encodedString = parameterString .addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)
print(encodedString !)
I did get an answer via email from a friend that works for now. Basically, I added the query items manually to the URL using the URL extension below so it didn't monkey with the '#' char:
extension URL {
func appendingNonEscapedQueryItems(_ queryItems: [URLQueryItem]) -> URL {
let url = self
var urlString = url.absoluteString
for queryItem in queryItems {
let queryName = queryItem.name
guard let queryValue = queryItem.value else {
continue
}
let query = queryName + "=" + queryValue
if queryItem == queryItems.first {
urlString = urlString + "?" + query
}
else
{
urlString = urlString + "&" + query
}
}
return URL(string: urlString)!
}
}
...
let requestURL = components.url!.appendingNonEscapedQueryItems(queryItems)
print("URL \(requestURL)")

Put a string before a certain string inside a text in Swift 3

I'm dealing with an issue here, I have a URL of an image which is like this
http://example.com/image/test.jpg
Which is a string.
And I would like to insert before .jpg a certain text like -40x40
Is there any way to analyze the URL string and somehow to add this text so the final string should be
http://example.com/image/text-40x40.jpg
What i've tried till now is this
var finalImage = "http://example.com/image/test.jpg"
finalImage.insert("-40x40" as Character, at: finalImage.endIndex - 4)
but i get 2 errors.
1) i cant add more than 1 character and 2) i cant do the math ad endIndex.
But i can't add more than one character there.
Thanks a lot!
Try this. It uses NSURL, which exists so that path manipulations are easy and legal! The documentation is really quite good.
let s1 = "http://example.com/image/test.jpg"
let u = URL(fileURLWithPath: s1)
let exExt = u.deletingPathExtension()
let s2 = exExt.absoluteString + "-40x40.jpg"
Another ways.
The code shown in the question, fixed:
var finalImage = "http://example.com/image/test.jpg"
let extIndex = finalImage.index(finalImage.endIndex, offsetBy: -4)
finalImage.insert(contentsOf: "-40x40".characters, at: extIndex)
Using NSRegularExpression:
let origImage = "http://example.com/image/test.jpg"
let regex = try! NSRegularExpression(pattern: "(\\.jpg)$", options: .caseInsensitive)
let finalImage = regex.stringByReplacingMatches(in: origImage, range: NSRange(0..<origImage.utf16.count), withTemplate: "-40x40$0")
As a complement to the neat accepted answer by #Grimxn: Foundation's URL has various more methods that allows for more separation "of concerns" in case you'd like to apply some more complex modification of the image (file) name, while not really worrying about the image (file) name extension.
let s1 = "http://example.com/something.cgi/image/test.jpg"
let u = URL(fileURLWithPath: s1)
// separate into (String) components of interest
let prefixUrl = u.deletingLastPathComponent().absoluteString
// "http://example.com/something.cgi/image/"
let fileName = u.deletingPathExtension().lastPathComponent
// "test"
let fileExtension = "." + u.pathExtension
// ".jpg"
// ... some methods that implements your possibly more
// complex filename modification
func modify(fileName fName: String) -> String {
// ...
return fName + "-40x40"
}
// reconstruct url with modified filename
let s2 = prefixUrl + modify(fileName: fileName) + fileExtension
print(s2) // http:/example.com/something.cgi/image/test-40x40.jpg
Grimxn's solution is probably the best fit for this problem, but for more complex manipulation of URLs, take a look at the NSURLComponents class. You can convert an NSURL to NSURLComponents, then use the various methods of NSURLComponents to manipulate your URL, and then finally convert the NSURLComponents back to an NSURL
As noted in a comment by #dfri, Swift 3 (and later) includes a native URLComponents class, which follows Swift naming and calling conventions. Going forward you should use that instead of the Objective-C/Swift 2 NSURLComponents class.
if let url = NSURL(string: "http://example.com/image/test.jpg"),withoutExt = url.URLByDeletingPathExtension
{
let finalstring : NSString = withoutExt.absoluteString + "-40x40.jpg"
print(finalstring)
}
Very simply with one line...
let newURL = oldURL.stringByReplacingOccurrencesOfString(".jpg", withString: "-40x40.jpg", options: NSStringCompareOptions.LiteralSearch, range: nil)

Resources