urlEncoding issue with Swift Odata - odata

Our OData service
{{url}}/odata/GroupMembers?$filter=GroupName eq 'PDL-**-Users-Test'
The spaces before and after eq is becoming %2520 upon urlencoding in Swift which is leading to error code 400.
odata/GroupMembers?$filter=GroupName%2520eq%2520'PDL-**-Users-Test'

My earlier code was,
urlComponents.queryItems = [URLQueryItem]()
for (key,value) in parameters {
let queryItem = URLQueryItem(name: key,
value: "\(value)".addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed))
}
urlRequest.url = urlComponents.url
by removing the following code, it worked.
.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)
Reason being Apple's URLComponents and URLQueryItem does the encoding in Swift.

Related

Get correct parameter value from URL in "/:id=" format using regex

What is the best way to get the id value from this url:
URL(string: "urlScheme://search/:id=0001")
I've been trying to route this URL using a deep link request. However, my url routing solution JLRoutes shows the parameters as key = id and value = :id=0001.
I instead need the parameters to be key = id and value = "0001".
In an ideal world I would just be using a URL string like "urlScheme://search/0001" and not have any problem but the ":id=" part has to be in there. George's comment about converting the parameter to a URL in of itself and using .pathComponents.last does work, but I think a regex solution is probably going to scale better going forward.
The answer from #George should work fine, but two things struck me: you decided you wanted a regex solution, and to make this generic seemed to be asking for a recursive solution.
The below approach uses regex to identify up to the last /: delimiter, then has to do a bit of inelegant string handling to split it into the base string and the final pair of (key: value) params. I'd hoped to be able to write a regex that just matches those final parameters as that would be a far cleaner range to work with, but haven't managed it yet!
func paramsFrom(_ str: String) -> [String: String] {
guard let baseRange = str.range(of:#"^.+\/:"#, options: .regularExpression ) else { return [:] }
let base = String(str[baseRange].dropLast(2))
let params = str.replacingCharacters(in: baseRange, with: "").components(separatedBy: "=")
return [params.first! : params.last!].merging(paramsFrom(base)){(current, _) in current}
}
using this on your example string returns:
["id": "0001", "title": "256", "count": "100"]
EDIT:
Managed to dig out the old regex brain cells and match just the final pair of parameters. You could adapt the above to use the regex
(?<=\/:)[a-zA-Z0-9=]+$
and the have slightly cleaner string handling as the shortened base string becomes
String(str.dropLast(str[paramsRange].count))
If your URL is in the form of an actual URL query, e.g. urlScheme://search?id=0001, there is a nice way to do this.
With thanks to vadian, this is really simple. You can just do the following:
let components = URLComponents(string: "urlScheme://search?id=0001&a=2")!
let dict = components.queryItems?.reduce(into: [:]) { partialResult, queryItem in
partialResult[queryItem.name] = queryItem.value
}
Or a slightly more compact version for dict:
let dict = components.queryItems?.reduce(into: [:], { $0[$1.name] = $1.value })
Result from given input:
["id": "0001", "a": "2"]
If you must use the current URL form
You can replace the URL string, such as:
let urlStr = "urlScheme://search/:id=0001/:a=2"
let comps = urlStr.components(separatedBy: "/:")
let newUrl: String
if comps.count > 1 {
newUrl = "\(comps.first!)?\(comps.dropFirst().joined(separator: "&"))"
} else {
newUrl = urlStr
}
print(newUrl)
Prints: urlScheme://search?id=0001&a=2
Original answer (slightly modified)
If you have a URL with queries separated by /: you can use the following:
// Example with multiple queries
let url = URL(string: "urlScheme://search/:id=0001/:a=2")!
let queries = url.lastPathComponent.dropFirst().split(separator: "/:")
var dict = [String: String]()
for query in queries {
let splitQuery = query.split(separator: "=")
guard splitQuery.count == 2 else { continue }
let key = String(splitQuery.first!)
let value = String(splitQuery[1])
dict[key] = value
}
print(dict)
Result is same as before.
You can use next regex approach to enumerate parameters in your url path:
let urlString = "urlScheme://search/:id=0001" as NSString
let regex = try! NSRegularExpression(pattern: "([^:\\/]+)=([0-9]+)")
if let match = regex.matches(in: urlString as String, options: [], range: NSMakeRange(0, urlString.length)).first, match.numberOfRanges == 3 {
let key = urlString.substring(with: match.range(at: 1))
let value = urlString.substring(with: match.range(at: 2))
print(key, ":", value)
}
// Prints
id : 0001

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
}

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)")

Getting query string parameters from url in the UIWebView in Swift?

