I'm trying to send an uploaded image to server. I'm getting the image from photos successfully, and I'm attaching it to a UIImageView in ViewController. Now I need to send this image to server along with other data. I'm able to send all data successfully except the image.
Here is my func:
func placeOrder(withOrder: Order) {
let returnedJobId: String? = UserDefaults.standard.object(forKey: "jobId") as? String
let returnedOrderPrice: String? = UserDefaults.standard.object(forKey: "orderPrice") as? String
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let currentDateTime = formatter.string(from: Date())
let selectedImage = imagePlaceHolder.image!
let uploadedFile = selectedImage.jpegData(compressionQuality: 0.5)
DispatchQueue.main.async {
let date = self.chosenTimeDateTextFieldDisplay.text!
let address = self.addressField.text!
let phone = self.phoneField.text!
let comments = self.commentsEntryView.text!
let file = uploadedFile
let jobId = returnedJobId!
let price = returnedOrderPrice!
let headers = [
"content-type" : "multipart/form-data",//application/x-www-form-urlencoded",
"cache-control": "no-cache",
"postman-token": "dded3e97-77a5-5632-93b7-dec77d26ba99"
]
let user = CoreDataFetcher().returnUser()
let provider = user.provider_id
let userID = user.id
let userType = user.user_type
let postData = NSMutableData(data: "data={\"user_type\":\"\(userType)\",\"job_id\":\"\(jobId)\",\"user_id\":\"\(userID)\",\"provider_id\":\"\(provider)\",\"order_placing_time\":\"\(currentDateTime)\",\"order_start_time\":\"\(date)\",\"order_address\":\"\(address)\",\"order_phone\":\"\(phone)\",\"order_comments\":\"\(comments)\",\"order_price\":\"\(price)\",\"$_FILES\":\"\(file!)\"}".data(using: String.Encoding.utf8)!)
let request = NSMutableURLRequest(url: NSURL(string: "http://Api/v2/placeOrder")! as URL,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
print(error!)
} else {
if let dataNew = data, let responseString = String(data: dataNew, encoding: .utf8) {
print(responseString)
DispatchQueue.main.async {
do {
let fetcher = CoreDataFetcher()
let json = try JSON(data: data!, options: .allowFragments)
let answer = json["answer"]
let status = json["status"]
let orderID = answer.int!
if status == "ok" {
print("Status is OK")
}
fetcher.addOrderID(orderId: orderID, toOrder: withOrder)
print("Order id has been saved!")
} catch {
print("Order ID Counldn't be Saved!")
}
}
}
}
})
dataTask.resume()
}
}
This method isn't working. as it should be made a multipart form data
How to rewrite my func to be a multipart form data to convert the image to PNG file and attach it in the API request?
The answer to your question strongly depends on how you are trying to send your data to server. Ironically you have provided no information about that at all. If this is JSON based API then two most likely solutions are:
You will need to convert your data to base64 string
You will need to send a multipart form data
This should all be covered somewhere in the documentation of your API. For base64 things are very simple; you can use image.pngData()?.base64EncodedString(). The multipart form data is a bit more complicated but you can find a lot of posts on it like this one.
For non-JSON I guess you could just POST raw data with correct content type header. But this again depends on the API implementation.
In any case you could also try to find what the error is if any.
Related
I set up the API and all, the only thing is Bearer Token I couldn't find any information about any code on how to implement it so it can validate the URL I am using as API.
do I need to create new swift file just for bearer token or I can write the code to the API swift file "the code below is api file"
static let shared = APICaller()
private let baseURL = "http://000.000.000.000:3030/api/"
private init() {}
var vehicles = [Vehicles]()
func getVehicles(for id: String, IMEI: Int, completed: #escaping (Result<[Vehicles],Errors>) -> Void ){
let endpoint = baseURL + "GetVehicle/?UserIdentificationValue=346HIU4623UIHG3I3I&IMEI=216216123612"
guard let url = URL(string: endpoint) else {
completed(.failure(.invalidURL))
return
}
let task = URLSession.shared.dataTask(with: url) { data, response, error in
if let _ = error {
completed(.failure(.unableToComplete))
return
}
guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {
completed(.failure(.invalidResponse))
return
}
guard let data = data else {
completed(.failure(.invalidData))
return
}
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
self.vehicles = try JSONDecoder().decode([Vehicles].self, from: data)
DispatchQueue.main.async {
completed(.failure(.invalidData))
}
} catch {
completed(.failure(.invalidData))
}
}
task.resume()
Thanks In Advance
Also I am new to swift so I would appreciate if you can tell me my API code is correct or needs any fixes since its about receiving some car info and putting into a table view cell :)
I have attached the request including headers in which you need to pass Bearer token like did in below code
let headers = [
"content-type": "application/json",
"authorizetoken": "NjQzOPA2N0NDNDFAH4CNDk3R23F2FQUY0NjV3FFE=",
"cache-control": "no-cache",
]
let parameters = ["id":"123456789"] as [String : Any]
let postData = try? JSONSerialization.data(withJSONObject: parameters, options: [])
let request = NSMutableURLRequest(url: NSURL(string: "Your URL")! as URL,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 120.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as? Data
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringCacheData
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringLocalCacheData
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringLocalAndRemoteCacheData
I've made a little App in Swift where a user can search the Spotify database for songs. I am using the Web API Console > Search for an Item. My problem is the new OAuth system where you have to sign-in and all that stuff. My authorization is ok, but when I'm trying to get an access token with the following code, it's returning me the following error: {"error":"server_error","error_description":"Unexpected status: 400"}. My code is:
let keys = "<MY_APPLICATION_KEYS>"
let url = NSURL(string: "https://accounts.spotify.com/api/token")
let session = URLSession.shared
let request = NSMutableURLRequest(url: url! as URL)
request.httpMethod = "POST"
request.setValue("Basic \(keys)", forHTTPHeaderField: "Authorization")
request.setValue("client_credentials", forHTTPHeaderField: "grant_type")
let task = session.dataTask(with: request as URLRequest) { (data, response, error) in
guard let _: Data = data, let _: URLResponse = response, error == nil else {
print(error!)
return
}
let dataString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
print("Data: \(dataString!)")
self.parseData(JSONData: data!)
}
task.resume()
}
var accessToken = ""
func parseData(JSONData : Data) {
do {
var readableJSON = try JSONSerialization.jsonObject(with: JSONData, options: .mutableContainers) as! JSONStandard
if let token = readableJSON["access_token"] as? String {
accessToken = token
}
print("Access Token: \(accessToken)")
updateTokenInFirebase()
}
catch{
print(error)
}
Any help would be very appreciated, thank you very much in advance!
Documentation of the Web API: Web API Link
I am using on the Client Credentials Flow the first method.
I know it's been ~1 year since you posted this but I had the same issue and after a few tries was able to get it. You can test this in Playground.
import Foundation
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
if let url = URL(string: "https://accounts.spotify.com/api/token") {
var postRequest = URLRequest(url: url)
postRequest.httpMethod = "POST"
let bodyParams = "grant_type=client_credentials"
postRequest.httpBody = bodyParams.data(using: String.Encoding.ascii, allowLossyConversion: true)
let id = "your client id"
let secret = "your secret"
let combined = "\(id):\(secret)"
let combo = "\(id):\(secret)".toBase64()
postRequest.addValue("Basic \(combo)", forHTTPHeaderField: "Authorization")
let task = URLSession.shared.dataTask(with: postRequest) { (data, response, error) in
guard let data = data else {
return
}
print(String(data: data, encoding: String.Encoding.utf8)!)
}
task.resume()
}
extension String {
func fromBase64() -> String? {
guard let data = Data(base64Encoded: self) else {
return nil
}
return String(data: data, encoding: .utf8)
}
func toBase64() -> String {
return Data(self.utf8).base64EncodedString()
}
}
I know this is really late, but the issue is with this line:
request.setValue("client_credentials", forHTTPHeaderField: "grant_type")
According to the authorization guide, this should be in the body of the request, not the headers.
I have an app where user selects 5 imagesfrom photo library, then i send to send those 5 UIImages to a nodejs server and store them in a mysql database, however i am getting many issues with this approach when i fetch the data from the database and try to convert back to uiimage, also many people here have said that its better to store the image url or path to image in the database instead of the actual image , so i have decided to try that approach however i do not know how to get an image url? Or how to get the path of the image? My app still needs to send an image from the iOS device, to nodejs server, store the actual image, and retrieve the images so other users can see it but where and how do i store the image?
UPDATE: so my issue is I start with a UIImage in swift, convert it to base64 string representation, send to nodejs server, and insert it into mysql database, but when i send the base64 string representation from the mysql table back to the ios device i get an error trying to convert the string back to a uiimage, i convert the string back to Data object and then try to create a uiimage from that data object but the uiimage is always nil, and i dont get an error description from xcode so i do not know how to go about this?
func sendToNodeServer()
{
let url: URL = URL(string: "http://localhost:8081/testInsert")!
let session = URLSession.shared
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringCacheData
let imageData:NSData = UIImagePNGRepresentation(imV.image!)! as NSData
let strBase64:String = imageData.base64EncodedString(options: .lineLength64Characters)
let paramString = "pic=" + strBase64
request.httpBody = paramString.data(using: String.Encoding.utf8)
let task = session.dataTask(with: request)
{ data, response, error in
if error != nil
{
print("error in web request")
}
else
{
}
}//completion handler end
task.resume() //start the web reuqest
}
func getFromNodeServer()
{
let url: URL = URL(string: "http://localhost:8081/testgrab")!
let session = URLSession.shared
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringCacheData
let task = session.dataTask(with: request)
{ data, response, error in
if error != nil
{
print("error in web request")
}
else
{
DispatchQueue.main.async
{
self.parseWeb2(stuff: data) //stoer results in class member
}
}
}//completion handler end
task.resume() //start the web reuqest
}
func parseWeb2(stuff: Data?)
{
if stuff != nil
{
if let dataAsAny: NSArray = try? JSONSerialization.jsonObject(with: stuff!, options: .mutableContainers) as! NSArray
{
let dic: [String: AnyObject] = dataAsAny[0] as! [String: AnyObject]
let str: String = dic["value"] as! String
let data: NSData = NSData(base64Encoded: str, options: NSData.Base64DecodingOptions.ignoreUnknownCharacters)!
//ERROR, UIImage is always nil
let p: UIImage = UIImage(data: data as Data)!
imV.image = p
}
}
}
//nodejs code
//import modules
var path = require("path");
var bodyParser = require('body-parser');
var express = require('express');
var mysql = require("mysql");
var fs = require('fs');
var app = express();
app.use(bodyParser.json({limit: "50mb"}));
app.use(bodyParser.urlencoded({limit: "50mb", extended: true, parameterLimit:50000}));
var con = mysql.createConnection(
{ /*my credentials*/ } );
app.post('/testInsert', function (req, res)
{
var queryString = "Insert into Test(value) VALUES ( ? );";
con.query(queryString,[req.body.pic],function(err,rows)
{
if(err) throw err;
});//CON.QUERY end
});
app.post('/testgrab', function (req, res)
{
var queryString = "select test.value from test where id = 3;";
con.query(queryString,function(err,rows)
{
if(err) throw err;
res.send(rows);
});//CON.QUERY end
});
//mysql has 1 table called test with 2 colums (id,value) of type (int, medium text)
I solved my problem, the issue was that base 64 string contains the characters +/= which aren't allowed in URL encoding so i had to convert my base 64 string to correct url encoded format then i upload the string and can retrieve it correctly, to url encode the string i added percent escapes for the +=/ chars
I'm trying to send data from my app to a rest API that is also being used by an Android app developed by another programmer. I have the JSON being converted into an NSData object using NSJSONSerialization.dataWithJSONObject and then attaching it to a NSURLRequest but the NSData object is a hexadecimal representation of the JSON String. According to the other developer his Android code is creating and transmitting the JSON object in UTF-8 encoding, so my question is how do I either send the JSON string as UTF-8 text or what is the best way to make the API able to handle both sources as seamlessly as possible?
EDIT: The code that I'm using now
func postToServer() {
let endPoint: String = "http://server.com"
guard let url = NSURL(string: endPoint) else {
print("ERROR: cannot create URL")
return
}
let urlRequest = NSMutableURLRequest(URL: url)
urlRequest.HTTPMethod = "POST"
urlRequest.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
let loc = self.getLocation()
var content:[String: AnyObject] = ["action": "put-point", "request": ["rangeKey": self.id, "lng": loc.coordinate.longitude, "lat": loc.coordinate.latitude, "count": self.count]]
var data: NSData! = NSData()
do {
data = try NSJSONSerialization.dataWithJSONObject(content, options: NSJSONWritingOptions())
print(data)
} catch {
print ("Error")
}
urlRequest.HTTPBody = data
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config)
let task = session.dataTaskWithRequest(urlRequest, completionHandler:{ data, response, error in
guard error == nil else {
print("ERROR: Cannot call Get on endpoint")
print(error)
return
}
guard let responseData = data else {
print("ERROR: Did not receive any data")
return
}
print("DATA: \(data)")
})
task.resume()
}
You could do something like
let jsonObj = [...]
var data = NSData()
do {
data = try NSJSONSerialization.dataWithJSONObject(jsonObj, options: .PrettyPrinted)
let dataString = NSString(data: data, encoding: NSUTF8StringEncoding)!
} catch {
print("error: \(error)")
}
*Tried on Swift 2 & Xcode 7.3.1
Im currently struggling with a small issue on my viewController. I have stored user information to hold the users logged in status, anyways when the user opens the app they are shown a "Profile" page. Im trying to (in this example return the email) return values related to that user as the app is opened but dont know how one would execute the code on the controller itself, my JSON is posted below and returns the correct needed information. However my Label will still not get the value of the users Email stored when they open the app. Label stays blank!
let myUrl = NSURL(string: "http://www.mywebsite.co.za/php/scripts/getuserEmail.php");
let request = NSMutableURLRequest(URL:myUrl!);
request.HTTPMethod = "POST";
let postString = "user_id=\(userId)";
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding);
let task = NSURLSession.sharedSession().dataTaskWithRequest(request)
{
data, response, error in
println(" response = \(response)")
let responseString = NSString(data: data, encoding: NSUTF8StringEncoding)
println("response data = \(responseString)")
var err: NSError?
var json = NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers, error: &err) as? NSDictionary
if let parseJSON = json
{
var userEmail = parseJSON["userEmail"] as? String
self.usersEmail.text = userEmail
}
}
Would basically like the code to execute and update my label as that view opens, as the user has the ability to change the email from another viewController already so the email should update accordingly. Any other information will gladly be provided.
Added: println to console will not execute past the assigning of the myUrl variable. The reply below will still not work however this is my full code on viewWillAppear:
override func viewWillAppear(animated: Bool) {
super.viewDidAppear(animated)
let userFirstName = NSUserDefaults.standardUserDefaults().stringForKey("userFirstName")
let userLastName = NSUserDefaults.standardUserDefaults().stringForKey("userLastName")
var userFullName = userFirstName!
userFullNameLabel.text = userFullName
let userId:String? = NSUserDefaults.standardUserDefaults().stringForKey("userId")
let imageUrl = NSURL(string:"http://www.mywebsite.co.za/profile-pictures/\(userId!)/user-profile.jpg")
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
let imageData = NSData(contentsOfURL: imageUrl!)
if(imageData != nil )
{
dispatch_async(dispatch_get_main_queue(),{
self.profilePictureImageVIew.image = UIImage(data: imageData!)
self.profilePictureImageVIew.backgroundColor = UIColor.clearColor()
})
}
}
// Send HTTP POST
let myUrl = NSURL(string: "http://www.mywebsite.co.za/php/scripts/getuserEmail.php");
let request = NSMutableURLRequest(URL:myUrl!);
request.HTTPMethod = "POST";
let postString = "user_id=\(userId)";
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding);
let task = NSURLSession.sharedSession().dataTaskWithRequest(request)
{
data, response, error in
// You can print out response object
println("******* response = \(response)")
// Print out reponse body
let responseString = NSString(data: data, encoding: NSUTF8StringEncoding)
println("****** response data = \(responseString)")
var err: NSError?
var json = NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers, error: &err) as? NSDictionary
if let parseJSON = json
{
var userEmail = parseJSON["userEmail"] as? String
self.usersEmail.text = userEmail
}
}
}
If I understand correctly, you're saying the page appears and your call to the web has not finished and assigned to the self.usersEmail.text yet. What I did in my project was dispatch the commands in a block like yours asynchronously on the main queue. Try doing this inside your if statement:
dispatch_async(dispatch_get_main_queue()) {
var userEmail = parseJSON["userEmail"] as? String
self.usersEmail.text = userEmail
}
Hopefully this helps. It may be improper, but hopefully somebody can let me know if it is. It works for me so far.
You also need to resume the task after its block:
let myUrl = NSURL(string: "http://www.mywebsite.co.za/php/scripts/getuserEmail.php");
let request = NSMutableURLRequest(URL:myUrl!);
request.HTTPMethod = "POST";
let postString = "user_id=\(userId)";
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding);
let task = NSURLSession.sharedSession().dataTaskWithRequest(request)
{
data, response, error in
// You can print out response object
println("******* response = \(response)")
// Print out reponse body
let responseString = NSString(data: data, encoding: NSUTF8StringEncoding)
println("****** response data = \(responseString)")
var err: NSError?
var json = NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers, error: &err) as? NSDictionary
if let parseJSON = json
{
var userEmail = parseJSON["userEmail"] as? String
self.usersEmail.text = userEmail
}
}
task.resume()
Thank you grdavis, marked as answer, I had however needed to change a few things to get the result I wanted and I just wanted to post the updated answer.
Moved to viewdidLoad and not viewWillAppear
When monitoring console the userID was not alone when being sent over the URL thus adding a '!' to the string fixed this too before sending the url
The resume worked
Below is my updated code, thank you again. Label updates like desired:
// Send HTTP POST
let userId:String? = NSUserDefaults.standardUserDefaults().stringForKey("userId")
let myUrl = NSURL(string: "http://www.mywebsite.co.za/php/scripts/getuserEmail.php");
let request = NSMutableURLRequest(URL:myUrl!);
request.HTTPMethod = "POST";
let postString = "user_id=\(userId!)";
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)){
let task = NSURLSession.sharedSession().dataTaskWithRequest(request)
{
data, response, error in
// You can print out response object
println("******* response = \(response)")
// Print out reponse body
let responseString = NSString(data: data, encoding: NSUTF8StringEncoding)
println("****** response data = \(responseString)")
var err: NSError?
var json = NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers, error: &err) as? NSDictionary
if let parseJSON = json
{
var userEmail = parseJSON["userEmail"] as? String
println("******* userEmail = \(userEmail)")
self.usersEmail.text = userEmail
}
}
task.resume()
}