Converting string (Contains json string) to NSURL in swift - ios

I have PAI its Implemented in .NET.
one of the web service url is like this
http://123.321.33/UploadCitizenImage?jsonString={\"Mobile\":\"12345678\", \"fileName\":\"7661832460_05072018.png\"}
while converting above string to URL in swift, app going crash.
for more info check this

The URL(string:) initializer returns an optional since the parsing of the string may fail. In that case, nil is returned. That's exactly what's happening here since the string you are providing is not a valid URL: there are several characters in the query that are not allowed there and need to be replaced: { as %7B, " as %22, space as %20 and } as %7D.
So the initializer returns nil. Next thing you do is force unwrap via the ! operator. But force-unwrapping a nil is illegal and is why you get the crash.
If you want to create an URL, please look into the URLComponents class which does all the necessary escaping for you so you don't need to care about it. The queryItems property is of particular interest for you, it's an array of URLQueryItem.

Please do something like that,
let jsonString = "jsonString={\"Mobile\":\"12345678\", \"fileName\":\"7661832460_05072018.png\"}" as String
let urlEncoadedJson = jsonString.addingPercentEncoding(withAllowedCharacters:.urlHostAllowed)
let urls = NSURL(string:"http://123.321.33/UploadCitizenImage?\(urlEncoadedJson ?? "")")
First convert your json into encodedJson then add into your url.
Do let me know if there is some issue.

You can try this,
let string = "http://123.321.33/UploadCitizenImage?jsonString={\"Mobile\":\"12345678\", \"fileName\":\"7661832460_05072018.png\"}"
let escapedString = string.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)
let url = URL(string: escapedString!)!
print(url)
Output will be like this,
http://123.321.33/UploadCitizenImage?jsonString=%7B%22Mobile%22:%2212345678%22,%20%22fileName%22:%227661832460_05072018.png%22%7D

Related

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]

URLQueryItem value is optional

Hello I am trying to add queryItems to my URLComponents but because URLQueryItem returns its value as an optional the url keeps having a question mark in it.
var params = ["id":"5"]
let queryParams = toStringDictionary(dictionary: params).map { pair in
return URLQueryItem(name: pair.key, value: pair.value)
}
var components = URLComponents(string: fullURL)
components.queryItems = queryParams
print(components.url)//This is the full url https://demo.com/users/?5
//This https://demo.com/users/?5 should be this https://demo.com/users/5
The question mark is of course resulting in a wrong url. I can't get rid of it.
Query parameters are separated from the rest of the url by a question mark, for example:
https://example.com/path?param_name=param_value
The question mark is not due to an optional, but is the way that query params are supplied.
The example in your comment looks like it has lost the name of the param, I would expect it to read
https://demo.com/users/?id=5
If it definitely doesn't include the id= bit, please could you share the implementation of toStringDictionary so that we can see what's going on there? The dictionary you pass in is already a string dictionary, so seems odd to have such a function.
EDIT: following your clarifications (which have now disappeared, but made it clear that you were looking to add to the path rather than the query string)
It looks like your code is adding query parameters correctly; look closer at the URL it produces, it will be https://demo.com/users/?id=5. Query parameters are not part of the path, they come at the end and are separated from the rest of the URL by a ?
From your edits, it looks as though what you actually want to do is to add to the path of the URL, so that you get https://demo.com/users/5. That URL does not have any query parameters, so URLQueryItem is the wrong tool for the job.
To add to the path, you can do something like the following:
let userID = "5"
let url = URL(string: "https://example.com/users")!
let newUrl = url.appendingPathComponent(userID)
print(newUrl.absoluteString) //https://example.com/users/5
NB - url is only force unwrapped for brevity in the example, since the string passed to URL() is known to be valid. In real use, you should treat the optional more carefully.
There's something wrong with your toStringDictionary
Assuming
let fullURL = "https://demo.com/users/"
all you need to do is…
let queryParams = params.map{ URLQueryItem(name: $0, value: $1) }
var components = URLComponents(string: fullURL)!
components.queryItems = queryParams
print(components.url!)
// https://demo.com/users/?id=5
Note that you should avoid the force unwrapping in practice

iOS: Prevent URL from being percent escaped automatically?

I'm fetching some JSON data using URLSession.dataTask. To filter the data, I'm using a parameter that limits by date. The API uses square brackets for this, e.g.
https://foo.com/api/items.json?find[date]=....
However, upon creating a URL with the string and requesting data from it, the square brackets are automatically escaped:
https://foo.com/api/items.json?find%5Bdate%5D=...
With the brackets escaped, the API fails to recognize the request.
I've tried explicitly removing the percent encoding to create a new URL, i.e.
let unsanitized = URL(string: url.absoluteString.removingPercentEncoding)
but the percent sanitization persists.
I've spent a lot of time searching for a solution but haven't had any success—any suggestions?
Per the spec for URI: "A host identified by an Internet Protocol literal address, version 6 [RFC3513] or later, is distinguished by enclosing the IP literal within square brackets ("[" and "]")." rfc3986.
Thus you can't use square brackets for your purpose without escaping them. Your server's REST service is at fault for not handling the escaped characters in the query params. (And I've had situations in recent past where I've had to ask my REST team to fix this sort of problem where they forgot to support escaped query parameter values).
Try use percentEncodedQueryItems of URLComponents instead queryItems.
This is my codes in my app.
let fullUrl = baseURL.appendingPathComponent(path)
guard var components = URLComponents(url: fullUrl, resolvingAgainstBaseURL: false) else {
fatalError("Unable to create URL components")
}
var params = self.parameters // <-- parameters = [String:String]
params["serviceKey"] = Const.OPEN_API_KEY // <-- percent encoded key
components.percentEncodedQueryItems = params.map { // <-- already percent encoded Query Items
URLQueryItem(name: String($0), value: String($1))
}
guard let url = components.url else {
fatalError("Could not get url")
}

Google Maps API: String URL Enconding

I'm using the following code to search for a place:
let correctedAddress:String! = self.searchResults[(indexPath as NSIndexPath).row].addingPercentEncoding(withAllowedCharacters: CharacterSet.symbols)
let url = URL(string: "https://maps.googleapis.com/maps/api/geocode/json?address=\(correctedAddress)&sensor=false")
self.searchResults[(indexPath as NSIndexPath).row] returns a string like this: "São Paulo - State of São Paulo, Brazil"
The string that is the parameter of URL is like this (after the encoding):
https://maps.googleapis.com/maps/api/geocode/json?address=Optional(\%22State%20of%20S%C3%A3o%20Paulo%2C%20Brazil\%22)&sensor=false
If I search for this URL in my web browser, the JSON is shown. So I assume it is a valid URL, but the URL(string: ..) method always returns nil
I probably need to change my encoding, I tried using CharacterSet.urlPathAllowed but always return nil.
What kind of encoding I should use? Or there is something else wrong?
Try to remove the Optional(...) string in the url
https://maps.googleapis.com/maps/api/geocode/json?address=\%22State%20of%20S%C3%A3o%20Paulo%2C%20Brazil\%22&sensor=false
I believe that's the reason for the URL is returning nil.
This worked for me :
"https://maps.googleapis.com/maps/api/geocode/json?address=" + correctedAddress + "&sensor=false"

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