I am trying to upload an image from my iOS app written in swift and tried some codes I found about it but nothing works.
Uploading throung Postman works perfect. Here a screenshot of the request in Postman:
Uploading image with Postman
As you can see the API expects a the PUT request with a JSON with only one field called "avatar" and then the image in it. Also keep in mind the form-data. In the Headers I only send the token to do the authentication.
Said this, in the Swift code I try to send as params the array with only the "avatar" key with the image encoded:
func uploadImage(url: String){
let httpMethod: String = "PUT"
var token = getTokenFromNSUserDefaults()
var imageDataJpeg = UIImageJPEGRepresentation(self.myImage, 0.9)
var base64StringJpeg = imageDataJpeg.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0)) // encode the image
var base64StringJpeg2 = imageDataJpeg.base64EncodedStringWithOptions(nil) // encode the image
var imageDataPng = UIImagePNGRepresentation(self.myImage)
var base64StringPng = imageDataPng.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0)) // encode the image
var base64StringPng2 = imageDataPng.base64EncodedStringWithOptions(nil) // encode the image
// API Request
self.request(httpMethod, token: token, params:["avatar":base64StringPng2], url: url) { (succeeded: Bool, msg: String) -> () in
var alert = UIAlertView(title: "Success!", message: msg, delegate: nil, cancelButtonTitle: "Okay.")
if(succeeded) {
println("Success")
}
else {
println("Fail")
}
// Move to the UI thread
dispatch_async(dispatch_get_main_queue(), { () -> Void in
})
}
}
//// API REQUEST ////
func request(httpMethod: String, token: String, params: Dictionary<String, AnyObject>, url : String, postCompleted : (succeeded: Bool, msg: String) -> ()) {
var request = NSMutableURLRequest(URL: NSURL(string: url)!)
var session = NSURLSession.sharedSession()
request.HTTPMethod = httpMethod
var err: NSError?
if token != ""{
request.addValue("JWT \(token)", forHTTPHeaderField: "Authorization")
}
request.HTTPBody = NSJSONSerialization.dataWithJSONObject(params, options: nil, error: &err)
////Option 1
request.addValue("multipart/form-data", forHTTPHeaderField: "Content-Type")
request.addValue("multipart/form-data", forHTTPHeaderField: "Content-Disposition")
request.addValue("multipart/form-data", forHTTPHeaderField: "Accept")
////Option 2
//request.addValue("multipart/form-data", forHTTPHeaderField: "Content-Type")
//request.addValue("multipart/form-data", forHTTPHeaderField: "Content-Disposition")
//request.addValue("application/json", forHTTPHeaderField: "Accept")
var task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
println("Response: \(response)")
var strData = NSString(data: data, encoding: NSUTF8StringEncoding)
println("Body: \(strData)")
var err: NSError?
var msg = "No message"
let json: AnyObject? = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &err)
println("JSON: \(json)")
if(err != nil) {
println(err!.localizedDescription)
let jsonStr = NSString(data: data, encoding: NSUTF8StringEncoding)
println("Error could not parse JSON1: '\(jsonStr)'")
postCompleted(succeeded: false, msg: "Error")
}
else {
self.parseJson(json!)
postCompleted(succeeded: true, msg: "Works!")
return
}
})
task.resume()
}
Here's the log response from the server with always a 406 error message as follows (do not look at the "censored" url):
Response: <NSHTTPURLResponse: 0x7f967bed1450> { URL: https://***********************/ } { status code: 406, headers {
Allow = "GET, PUT, DELETE, HEAD, OPTIONS";
Connection = "keep-alive";
"Content-Type" = "application/json";
Date = "Wed, 02 Dec 2015 18:30:13 GMT";
Server = "nginx/1.4.6 (Ubuntu)";
"Transfer-Encoding" = Identity;
Vary = Accept;
"X-Frame-Options" = SAMEORIGIN;
} }
Body: Optional()
JSON: nil
The operation couldn’t be completed. (Cocoa error 3840.)
Error could not parse JSON1: 'Optional()'
Fail
Thank you very much for your help in advance!
Your "params" is not in JSON format - the curly braces for JSON dictionary are missing.
Related
Guys I am getting a response for an api call I make. it returns the whole response and I give it to the method like.
completionHandler(response: response, error: nil)
then by doing
print(response.description)
The result is that I get headers statuscode etc.
URL:
removed
Status Code:
200
Headers:
Keep-Alive: timeout=5, max=99
Content-Length: 2423
Server: Apache/2.4.10 (Debian)
SessionID: removed
Content-Type: application/json
Date: Thu, 24 Sep 2015 12:50:14 GMT
Connection: Keep-Alive
Cache-Control: no-cache
Payload:
[{"id":148,"name":"Amsterdam","avatar":"removed","cover":"removed"}]
The part I am mostly interested in is this part
Payload:
[{"id":148,"name":"Amsterdam","avatar":"removed","cover":"removed"}]
Somehow I can't extract that array of json objects. Anyone who can help me out on how to get this?
EDIT: added the method where I receive the response and pass it through
func requestObj(url: Routes, params: Dictionary<String, String>?, completionHandler: (response: Response?, error: NSError?) -> ())
{
self.requestConfig(completionHandler: { () -> () in
if let req = NSMutableURLRequest(urlString: self.config!.api!.baseUrl! + "/v2" + url.rawValue) {
do {
req.addValue(String(self.config!.api!.token!), forHTTPHeaderField: "Token")
req.addValue(String(self.sessionID), forHTTPHeaderField: "SessionID")
let opt = HTTP(req)
opt.start { response in
if let err = response.error {
print("error: \(err.localizedDescription)")
print("opt finished with error info: \(response.description)")
completionHandler(response: nil, error: nil)
}
completionHandler(response: response, error: nil)
//print("data is: \(response.data)") access the response of the data with response.data
}
}
}
}) // request a valid config before doing anything
}
This is called with
adapter.requestObj(APIAdapter.Routes.getMunicipalities, params: nil, completionHandler: {(
response, error) in
if let response = response {
print(response.description)
}
})
For SwiftHTTP, you can get the response body with:
response.data
Have you already tried with "SwiftyJson" you could save the data in a JSON object and then access the data as a Dictionary here's how I deal with the data
import Alamofire
import SwiftyJSON
func RequestImages()
{
Alamofire.request(.GET, "https://api.500px.com/v1/photos",parameters:["consumer_key":"gRU4LletUCA9RiOQhaJBAt62UyRRYUE6vsIcC7fO"])
.responseJSON { _,_,result in
switch result {
case .Success(let data):
let json = JSON(data)
debugPrint(data)
self.Photos = self.ParseJSON(json)
self.performSegueWithIdentifier("ToCollection", sender: self)
case .Failure(_, let error):
print("Request failed with error: \(error)")
}
}
}
here is some code where I use a library called Alamofire where i retrieve a response in JSON, then if data has be found i save the data in a JSON object provided by the SwiftyJSON library
let json = JSON(data)
then I have a collection of an "Image" mode called "Photos" i fill this collection by parsing the JSON data as the following
func ParseJSON(json:JSON)->[Image]
{
//Get Image_URL
var pictures = [Image]()
for result in json["photos"].arrayValue
{
pictures.append(Image(url: result["image_url"].stringValue, name: result["name"].stringValue, news: result["description"].stringValue))
}
debugPrint(pictures)
return pictures
}
I hope my implementation helps you Greetings!
Try this :-
override func viewDidLoad() {
super.viewDidLoad()
get_data_from_url("http://yourURL")
}
func get_data_from_url(url:String) {
let prefs:NSUserDefaults = NSUserDefaults.standardUserDefaults()
Id = label1.text //your request parameters
JId = label2.text //your request parameters
var post:NSString = "uid=\(Id)&jid=\(JId)"
//NSLog("PostData: %#",post);
var url:NSURL = NSURL(string: url)!
var postData:NSData = post.dataUsingEncoding(NSASCIIStringEncoding)!
var postLength:NSString = String( postData.length )
var request:NSMutableURLRequest = NSMutableURLRequest(URL: url)
request.HTTPMethod = "POST"
request.HTTPBody = postData
request.setValue(postLength as String, forHTTPHeaderField: "Content-Length")
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.setValue("*/*", forHTTPHeaderField: "Accept")
var reponseError: NSError?
var response: NSURLResponse?
var urlData: NSData? = NSURLConnection.sendSynchronousRequest(request, returningResponse:&response, error:&reponseError)
if urlData != nil && reponseError == nil {
let res = response as! NSHTTPURLResponse!;
//NSLog("Response code: %ld", res.statusCode);
if (res.statusCode >= 200 && res.statusCode < 300) {
var responseData:NSString = NSString(data:urlData!, encoding:NSUTF8StringEncoding)!
NSLog("Response ==> %#", responseData)
if Id != nil {
extract_json(urlData!)
}
} else {
var alertView:UIAlertView = UIAlertView()
alertView.title = "Sign in Failed!"
alertView.message = "Connection Failed"
alertView.delegate = self
alertView.addButtonWithTitle("OK")
alertView.show()
}
} else {
var alertView:UIAlertView = UIAlertView()
alertView.title = "Sign in Failed!"
alertView.message = "Connection Failure"
if let error = reponseError {
alertView.message = (error.localizedDescription)
}
alertView.delegate = self
alertView.addButtonWithTitle("OK")
alertView.show()
}
}
func extract_json(data:NSData) {
var error: NSError?
let jsonData:NSArray = NSJSONSerialization.JSONObjectWithData(urlData!, options:NSJSONReadingOptions.MutableContainers , error: &error) as! NSArray
let Id_temp: AnyObject? = ((jsonData)[0] as! NSDictionary)["id"]
let Name_temp: AnyObject? = ((jsonData)[0] as! NSDictionary)["name"]
}
First go to this site and validate your URL
https://chrome.google.com/webstore/detail/advanced-rest-client/hgmloofddffdnphfgcellkdfbfbjeloo?utm_source=chrome-app-launcher-info-dialog ( GOOGLE extenstion )
then replace below given values according to your settings :-
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.setValue("*/*", forHTTPHeaderField: "Accept")
Try with this:
NSDictionary *jsonDictionary = [NSJSONSerialization JSONObjectWithData:response options:0 error:&error];
Then you can do [jsonDictionary objectForKey:#""] to retrieve values.
This is Objective C, but I am sure it must be similar in Swift.
I have problem when i post the data to server, it seems doesn't work at all.. I've found the code thats the other use it for post
here is the code for post
func post(params : Dictionary<String, String>, url : String, postCompleted : (succeeded: Bool, msg: String) -> ()) {
var request = NSMutableURLRequest(URL: NSURL(string: "")!)
var session = NSURLSession.sharedSession()
request.HTTPMethod = "POST"
// let postString = "Body: \(personaldata.data)"
var err: NSError?
// request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
request.HTTPBody = NSJSONSerialization.dataWithJSONObject(params, options: nil, error: &err)
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
var task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
println("Response: \(response)")
var strData = NSString(CString: personaldata.data, encoding: NSUTF8StringEncoding)
println("Body: \(personaldata.data)")
var err: NSError?
var json = NSJSONSerialization.JSONObjectWithData(data, options: .MutableLeaves, error: &err) as? NSDictionary
var msg = "No message"
})
task.resume()
}
and when the button send pressed, here is the code
func tapGesture(gesture: UIGestureRecognizer) {
self.performSegueWithIdentifier("analisa", sender: self)
if let Kirim = gesture.view as? UIImageView { // if you subclass UIImageView, then change "UIImageView" to your subclass
self.post(["ID":"123", "function_name":"update_db", "personal_information_table":"\(personaldata.data)"], url: "") { (succeeded: Bool, msg: String) -> () in
}
}
}
the output when println correct already,but it doesn't appear at the website, how it could be?
I hide the website address ( it only shows line with id and weight , height on it that contains in(personaldata.data)
well my friend who working on android, have sent the data successfully :/
here is the website, and that is my friends data, not mine
is my code error or what?
here is the println
Response: <NSHTTPURLResponse: 0x7fe591554d60> { URL: } { status code: 500, headers {
"Accept-Ranges" = none;
"Cache-Control" = private;
"Content-Type" = "text/plain; charset=UTF-8";
Date = "Fri, 28 Aug 2015 09:34:02 GMT";
Server = "Google Frontend";
Vary = "Accept-Encoding";
"alt-svc" = "quic=\":443\"; p=\"1\"; ma=604800";
} }
Body: {"height": 214, "weight": 123}
Status code: 500 indicates that the error can only be resolved by fixes to the Web server software. It is not a client-side problem. It is up to the operators of the Web server site to locate and analyse the logs which should give further information about the error.
I need to upload an image to the server endpoint where the structure has to be as following:
{ "image": { "file": imageData }, "access_token": access_token }
How can I send such a request using NSURLSession (or maybe even Alamofire or AFNetworking)?
You cannot just include binary image data in a JSON request. JSON requires text representation, so if you do this, you must convert it to string (e.g. base64 encoding), use that in the JSON, and then the server code would presumably convert the base64 string back to binary data before attempting to use it.
But if you were base64 encoding of the image, it might look something like:
// get image data
let imageData = UIImagePNGRepresentation(image)
// convert to base64
let base64String = imageData.base64EncodedStringWithOptions(nil)
// build parameters
let parameters = ["image": ["file" : base64String], "access_token" : accessToken]
// get JSON
var error: NSError?
let data = NSJSONSerialization.dataWithJSONObject(parameters, options: nil, error: &error)
assert(data != nil, "Unable to serialize \(error)")
// build request
let url = NSURL(string: "http://example.com/upload")!
let request = NSMutableURLRequest(URL: url)
request.addValue("text/json", forHTTPHeaderField: "Content-Type")
request.HTTPMethod = "POST"
let task = NSURLSession.sharedSession().uploadTaskWithRequest(request, fromData: data) { data, response, error in
// check for basic connectivity errors
if error != nil {
println("error: \(error)")
return
}
// check for server errors
if let httpResponse = response as? NSHTTPURLResponse, let statusCode = httpResponse.statusCode as Int? {
if statusCode != 200 {
println("status code is \(statusCode)")
}
}
// check for details of app-level server response, e.g. if JSON that was dictionary:
var parseError: NSError?
if let responseObject = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: &parseError) as? [String : AnyObject] {
println(responseObject)
} else {
println("JSON parse failed: \(parseError)")
println("response was: \(response)")
let responseString = NSString(data: data, encoding: NSUTF8StringEncoding)
println("responseString was: \(responseString)")
}
}
task.resume()
If you use Alamofire, this is simplified:
// build parameters
let parameters = ["image": ["file" : base64String], "access_token" : accessToken] as [String : AnyObject]
// build request
let urlString = "http://example.com/upload"
Alamofire.request(.POST, urlString, parameters: parameters, encoding: .JSON)
.responseJSON(options: nil) { request, response, responseObject, error in
if error != nil {
println(error)
} else {
println(responseObject)
}
}
But both of the above are making assumptions about the nature of the response, that the server is base64 decoding the image data from the JSON, etc., but hopefully this illustrates the basic patterns.
Alternatively, use an application/x-www-form-urlencoded request, in which you can send binary data as illustrated here.
Found a solution using AFNetworking with help from https://stackoverflow.com/a/11092052/3871476
For others looking for the solution.
let manager = AFHTTPRequestOperationManager(baseURL: NSURL(string: url))
let request = manager.POST(url, parameters: param, constructingBodyWithBlock: {(formData: AFMultipartFormData!) -> Void in
formData.appendPartWithFileData(imgdata, name: "image[file]", fileName: "photo.jpeg", mimeType: "image/jpeg")
}, success: {(operation: AFHTTPRequestOperation!, responseObject: AnyObject!) -> Void in
//Success
}, failure: {(operation: AFHTTPRequestOperation!, error: NSError!) -> Void in
//Failure
println(error.localizedDescription)
})
The trick was to use the "image[file]" parameter.
Try this:
var request = NSMutableURLRequest(URL: NSURL(string: "https://\(IP):\(port)/")!)
var response: NSURLResponse?
var error: NSError?
//Adding the JSON String in HTTP Body
request.HTTPBody = NSJSONSerialization.dataWithJSONObject(jsonString, options: nil, error: &error)
request.timeoutInterval = (number as! NSTimeInterval)
request.HTTPMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("gzip", forHTTPHeaderField: "Accept-encoding")
let urlData = NSURLConnection.sendSynchronousRequest(request, returningResponse: &response, error: &error)
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 trying to connect into a webservice, making a post call. The JSON outputed is ok, according with jsonlint.com. The request is not being completed as I can see on my WS logs.
func create() -> Bool {
var error: NSError?
var url: NSURL = NSURL(fileURLWithPath: "\(WSUrl)/profiles.json")!
var successedOperation: Bool = false
var request: NSMutableURLRequest = NSMutableURLRequest(URL: url)
request.HTTPMethod = "post"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
var dataToJSON = "{\"profiles\":{\"email\":\"\(self.email)\",\"password\":\"\(self.password)\",\"password_confirmation\":\"\(self.passwordConfirmation)\",\"username\": \"\(self.username)\",\"age\": \"\(self.age)\",\"gender\":\"\(self.gender)\"}}"
println(dataToJSON)
request.HTTPBody = dataToJSON.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config)
var task = session.dataTaskWithRequest(request, completionHandler: { (data, response, error) -> Void in
println("Response: \(response)")
var strData = NSString(data: data, encoding: NSUTF8StringEncoding)
println("Body: \(strData)")
var error: NSError?
var json = NSJSONSerialization.JSONObjectWithData(data, options: .MutableLeaves, error: &error) as? NSDictionary
if error != nil {
println(error?.localizedDescription)
let jsonString = NSString(data: data, encoding: NSUTF8StringEncoding)
println("Error: \(jsonString)")
} else {
if let parseJSON = json {
var success = parseJSON
println("Success: \(success)")
} else {
let jsonString = NSString(data: data, encoding: NSUTF8StringEncoding)
println("Error could not parse JSON: \(jsonString)")
}
}
})
task.resume()
return successedOperation
}
The JSON outputed is alright:
{"profiles":{"email":"diegocharles#diegocharles.com","password":"abc1234","password_confirmation":"abc1234","username": "diegocharles","age": "18","gender":"Male"}}
And the error printed
"The operation couldn’t be completed. (Cocoa error 3840.)"
fileURL(withPath:) is used to create an URL for a file on disk. What you're looking for is URL(string:).