my question is pretty straight forward. Can someone please show me how to get the query string parameters in a url received by the UIWebView. It is pretty easy in objective c, but I need some help in swift as i'm new to the language. Thanks in advance.
In the NSURL class exists the .query property which returns the string of everything after the ? in the url in form:
http://www.example.com/index.php?key1=value1&key2=value2
in this example using the code:
var url: NSURL = NSURL(string: "http://www.example.com/index.php?key1=value1&key2=value2")
println(url.query) // Prints: Optional("key1=value1&key2=value2")
More information in the documentation
As far as getting the url from the uiwebview, you could use something along the lines of:
let url: NSURL = someWebView.NSURLRequest.URL
To make it a bit easier to get the value of a particular parameter you expect, you can use URLComponents which will parse out the query string parameters for you.
For example, if we want the value of the query string parameter key2 in this URL:
"https://www.example.com/path/to/resource?key1=value1&key2=value2"
We can create a URLComponents struct, then filter for the query item which matches the name, take the first one, and print its value:
let url = URL(string: "https://www.example.com/path/to/resource?key1=value1&key2=value2")
if let url = url,
let urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false) {
let parameterWeWant = urlComponents.queryItems?.filter({ $0.name == "key2" }).first
print(parameterWeWant?.value ?? "")
}
The key (!) thing is that urlComponents.queryItems gives us this array of QueryItem structs back, which gives us an easier way to filter the parameters and get the value of the parameter we're looking for.
▿ Optional([key1=value1, key2=value2])
▿ some: 2 elements
▿ key1=value1
- name: "key1"
▿ value: Optional("value1")
- some: "value1"
▿ key2=value2
- name: "key2"
▿ value: Optional("value2")
- some: "value2"
We can parse the Url params with this method,
func getParameterFrom(url: String, param: String) -> String? {
guard let url = URLComponents(string: url) else { return nil }
return url.queryItems?.first(where: { $0.name == param })?.value
}
let url = "http://www.example.com/index.php?key1=value1&key2=value2"
let key1 = self.getParameterFrom(url: url, param: "key1")
print("\(key1)") // value1

Replace occurrences of space in URL

I have a URL in an iPhone application to work with. But the problem is that it has some spaces in the URL. I want to replace the spaces with '%20'. I know that there are the stringByReplacingOccurencesOfString and stringByAddingPercentEscapesUsingEncoding methods. I also have used them. But they are not working for me. The spaces are replaced by some unusual values.
I'm applying those methods on an instance of NSString.
The correct format for replacing space from url is :
Swift 4.2 , Swift 5
var urlString = originalString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)
Swift 4
var urlString = originalString.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)
Objective C
NSString *urlString;//your url string.
urlString = [originalUrl stringByReplacingOccurrencesOfString:#" " withString:#"%20"];
or
urlString = [originalUrl stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
iOS 9 and later
urlString = [originalUrl stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
Swift 2.0
let originalUrl = "http://myurl.com/my photo.png"
let urlNew:String = urlReq.stringByAddingPercentEncodingWithAllowedCharacters( NSCharacterSet.URLQueryAllowedCharacterSet())!
Output:
http://myurl.com/my%20photo.png
To replace occurence in SWIFT 3 :
let updatedUrl = originalUrl.addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed)
Swift 4
Another way to replace an empty space with replacingOccurrences method:
let yourString = "http://myurl.com/my photo.png"
let urlNew:String = yourString.replacingOccurrences(of: " ", with: "%20").trimmed
That will replace the empty space (" ") with '%20'
Swift 5
var urlString = originalString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)
Swift 5.3, clear space your string,
let str = " www.test.com "
let trimmed = str.trimmingCharacters(in: .whitespacesAndNewlines)
print(str) // "www.test.com"
var urlString :String = originalUrl.addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed)!
Hope this will work
let url = "https:youtube.56432fgrtxcvfname=xyz&sname=tuv"
let urlNew:String = url.replacingOccurrences(of: " ", with: "%20")
Alamofire.request(urlNew, method: .get, headers: headers).responseJSON{
response in
print(response)
}
It will remove all kind of spaces from the url.
The quickest solution: just use the stringy method...
.replacingOccurrences(of: " ", with: "%20")
...at the end of your string, and it will do as it says.
Apple should be EMBARRASSED that their URL(string: ) method doesn't automatically do this (and more). It's 2023 and I wasted 3 hours finding this half-assed solution.
My specific example was inside cellForItemAt and having to do with optionals.
let stringOfImageURLFromFirebase = wordController?.word?.images[indexPath.item].imgUrl
let stringWithNoSpaces = string?.replacingOccurrences(of: " ", with: "%20")
if let imageURLString = stringWithNoSpaces,
let imageURL = URL(string: imageURLString) {
cell.definitionImageView.loadImageFromFirebase(url: imageURL)
} else {
cell.definitionImageView.image = UIImage(named: "slictionarylogo")
}
You can fuddle with .addingPercentEncoding for a long time, try NSURL nonsense, and you'll just be faster with using the obvious. Apple sucks, and should've made this painfully easy since it's such a common problem. Again, they are asleep at the wheel, and their documentation is AWFUL too.
SWIFT 3.1
Simple way to replace an empty space with replacingOccurrences:
URL = URL.replacingOccurrences(of: " ", with: "", options: .literal, range: nil)
Swift 4, iOS-9
let **urlSearchVal**:String = "top 10 movies"
let urlString =
"https://www.googleapis.com/youtube/v3/search?part=snippet&q=\(urlSearchVal)&key=......&type=video"
//replace ...... above with your youtube key
// to ignoring white space in search
let UrlString :String = urlString.addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed)!
A Swift 4 solution. You simply pass through a string and it fills spaces with %20 and adds "http://" to the beginning to the string. Pretty sweet!
URL(fileURLWithPath: String)

Resources