So here is my problem, I must upload an image in my swift application to a server that use rails. I got almost everything set up, except that the rails api must receive the file data in a variable document[:file]
here is the function I use to encode my image with alamofire:
static func getDocumentCreateUploadData(parameters: [String : AnyObject], imageData: NSData) -> (uploadData:NSMutableData, contentType:String) {
let boundaryConstant = "NET-POST-boundary-\(arc4random())-\(arc4random())";
let contentType = "multipart/form-data;boundary="+boundaryConstant
let uploadData = NSMutableData()
// add image
uploadData.appendData("\r\n--\(boundaryConstant)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
uploadData.appendData("Content-Disposition: form-data; name=\"file\"; filename=\"file.png\"\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
uploadData.appendData("Content-Type: image/png\r\n\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
uploadData.appendData(imageData)
// add parameters
for (key, value) in parameters {
uploadData.appendData("\r\n--\(boundaryConstant)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
uploadData.appendData("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n\(value)".dataUsingEncoding(NSUTF8StringEncoding)!)
}
uploadData.appendData("\r\n--\(boundaryConstant)--\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
return (uploadData, contentType)
}
And then I call my api with
var imageParameters = [
"documents": [
"documentable_type": "Profile",
"documentable_id": user.profile.id!
]
]
let data = Router.getDocumentCreateUploadData(imageParameters, imageData: imageData)
let urlRequest = Router.CreateDocument(contentType: data.contentType)
Alamofire.upload(urlRequest, data.uploadData).validate().responseSwiftyJSON({ (request, response, json, error) in
if error != nil {
...
What I would like to know is how can I encode the image so that it respect the api specification.
{
"documents" : {
"file": the_file,
"documentable_type": the_documentable_type,
"documentable_id": the_documentable_id
}
}
Is there a way to achieve this?
Thank in advance.
I simply misundertood the api, I didn't needed the root document in my upload call
All I needed to do was remove the document root but here is the way I did it :
let imageParameters = [
"documentable_type": "Comment",
"organization_id": organizationId,
"member_id": memberId,
"user_id": user.id
]
let uuid = NSUUID().UUIDString
composeBarView.enabled = false
ProgressHUDManager.uploadingStatus()
Alamofire.upload(
Router.CreateDocument,
multipartFormData: { multipartFormData in
multipartFormData.appendBodyPart(data: imageData!, name: "file", fileName: "\(uuid).jpg", mimeType: "image/jpeg")
for (key, value) in imageParameters {
multipartFormData.appendBodyPart(data: value.dataUsingEncoding(NSUTF8StringEncoding)!, name: key)
}
},
encodingCompletion: { encodingResult in
// Handle result
}
)
The API Evolved so the code is new but I hope it can help
Related
I'm using Alamofire 5, and I'm trying to upload an image to a Rocket Chat server. The corresponding curl statement that I need to duplicate using AF is at the following link:
(link to documentation: https://docs.rocket.chat/api/rest-api/methods/rooms/upload)
I've been trying to upload using multipartFormData with no success. I've also attempted to bypass Alamofire altogether and use Swift URLSession. The best I can do is get the same error message from the server, which is "errorType": invalid-field."
My code as it stands right now:
let url = URL_MESSAGES + "rooms.upload/\(_group._id ?? "")"
let boundary = UUID().uuidString
let headers: HTTPHeaders = [
"X-Auth-Token": authToken,
"X-User-Id": me._id ?? "",
"Content-type":"multipart/form-data; boundary=\(boundary)"
]
if let data = image.jpeg(.medium) {
print(data.count)
AF.upload(
multipartFormData: { multipartFormData in
multipartFormData.append(data, withName: "image", fileName: "image.jpeg", mimeType: "image/jpeg")
},
to: url, method: .post , headers: header)
.response { resp in
print(resp)
}
.cURLDescription { description in
print(description)
}
.responseString { [weak self] (response) in
DispatchQueue.main.async {
if response.error == nil {
guard let data = response.data else {
return completion(true,[:])
}
if let json = try? JSON(data: data) {
let dictionaryIn = json.rawValue as! [String : Any]
if (self?.isSuccess(data: dictionaryIn))! {
completion(true,json.rawValue as! [String : Any])
}else{
completion(false,[:])
self?.handleError(data: dictionaryIn)
}
}
}else{
completion(false,[:])
self?.handleError(data: [:])
}
}
}
}
}
I think you're breaking the upload by trying to set your own boundary. Alamofire will that for you automatically. Try removing the header.
The above code works perfectly, when I changed this:
AF.upload(
multipartFormData: { multipartFormData in
multipartFormData.append(data, withName: "image", fileName: "image.jpeg", mimeType: "image/jpeg")
to:
AF.upload(
multipartFormData: { multipartFormData in
multipartFormData.append(data, withName: "file", fileName: "image.jpeg", mimeType: "image/jpeg")
Swapping out "image" for "file" fixed everything. I went with "image" because this is what every Alamofire tutorial I could find said to do. However, RocketChat requires it to be "file". It is in the documentation, but I didn't realize that's what it was telling me to do. Specifically, in the documentation, it says:
"Note: For some file types if uploading via curl you may need to set the mime type.
With some file types, curl will upload the file as application/octet-stream. You can pass a custom mime type like this: -F "file=#file.wav;type=audio/wav" "
I was trying -F "image=#file.wav;type=audio/wav" when translated from Alamofire. It needs to be: -F "file=#file.wav;type=audio/wav"
I am trying to make a request to Google Cloud NLP API to obtain sentiment analysis for a piece of text. I used Postman to design the correct request, and I was able to get a valid response using Postman. However, when I try to make the same request from Swift, it gives me an error. The error and code snippet used to make the request is shown below.
func sendAPIRequest(with text: String){
print("Text: ", text)
let jsonRequest = [
[
"document":[
"type":"PLAIN_TEXT",
"language": "EN",
"content":"'Lawrence of Arabia' is a highly rated film biography about British Lieutenant T. E. Lawrence. Peter O'Toole plays Lawrence in the film."
],
"encodingType":"UTF8"
]
]
let jsonObject = JSON(jsonRequest)
let headers: HTTPHeaders = [
"X-Ios-Bundle-Identifier": "\(Bundle.main.bundleIdentifier ?? "") ",
"Content-Type": "application/json"
]
let APIRequest = Alamofire.request("https://language.googleapis.com/v1/documents:analyzeSentiment?key=\(gCloudAPIKey)", method: .post , parameters: jsonRequest as? [String: Any], encoding: JSONEncoding.default , headers: headers).responseJSON { (response) in
print(response)
if let json = response.result.value {
print("JSON: \(json)")
}
}
Error:
JSON: {
error = {
code = 400;
details = (
{
"#type" = "type.googleapis.com/google.rpc.BadRequest";
fieldViolations = (
{
description = "Must have some text content to annotate.";
field = "document.content";
}
);
}
);
message = "One of content, or gcs_content_uri must be set.";
status = "INVALID_ARGUMENT";
};
}
Sorry. Solved it. My jsonRequest should be of type Parameters according to Alamofire.
let jsonRequest: Parameters =
[
"document":[
"type":"PLAIN_TEXT",
"language": "EN",
"content":"\(text)"
],
"encodingType":"UTF8"
]
I need to send JSON format in post api using alamofire in swift. Way data need to send is
{
"data": [{
"id": "1015683997",
"name": "Pawel"
}, {
"id": "108350039247",
"name": "Randeep"
}, {
"id": "115607797616",
"name": "Mohit"
}]
}
And way i am able to generate as of now is:
["data": {
data = (
{
id = 101583997;
name = "Pawel";
},
{
id = 108359247;
name = "Randeep";
},
{
id = 11567616;
name = "Mohit ";
}
);
}
]
Using the below mentioned way to generate json format.
for i in 0..<self.arrFriendsList.count
{
let dictFrndList:[String: AnyObject] = [
"id" : arrFriendsList[i].valueForKey("id")!,"name" : arrFriendsList[i].valueForKey("name")!
]
arrFrndList.addObject(dictFrndList)
}
Then,
let dictFBFrndList: [String: AnyObject] = [
"data":arrFrndList
]
Then,
let params: [String: AnyObject] = [
"access_token" : accessToken,
"data":dictFBFriends
]
So, please guide me. i have already spend more than a day on it. thanks in advance.
I second what Larme said, you should post "arrFrndList" at the params with key "data" but not dictFBFrndList.
Managed this way
let jsonData = try! NSJSONSerialization.dataWithJSONObject(arrFriendsList, options: NSJSONWritingOptions.PrettyPrinted)
let jsonString = NSString(data: jsonData, encoding: NSUTF8StringEncoding)! as String
print(jsonString)
let params: [String: AnyObject] = [
"access_token" : accessToken,
"data":jsonString
]
let options = NSJSONWritingOptions()
let data = try? NSJSONSerialization.dataWithJSONObject(parameters, options: options)
let mutableURLRequest = NSMutableURLRequest(URL: NSURL(string:"http:/myurl.com")
if let str = NSString(data: data!, encoding: NSUTF8StringEncoding) as? String {
print(str)
}
mutableURLRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
mutableURLRequest.setValue("your_access_token", forHTTPHeaderField: "Authorization")
mutableURLRequest.HTTPBody = data
mutableURLRequest.HTTPMethod = "POST"
Alamofire.request(mutableURLRequest).responseJSON(){
json in
if let value = json.result.value {
let _json = JSON(value)
if (!_json.isEmpty){
print(_json)
}
}
}
you can check how to seen your JSON array this code. Look at 5th cell code. This code will write your json array for you in output screen and you can coppy from there and paste in here then you will see how to send data.
Maybe this way can help you .
I would like to pass a nil value i.e., optional to one of the parameter value. And it must proceed with the nil value in the Alamofire Post request .It would be helpful if you tell me how to proceed next?
let image: UIImage = UIImage()
let imageData = UIImagePNGRepresentation(image)
let base64String = imageData?.base64EncodedStringWithOptions(.Encoding64CharacterLineLength)
let parameters = [
"first_name": "XXXXX",
"email" : "1234#gmail.com",
"password" : "password",
"profile_picture" : base64String]
Alamofire.request(.POST, "http://abc/public/user/register", parameters: parameters, encoding: .JSON, headers: nil)
.progress { bytesWritten, totalBytesWritten, totalBytesExpectedToWrite in
print(totalBytesWritten)
// This closure is NOT called on the main queue for performance
// reasons. To update your ui, dispatch to the main queue.
dispatch_async(dispatch_get_main_queue()) {
print("Total bytes written on main queue: \(totalBytesWritten)")
}
}
.responseJSON { response in
debugPrint(response)
}
The response should gets succeeded even if the profile_pictures is empty. I know it can be done with optional chaining but don't know how to proceed!!
By passing nil or uninitialized optional parameter Server will get Optional
You can pass NSNull() to dictionary
try this, like
var params = ["paramA","valueA"] if imageBase64 == nil { parms["image"] = NSNull()} else { params["image"] = imageBase64 }
swiftyjson also handle null as NSNull
also there is good reference here
null / nil in swift language
I think your simplest answer would be to add "profile_picture" as a second step.
var parameters = [
"first_name": "XXXXX",
"email" : "1234#gmail.com",
"password" : "password"]
if let base64String = base64String where !base64String.isEmpty {
parameters["profile_picture"] = base64String
}
hope this will help you :)
var parameters :[String : AnyObject?] = [
"first_name": "XXXXX",
"email" : "1234#gmail.com",
"password" : "password"]
if let string = base64String where base64String.isEmpty != false {
parameters["profile_picture"] = string
}
After a very thorough research, I found out it can be done easily through optional chaining and type casting.
The first step it to divide the parameters by type casting it to string and Image and check for the availability of String value and Image value.
let parameters: [String : AnyObject? ] = [
"first_name": "XXXXX",
"email" : "1234#gmail.com",
"password" : "password",
"profile_pic" : UIImage(named: "logo")]
Do it like this in the request method
for (key, value) in parameters {
if let value1 = value as? String {
multipartFormData.appendBodyPart(data: value1.dataUsingEncoding(NSUTF8StringEncoding)!, name: key)
}
if let value1 = value as? UIImage {
let imageData = UIImageJPEGRepresentation(value1, 1)
multipartFormData.appendBodyPart(data: imageData!, name: key, fileName: "logo.png" , mimeType: "image/png")
}
You don't have to split the parameters into two, one for the string values and other for the image and also it is unnecessary to convert the image to string. Hope this solution helps!!
i am quite new to iOS and Swift and now i want to use Neo4j, a Graph Database for persistent saving of my data.
I know there's a Lib named Theo for connecting to the Graph Database, but i want to use the REST API. Now i am not sure how to handle the HTTP-Request in order to get data from Database to my iOS-App written in Swift?
Anyone has some helpful clues for my?
Thanks hannes
The Theo library that you mention is a wrapper around the Neo4j REST API. The benefit of using such a library is that it takes care of things like JSON serialization, error handling, adding the correct headers to HTTP requests, etc.
You can use Cypher with the transactional Cypher REST API endpoint using Swift like this (Note: this uses the Alamofire library for handling the HTTP request.):
import Alamofire
let cypherStatement = "CREATE (:Person {name: 'Bob'})-[:LIKES]->(pizza:Food {type: 'Pizza'})<-[:LIKES]-(:Person {name: 'William'}) WITH pizza MATCH (p:Person)-[:LIKES]->(pizz) RETURN p.name AS name"
let endpoint = "http://localhost:7474/db/data/transaction/commit"
let requestBody = [
"statements": [
[
"statement": cypherStatement
]
]
]
Alamofire.request(.POST, endpoint, parameters: requestBody, encoding: .JSON)
.responseJSON { response in
debugPrint(response)
}
Which logs:
[Request]: <NSMutableURLRequest: 0x7fafcb637320> { URL: http://localhost:7474/db/data/transaction/commit }
[Response]: <NSHTTPURLResponse: 0x7fafcb81d480> { URL: http://localhost:7474/db/data/transaction/commit } { status code: 200, headers {
"Access-Control-Allow-Origin" = "*";
"Content-Length" = 91;
"Content-Type" = "application/json";
Date = "Fri, 18 Dec 2015 19:29:41 GMT";
Server = "Jetty(9.2.z-SNAPSHOT)";
} }
[Data]: 91 bytes
[Result]: SUCCESS: {
errors = (
);
results = (
{
columns = (
name
);
data = (
{
row = (
Bob
);
},
{
row = (
William
);
}
);
}
);
}
Edit
Making the same request without using Alamofire would look like this:
let cypherStatement = "CREATE (:Person {name: 'Bob'})-[:LIKES]->(pizza:Food {type: 'Pizza'})<-[:LIKES]-(:Person {name: 'William'}) WITH pizza MATCH (p:Person)-[:LIKES]->(pizz) RETURN p.name AS name"
let endpoint = "http://localhost:7474/db/data/transaction/commit"
let requestBody = [
"statements": [
[
"statement": cypherStatement
]
]
]
guard let url = NSURL(string: endpoint) else {
print("Error")
return true
}
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "POST"
request.addValue("application/json",forHTTPHeaderField: "Content-Type")
request.addValue("application/json",forHTTPHeaderField: "Accept")
do{
let jsonData = try NSJSONSerialization.dataWithJSONObject(requestBody, options: .PrettyPrinted)
request.HTTPBody = jsonData
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config)
let task = session.dataTaskWithRequest(request, completionHandler: { (data, response, error) in
let result: NSDictionary
do {
result = try NSJSONSerialization.JSONObjectWithData(data!,
options: []) as! NSDictionary
} catch {
print("error trying to convert data to JSON")
return
}
print("\(result)")
})
task.resume()
} catch let error as NSError {
print("JSON serialization error")
return true
}