Reputation: 15904
Please consider below code that I implemented in my iOS App:
let kBaseURL = "https://www.sampledomain.com/"
let method = "api/MasterData/GetCompanyList?jsonArgs={\"AccountID\":%d}"
private func prepareURL(api: String) -> String {
return (kBaseURL + api).addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
}
In above code, "method" has "%d" which will be replaced by account id (at run time) that is an integer.
let accountID = 20
let methodToCall = String(format: method, accountID)
var apiRequest = URLRequest(url: URL(string: prepareURL(api: methodToCall))!)
Above code is crashing as URL is nil which is due to "method" that has special characters in it.
Crash reason given by Xcode is: Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
Upvotes: 2
Views: 1376
Reputation: 804
As found in Apple's doc, if you have some characters in the string which are illegal in a URL, URL(string:) returns nil. At this case, you need to % encode as follows.
let url = URL(string: "123\"456") // this returns nil due to double quote
So you need to mofify to
let url = URL(string: "123\"456".addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!)
I came across the same issue when I was handling non-English letters; for my case they are "Kanji". So, I always use percent encoding w/o checking what's in the string when calling URL(string:). Actually, there are so many illegal URL letters w/ "Kanji" and it's next to impossible to check what's legal and what's not manually. URL(string:) w/ the percent encoded string by addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) always works nicely for me.
Upvotes: 1
Reputation: 16794
You need to encode those characters as well. You can check what percent escaped values are for {
and }
and put them in manually. But I rather suggest you have the ability to create a query string from dictionary. Something like the following should fix your issue:
func createQueryString(from parameters: [String: String]) -> String {
let components: [String] = parameters.compactMap { item in
guard let encoded = item.value.addingPercentEncoding(withAllowedCharacters: CharacterSet(charactersIn: "!*'\"(){};:@&=+$,/?%#[]% ").inverted) else { return nil }
return item.key + "=" + encoded
}
return components.joined(separator: "&")
}
Now all you need to do is use it and ensure jsonArgs={\"AccountID\":%d}
is used in a dictionary as well. Try something like:
func generateAccountsRequest(accountID: Int) -> URLRequest {
let kBaseURL = "https://www.sampledomain.com"
let endpoint = "api/MasterData/GetCompanyList"
func prepareURL(endpoint: String, query: [String: String]) -> String {
return kBaseURL + "/" + endpoint + "?" + createQueryString(from: query)
}
return URLRequest(url: URL(string: prepareURL(endpoint: endpoint, query: ["jsonArgs": "{\"AccountID\":\(accountID)}"]))!)
}
Upvotes: 0