The following curl works
curl -G -H "api_key: MYAPIKEY" https://api.semantics3.com/test/v1/products -d 'q={"upc":"70411576937"}'
However, upon trying to convert it to iOS I get the following error:
Error Domain=NSURLErrorDomain Code=-1005 "The network connection was lost." {NSErrorFailingURLStringKey=https://api.semantics3.com/test/v1/products,...}
I have attached my code below but I believe that my problem is the "q=" right before the json data that is being submitted to the URL. If so, what is this called and how do I put "q=" before my json data? I can't exactly tell though, due to iOS' unfaltering ability to provide us with unrelated error messages. Thank you.
var urlString = "https://api.semantics3.com/test/v1/products"
var request = NSMutableURLRequest(URL: NSURL(string: urlString)!)
var response: NSURLResponse?
var error: NSErrorPointer = nil
var reqText = ["upc": "70411576937"]
var err: NSError?
request.HTTPBody = NSJSONSerialization.dataWithJSONObject(reqText, options: nil, error: &err)
request.HTTPMethod = "GET"
request.addValue("MYAPIKEY", forHTTPHeaderField: "api_key")
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
var session = NSURLSession.sharedSession()
var task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
var strData = NSString(data: data, encoding: NSUTF8StringEncoding)
if(err != nil) {
println(err!.localizedDescription)
}
else {
//this is where the error is printed
println(error)
var parseError : NSError?
// parse data
let unparsedArray: AnyObject? = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.AllowFragments, error: &parseError)
println(parseError)
if let resp = unparsedArray as? NSDictionary {
println(resp)
}
}
})
task.resume()
Body is not used in GET http methods. Use the following code to concat your params:
extension String {
/// Percent escape value to be added to a URL query value as specified in RFC 3986
///
/// This percent-escapes all characters besize the alphanumeric character set and "-", ".", "_", and "~".
///
/// http://www.ietf.org/rfc/rfc3986.txt
///
/// :returns: Return precent escaped string.
func stringByAddingPercentEncodingForURLQueryValue() -> String? {
let characterSet = NSMutableCharacterSet.alphanumericCharacterSet()
characterSet.addCharactersInString("-._~")
return self.stringByAddingPercentEncodingWithAllowedCharacters(characterSet)
}
}
extension Dictionary {
/// Build string representation of HTTP parameter dictionary of keys and objects
///
/// This percent escapes in compliance with RFC 3986
///
/// http://www.ietf.org/rfc/rfc3986.txt
///
/// :returns: String representation in the form of key1=value1&key2=value2 where the keys and values are percent escaped
func stringFromHttpParameters() -> String {
let parameterArray = map(self) { (key, value) -> String in
let percentEscapedKey = (key as! String).stringByAddingPercentEncodingForURLQueryValue()!
let percentEscapedValue = (value as! String).stringByAddingPercentEncodingForURLQueryValue()!
return "\(percentEscapedKey)=\(percentEscapedValue)"
}
return join("&", parameterArray)
}
}
var urlString = "https://api.semantics3.com/test/v1/products"
var reqText = ["upc": "70411576937"]
var err: NSError?
let parameterString = reqText.stringFromHttpParameters()
let requestURL = NSURL(string:"\(urlString)?\(parameterString)")!
var request = NSMutableURLRequest(URL: NSURL(string: urlString)!)
var response: NSURLResponse?
var error: NSError?
request.HTTPMethod = "GET"
request.addValue("MYAPIKEY", forHTTPHeaderField: "api_key")
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
var session = NSURLSession.sharedSession()
PARTIAL EDIT: SWIFT 2.1 (updated)
extension Dictionary {
func stringFromHttpParameters() -> String {
let parameterArray = self.map { (key, value) -> String in
let percentEscapedKey = (key as! String).stringByAddingPercentEncodingForURLQueryValue()!
let percentEscapedValue = (value as! String).stringByAddingPercentEncodingForURLQueryValue()!
return "\(percentEscapedKey)=\(percentEscapedValue)"
}
return parameterArray.joinWithSeparator("&")
}
}
Convert your JSON to a string, prepend the q= to this, then convert the resulting string to Data before assigning it to the request's HTTPBody.
Something like this perhaps:
let array = [ "one", "two" ]
let data = NSJSONSerialization.dataWithJSONObject(array, options: nil, error: nil)
let body= "q=" + NSString(data: data!, encoding: NSUTF8StringEncoding)
request.HTTPBody = body.dataUsingEncoding(NSUTF8StringEncoding)
Related
I am trying to add a GET request in my application.
The values or the final url string is flowing correctly in my sURL variable. But still while executing this code I get "Found nil error" at line - "var request = URLRequest(url: URL(string: sUrl)!)"
Please help.
My code -
class AllStickerService {
static let allStickerInstance: AllStickerService = AllStickerService()
var delegateAllSticker: AllStickerProtocol!
func fetchAllSticker(category: String, APITokenString: String) {
var sUrl = "http://xyzabc.com/api/stickers"
let params = ["category": category]
var sParams = ""
for (key,value) in params {
sParams += key + "=" + value
print("\(key), \(value)")
}
if !sParams.isEmpty {
sParams = "?" + sParams
sUrl = sUrl + sParams
}
var request = URLRequest(url: URL(string: sUrl)!)
print(request)
request.httpMethod = "GET"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.addValue("Bearer "+APITokenString, forHTTPHeaderField: "Authorization")
let session = URLSession.shared
session.dataTask(with: request) { (data, response, error) in
if (response as? HTTPURLResponse) != nil {
if let httpResponse = response as? HTTPURLResponse {
print("statusCode: \(httpResponse.statusCode)")
print(httpResponse)
}
if let data = data{
do {
guard let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else { return }
print(json)
}catch {
print("Error\(error)")
}
}
}
}.resume()
}
}
Force unwrapping is rarely a good idea. Either is using string concatenation to create URLs; Aside from potential security problems, you have to worry about things like url encoding, which is your problem here.
Your category parameter value has a space, this needs to be encoded as %20, but you don't do this and you end up with an invalid URL string. You don't see this with Postman because it is encoding the space for you behind the scenes.
A better approach is to use URLComponents, URLQueryItem and use conditional unwrapping
func fetchAllSticker(category: String, APITokenString: String) {
var sUrl = "http://xyzabc.com/api/stickers"
let params = URLQueryItem(name:"category", value: category)
if var urlComponents = URLComponents(string:"http://xyzabc.com/api/stickers") {
urlComponents.queryItems = params
if let url = urlComponents.url {
var request = URLRequest(url: url)
...
}
}
I would also recommend you look into using Decodable to handle your JSON response rather than JSONSerialization
Encoding an UIImage as a Base64 string works on the device, but transferring the string to the server somehow corrupts the string and prevents the server from successfully decoding the image.
Any suggestions on the problem?
// Define params
params["thumbnail_base64"] = imageToBase64(blockSet.thumbnailURL)
...
// Convert params -> query string
let postString = buildQueryString(params)
// Define upload URL
let uploadURL = NSURL(string: RootURL + UploadFilePath)!
// Hit server
let request = NSMutableURLRequest(URL: uploadURL)
request.HTTPMethod = "POST"
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
...
private func buildQueryString(parameters: [String:String], omitQuestionMark: Bool = false) -> String {
var urlVars = [String]()
for (k, var v) in parameters {
v = v.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())!
urlVars += [k + "=" + "\(v)"]
}
return ((urlVars.isEmpty || omitQuestionMark) ? "" : "?") + urlVars.joinWithSeparator("&")
}
private func imageToBase64(filename: String) -> String {
// Get image path
let imagePath = getFilePath(filename)
// Convert image to base64 or return empty string
if let imageData = NSData(contentsOfFile: imagePath) {
let base64String = imageData.base64EncodedStringWithOptions(.EncodingEndLineWithLineFeed)
return base64String
} else {
printError("Error converting image to Base64: missing image. Filename: \(filename)")
return ""
}
}
The problem is with the queryString, base64 is long text with many characters, let JSON do the work for you
Use the next (with some example of NodeJS)
let params = NSMutableDictionary();
//you can only set `serializable` values
params.setValue(imageToBase64(),forKey:"base64")
params.setValue(username,forKey:"username")
params.setValue(["array","of","string"],forKey:"arr")
let uploadURL = NSURL(string: theURL)!
// Hit server
let request = NSMutableURLRequest(URL: uploadURL)
request.HTTPMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
do {
let jsonData = try NSJSONSerialization.dataWithJSONObject(params, options: NSJSONWritingOptions(rawValue: 0))
request.HTTPBody = jsonData
let session = NSURLSession.sharedSession()
session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
print("Response: \(response)")
}).resume()
} catch let error as NSError {
print(error)
}
nodejs:
var buffer = new Buffer(request.body["base64"], 'base64')
fs.writeFile('test.jpeg',buffer,"base64"); //Works
var username = request.body["username"];
var someStringsArr = request.body["arr"]
by the way...
you wrote the function buildQueryString, which is already exists in Foundation
let urlComponents = NSURLComponents(string: "http://myUrl.com/getApi/")!
urlComponents.queryItems = [NSURLQueryItem]()
urlComponents.queryItems!.append(NSURLQueryItem(name:"myKeyA",value:"myValueA"))
urlComponents.queryItems!.append(NSURLQueryItem(name:"myKeyB",value:"myValueB"))
print(urlComponents.URL!) //http://myUrl.com/getApi/?myKeyA=myValueA&myKeyB=myValueB
Use url query if want to send GET parameters via the URL
How to pass json as parameter string. I have tried by passing json as below but it throws error like AuthenticateUser: Invalid JSON primitive.
let jsonString = "{\"user\":\"usr\",\"password\":\"pass\"}"
var urlStr = "http://testserver/AuthenticateUser?data=\(jsonString)"
var url = NSURL(string: urlStr)
let request = NSMutableURLRequest(URL: url!)
request.URL = url
request.HTTPMethod = "POST"
request.addValue("application/xml", forHTTPHeaderField: "Content-Type")
request.addValue("application/xml", forHTTPHeaderField: "Accept")
let task = NSURLSession.sharedSession().dataTaskWithRequest(request)
{
(data, response, error) in
var error: NSError?
if data != nil {
var reply = NSString(data: data!, encoding: NSUTF8StringEncoding)
println("reply >> \(reply)")
}
}
task.resume()
Add this line after request initializtion:
request.HTTPBody = NSJSONSerialization.dataWithJSONObject(jsonString, options: nil, error: &err)
Update these lines for json format:
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
Reference:
POST with swift and API
http://jamesonquave.com/blog/making-a-post-request-in-swift/
I brought you answer up to passing json parameter as a string to URL.
var jsonString = "{\"user\":\"usr\",\"password\":\"pass\"}"
if let data = jsonString.dataUsingEncoding(NSUTF8StringEncoding)
{
var error: NSError?
var json = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.allZeros, error: &error) as? NSDictionary
if error != nil
{
println(error)
}
else
{
println(json)
}
var strUser = json?.valueForKey("user") as String
var strPassword = json?.valueForKey("password") as String
println(strUser)
println(strPassword)
var url = "http://testserver/AuthenticateUser?data="
var urlUserParameter = "user="
var urlPasswordParameter = "&password="
var appendString = "\(url)\(urlUserParameter)\(strUser)\(urlPasswordParameter)\(strPassword)"
//OR
var appendStringOne = url + urlUserParameter + strUser + urlPasswordParameter + strPassword
println(appendString)
println(appendStringOne)
}
Explanation
I converted JSON string to NSDictionary.
Then i get value using key
Finally i append all these string and now we have URL with parameters.
So Im in the early stages of learning swift, and I'm trying to make a trivial class to wrap the process of sending/retrieving data from a given web service. The issue I'm having is that nothing is printing to console after I have sent the request, or any kind of response for that matter. I would really appreciate any help or guidance as to what I am doing wrong
import Foundation
class URLHelper : NSObject,NSURLConnectionDelegate,NSURLConnectionDataDelegate{
var data = NSMutableData()
func sendReq(){
let urlPath: String = "http://localhost/web-service/action.php?callback=showUserDetails&uid=1"
var url: NSURL = NSURL(string: urlPath)!
var request: NSURLRequest = NSURLRequest(URL: url,cachePolicy: NSURLRequestCachePolicy.ReloadIgnoringLocalCacheData, timeoutInterval: 4)
var connection: NSURLConnection = NSURLConnection(request: request, delegate: self, startImmediately: false)!
connection.start()
}
func connection(connection: NSURLConnection!, didReceiveData data: NSData!){
self.data.appendData(data)
}
func connection(connection: NSURLConnection, didFailWithError error: NSError) {
println(error.description)
}
func connectionDidFinishLoading(connection: NSURLConnection!) {
var err: NSError
var jsonResult: NSDictionary = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as NSDictionary
println(jsonResult)
}
}
var req = URLHelper()
req.sendReq()
UPDATE
<?php
//Get the action to run the coorect request
if(isset($_GET['callback'])){
$function = $_GET['callback'];
call_user_func($function);
//$function();
}else{
echo "Error: No valid callback supplied to request";
}
function showUserDetails(){
$conn = mysqli_connect("localhost", "root", "root", "service_db") or die("Error " . mysqli_error($conn));
$userid = $_GET['uid'];
$results = mysqli_fetch_assoc(mysqli_query($conn,"SELECT * FROM user WHERE id = $userid"));
mysqli_close($conn);
echo json_encode($results);
}
if($db->connect_errno > 0){
die('Unable to connect to database [' . $db->connect_error . ']');
}
?>
The returned json is as follows {"id":"1","username":"tom","email":"tom_smith#gmail.com"}
For posting you don't need to make a request, just get content from the url:
var strResult:NSString
let strURL = "http://localhost/web-service/action.php?callback=showUserDetails&uid=1"
var dataURL = NSData(contentsOfURL: NSURL(string: strURL)!);
if let d = dataURL
{
strResult = NSString(data: d, encoding: NSUTF8StringEncoding)!
println(strResult)
}
Here i simply go to the url, get the content and store it in the data object, and then turning it into a string and printing it. You can also decode the JSON string (strResult) into the JSON object.
Hope it helps :)
You can use this method which have completionHandler to get back the result from your web service
func postAsync(backendMethodName:String ,body: [[String:String]], completionHandler: (resultnig:String) -> Void)
{
let session: NSURLSession = NSURLSession.sharedSession()
let urlPath: String = "\(sessionclass.connectionString)/\(backendMethodName)"
let request = NSMutableURLRequest(URL: NSURL(string:urlPath)!)
request.HTTPMethod = "POST"
request.timeoutInterval=10
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.HTTPBody = try! NSJSONSerialization.dataWithJSONObject(body, options: [])
let task = session.dataTaskWithRequest(request) { data, response, error in
guard data != nil else
{
print("no data found: \(error)")
return
}
let strData = NSString(data: data!, encoding: NSUTF8StringEncoding)
completionHandler(resultnig: strData as! String)
}
task.resume()
}
its take the backend method Name (in web service) and your jsonData
you can call it like this :
self.postAsync("checkConnection", body: self.alldictionariesConn, completionHandler: { (resultnig) in
print(resulting) })//resulting = result from your web service
I'm using the following function to make post an HTTP request in JSON for my application (uploading photo):
static func HTTPPostJSON(url: String,
jsonObj: AnyObject,
callback: (String, String?) -> Void) {
var request = NSMutableURLRequest(URL: NSURL(string: url)!)
request.HTTPMethod = "POST"
request.addValue("application/json",
forHTTPHeaderField: "Content-Type")
let jsonString = JSONStringify(jsonObj)
let data: NSData = jsonString.dataUsingEncoding(
NSUTF8StringEncoding)!
request.HTTPBody = data
HTTPsendRequest(request, callback)
}
jsonStringify:
static func JSONStringify(jsonObj: AnyObject) -> String {
if((jsonObj as? [Dictionary<String, AnyObject>]) != nil || (jsonObj as? [Array<AnyObject>]) != nil){
var e: NSError?
var jsonData = NSJSONSerialization.dataWithJSONObject(jsonObj, options: NSJSONWritingOptions(0), error: &e);
if e != nil {
println(e);
return "\(jsonObj)";
} else {
return NSString(data: jsonData!, encoding: NSUTF8StringEncoding)!
}
} else {
return "\(jsonObj)";
}
}
In this case, the callback function receives the parameter "Bad Request", though I don't know why it happened. Is there a better working way to make an HTTP photo post request in JSON?