Somehow I've ended up completely stuck on what should be an easy REST call. I have a restful service (in node) that is expecting an array of strings. Every time I call with what appears to be a valid parameter, I get an error from the server that looks like it was trying to parse the string as an integer, which makes me think it is losing the quotes around the string somewhere.
Here's the original Swift (3) code (with two more variants at the end). "id" and "parmvalue" are passed to this function.
let url = URL(string: "\(root)path/\(id)/myendpoint")
var request = URLRequest(url:url!)
request.httpMethod = "POST"
request.addValue("application/json",forHTTPHeaderField: "Content-Type")
let params = [ "parm" : [ parmvalue ] ]
do {
let jsonParams = try JSONSerialization.data(withJSONObject: params)
request.httpBody = jsonParams
let task = URLSession.shared.dataTask(with: request, completionHandler: {(data, response, error) -> Void in
...etc
Here's the server code:
app.post('/path/:id/myendpoint', globals.authentication, function(req, res){
var param = JSON.parse(req.param('parm', "[]"));
var Id = req.param('id', -1);
...etc
Passing in a parmvalue = "898e1ac8-4892-4e6e-89cb-4b2ea9306f75.jpg", I get a 500 response from the server, and the data passed to the completionHandler for the dataTask looks like:
SyntaxError: Unexpected token e
and a stack trace pointing right at that "var param" line. The unexpected token that is returned almost always corresponds to the first non-numeric value in the guid.
Here was variant 1 (just hardcode the json, it isn't that complicated)
let url = URL(string: "\(root)path/\(id)/myendpoint")
var request = URLRequest(url:url!)
request.httpMethod = "POST"
request.addValue("application/json",forHTTPHeaderField: "Content-Type")
let testparamsString = "{ \"photos\" : [ \"\(filename)\" ] }"
let testparams = testparamsString.data(using: .utf8)
request.httpBody = testparams
...etc
And here was variant 2 (if json doesn't work, try query param format)
let url = URL(string: "\(root)path/\(id)/myendpoint")
var request = URLRequest(url:url!)
request.httpMethod = "POST"
let queryparams = "parm[]=\(filename.stringByAddingPercentEncodingForFormUrlencoded()!)"
request.httpBody = queryparams.data(using: .utf8)
(note that all of the code fragments have been somewhat anonymized wrt variable names and values, and all three variations in Swift generate the same response from the server, so if there's something wonky in a particular fragment, it's probably my anonymizing by hand)
Just for grins, here's part of a C# app that also calls this endpoint (using the RestSharp library) and works - so I pretty much trust that the server code is correct:
var request = new RestRequest($"/path/{id}/myendpoint", Method.POST);
var parmArray = new JArray();
parmArray.Add(parmvalue);
var jsonParms = JsonConvert.SerializeObject(parmArray, Formatting.None);
request.AddParameter("parm", jsonParms);
Variant 1 was close, but it needed an extra (escaped) backslash to keep those quotes in there. Here's what I ended up with:
let url = URL(string: "\(root)path/\(id)/myendpoint")
var request = URLRequest(url:url!)
request.httpMethod = "POST"
request.addValue("application/json",forHTTPHeaderField: "Content-Type")
let paramsString = "{ \"photos\" : \"[\\\"\(filename)\\\"]\" }"
request.httpBody = paramsString.data(using: .utf8)
Related
I am trying to use following code for sending some information to the server.
var request = URLRequest(url: URL(string: GlobalVariable.globalIpAdresDispatch)!)
request.httpMethod = "POST"
request.addValue("application/x-www-form-urlencoded; Charset=utf-8", forHTTPHeaderField: "Content-Type")
let postString = "saveBeyanname" + "&jp=" + myDraft + "&token=" + GlobalVariable.globalToken
request.httpBody = postString.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlFragmentAllowed)?.data(using: .utf8)
myDraft variable comprises of a json string which includes ö,ç,i,ü letters. However when I send these information to server it is not accepting and they are converting ÅiÄüçö meaningless information. I have searched a lot and looked similar questions but still not working. How can I create my post string to send a server in a correct way?
After updating my code i got stuck with this error.
"Argument labels '(_:)' do not match any available overloads"
var request = NSMutableURLRequest(url: baseURL!)
request.httpMethod = "POST"
request.httpBody = soapBodyUrlEncoded.data(using:String.Encoding.utf8)
request.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
let operation = AFHTTPSessionManager(request) //Error in this line
Could anyone help me to fix this.
Thanks in Advance
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)")
I send post request with data to my server:
var syncData = "data=Hello&World"
let urlRequest = NSMutableURLRequest(URL: NSURL(string: url)!, cachePolicy: NSURLRequestCachePolicy.ReloadIgnoringCacheData, timeoutInterval: NSTimeInterval(60))
urlRequest.HTTPMethod = "POST"
urlRequest.HTTPBody = syncData.dataUsingEncoding(NSUTF8StringEncoding)
But problem that server can't decode &(ampersand) character. On server i get only word "Hello". And it is not the server issue. because i tried to sent this string with java and python and curl.
All works good.
I tried this:
NSXMLParser problem with "&"(Ampersand) character
but it doesn't help... Any ideas ?
As Musa said. Solution will be:
let customAllowedSet = NSCharacterSet(charactersInString:"=\"#%/<>?#\\^`{|}&").invertedSet
syncData = syncData.stringByAddingPercentEncodingWithAllowedCharacters(customAllowedSet)!
urlRequest.HTTPBody = syncData.dataUsingEncoding(NSUTF8StringEncoding)
try
var sync = syncData.stringByReplacingOccurrencesOfString("&", withString: " ")
urlRequest.HTTPBody = sync.dataUsingEncoding(NSUTF8StringEncoding)
place the sync variable under the syncData variable and replace your urlRequest with the one above using the new sync variable
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")