AFNetworking: Send image from file - ios

I am trying to send a multi-part post request which includes an image. The following code works fine:
manager.POST( apiUrl + "/location/add",
parameters: parameters,
constructingBodyWithBlock: { (formData : AFMultipartFormData!) -> Void in
// formData.appendPartWithFileURL(NSURL(string: location.imagePath!), name: "image", error: nil)},
formData.appendPartWithFileData(img, name: imgParam, fileName: "randomimagename.jpg", mimeType: "image/jpeg")},
success: { (operation: AFHTTPRequestOperation!,responseObject: AnyObject!) in
println("JSON: " + responseObject.description)
var dict = responseObject as NSDictionary
let json = JSONValue(dict)
var message = ""
if let msg = json["message"].string {message = msg}
var success = false
if let s = json["success"].bool {
callback(success: success, msg: message)
}
},
failure: { (operation: AFHTTPRequestOperation!,error: NSError!) in
println("Error: " + error.localizedDescription)
var apiError = ApiError()
apiError.noConnection = true
errorCallback(apiError: apiError)
})
I want to use appendPartWithFileURL instead of appendPartWithFileData. If I replace the 5th line wiht the line which is commented out in the code above, I get the following compiler error:
Extra argument 'constructingBodyWithBlock' in call
Does anybody know how to resolve this?
edit: Found a (very, very, very strange) solution. Replace the line
formData.appendPartWithFileURL(NSURL(string: location.imagePath!), name: "image", error: nil)},
with
var temp = formData.appendPartWithFileURL(NSURL(string: location.imagePath!), name: "image", error: nil)},
I didn't change anything beside adding var temp =. I have no idea why this is working, but it does. Seems to be a strange bug.

If you haven't solved this problem yet, try casting location.imagePath to String.
I had the same problem till I've added as String in the following code:
func uploadFile(file: NSData, url: String, formData: NSDictionary, parameters: NSDictionary, completion: AnyObject -> Void, failure: NSError -> Void) {
operationManager.requestSerializer = AFJSONRequestSerializer() as AFHTTPRequestSerializer
if operationManager.reachabilityManager.networkReachabilityStatus != .NotReachable {
operationManager.POST(url, parameters: parameters, constructingBodyWithBlock: { (data) in
data.appendPartWithFileData(file, name: formData["fileName"] as String, fileName: formData["fileName"] as String, mimeType: formData["mimeType"] as String)
}, success: { (operation, responseObject) in
completion(responseObject)
}, failure: { (operation, error) in
failure(error)
})
} else {
showReachabilityAlert()
}
}
Hope it helps.

I kept receiving this error until I cast the URL as a string. Here's what I was doing (and receiving the error while doing so):
let manager = AFHTTPSessionManager()
let fullUrl = NSURL(string: name, relativeToURL: NSURL(string: apiBaseEndpoint))
manager.POST(fullUrl, parameters: nil, constructingBodyWithBlock: { (formData) in
formData.appendPartWithFormData(file, name: "image")
}, success: { (operation, responseObject) in
NSLog("hit success")
}, failure: { (operation, error) in
NSLog("hit the error")
})
To resolve the issue, I simply changed the assignment of fullUrl to take a string rather than NSURL by adding .absoluteString.
let fullUrl = NSURL(string: name, relativeToURL: NSURL(string: apiBaseEndpoint)).absoluteString

Related

How do I upload an image using AFNetworking class with Swift 3 in Xcode 8 and iOS 10

manager.post(urlString, parameters: perameter, progress: nil,
success:
{
requestOperation, response in
SVProgressHUD.dismiss()
let json = try! JSONSerialization.jsonObject(with: (response as! NSData) as Data, options:.allowFragments) as! NSDictionary
self.listData = json["Result"] as! [NSDictionary]
print(self.listData)
self.tableViewOutlet.reloadData()
},
failure:
{
requestOperation, error in
print(error.localizedDescription)
})
For image upload, you can use multipart.
manager.responseSerializer.acceptableContentTypes = ["text/html"]
manager.post(url as String, parameters: dic, constructingBodyWith: { (data) in
if imgData != nil
{
data.appendPart(withFileData: imgData!, name: "image", fileName: imgName as String , mimeType: "image/jpeg")
}
}, progress: nil, success: { (sessiondata, result) in
UIApplication.shared.isNetworkActivityIndicatorVisible = true
let dict = result as! Dictionary<String, AnyObject>
print(dict)
if (dict["status"]?.boolValue == true)
{
block(dict as NSDictionary?)
}
else{
block(dict as NSDictionary?)
}
}) { (data, result) -> Void in
let errorDict = ["error": data?.error?.localizedDescription as AnyObject]
UIApplication.shared.isNetworkActivityIndicatorVisible = false
block(errorDict as NSDictionary?)
}
Try this :
let manager = AFHTTPRequestOperationManager()
let url = "http://path/to/server"
let URL : NSURL = NSURL(string: url)!
let req : NSURLRequest = NSURLRequest(URL: URL)
let fileURL = NSURL(string: "file:///var/mobile/Media/DCIM/102APPLE/IMG_2623.PNG")// change your image url
manager.POST( url, parameters: nil,
constructingBodyWithBlock: { (data: AFMultipartFormData!) in
do{
_ = try data.appendPartWithFileURL(fileURL!, name: "uploaded_file", fileName: "image.png", mimeType: "image/png")
}catch{
}
},
success: { (operation: AFHTTPRequestOperation!, responseObject: AnyObject!) in
print("\(responseObject)")
},
failure: { (operation: AFHTTPRequestOperation!, error: NSError!) in
print("\(error.localizedDescription)")
})
Use below function to upload image with other parameter to server:
typealias MultipartFormDataBlock = (_ multipartFormData : MultipartFormData?) -> Void
typealias CompletionBlock = (_ response: Any?, _ error: Error?) -> Void
func multiPartRequestWithUrl(url: String, parameters: Dictionary<String,Any>?, multiPartFormDataBlock: MultipartFormDataBlock?, completion: CompletionBlock?) -> Void {
var param : Dictionary! = parameters ?? Dictionary()
print("Param = \(param)")
Alamofire.upload(multipartFormData: { (multiPartFormData) in
multiPartFormDataBlock?(multiPartFormData)
if param != nil {
for (key, value) in param! {
multiPartFormData.append("\(value)".data(using: .utf8)!, withName: key)
}
}
}, to: url, encodingCompletion:{encodingResult in
switch encodingResult {
case .success(let upload, _, _):
upload.responseJSON { response in
switch response.result {
case .success(let value):
handleResponse(response: value, completion: completion)
case .failure(let error):
print(error.localizedDescription)
completion?(nil, error)
}
}
case .failure(let encodingError):
completion?(nil, encodingError)
}
})
}
For New function with AFNetworking
+ (NSURLSessionDataTask *)multiPartRequestWithURL:(NSString *)strUrl parameters:(NSDictionary *)parameters constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))formData progress:(void (^)(NSProgress *progress))progressBlock requestCompletionBlock:(requestCompletionBlock)requestCompletionBlock {
NSMutableDictionary *dictParameters = [parameters mutableCopy];
if(!dictParameters) {
dictParameters = [NSMutableDictionary dictionary];
}
dictParameters[#"lang"] = [AppUtility CURRENT_SELECTED_LANGUAGE];
NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:#"POST" URLString:strUrl parameters:dictParameters constructingBodyWithBlock:formData error:nil];
[request setTimeoutInterval:3600];
NSURLSessionDataTask *dataTask = [self request:request uploadProgress:^(NSProgress *uploadProgress) {
dispatch_async(dispatch_get_main_queue(), ^{
if(progressBlock) {
progressBlock(uploadProgress);
}
});
} downloadProgress:nil requestCompletionBlock:^(id responseObject, NSError *error) {
if(requestCompletionBlock) {
requestCompletionBlock(responseObject, error);
}
}];
[dataTask resume];
return dataTask;
}
Upload image using AFNetworking in Swift 4
let urlPath1 = "Path url here"
let manager = AFHTTPRequestOperationManager()
var Timestamp: String {
return "\(NSDate().timeIntervalSince1970 * 1000)"
}
let operation = manager.post(urlPath1 as String, parameters: dictData, constructingBodyWith: { (data:AFMultipartFormData!) -> Void in
if image != nil {
data.appendPart(withFileData: UIImagePNGRepresentation(image!)!, name: imageName as String, fileName: "\(Timestamp).png", mimeType: "image/png")
}
}, success: { (operation, responseObject) -> Void in
success(responseObject as AnyObject)
}) { (operation, error) -> Void in
print(error, terminator: "")
}
operation?.start()
}
In Swift 5 use following:
image.pngData()
or
forImage.jpegData(compressionQuality: 0.5)
based on your requirement.

swift 3 A very weird issue with the function parameter when calling post() of AFNetworking

I'm newbie with ios programming. And now I can not explain for the weird situation as following:
let url: String = isFavorite ? "1.1/favorites/create.json" : "1.1/favorites/destroy.json"
post(url, parameters: params, progress: nil, success: { (task, response) -> Void in
let dictionary = response as! NSDictionary
let tweet = Tweet(dictionary: dictionary)
success(tweet)
}, failure: { (task, error) -> Void in
print("like tweet error: \(error.localizedDescription)")
failure?(error)
})
It always throws the exception:
like tweet error: Request failed: not found (404)
It's OK when I try as below (pass the url value directly instead of a variable):
post("1.1/favorites/create.json", parameters: params, ...
Because the error is 404, so maybe the url is not recognized by post() method, I really don't know what is the difference between them?
Try to convert the url string in allowed url
var url: String = isFavorite ? "1.1/favorites/create.json" : "1.1/favorites/destroy.json"
url = url.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)
post(url, parameters: params, progress: nil, success: { (task, response) -> Void in
let dictionary = response as! NSDictionary
let tweet = Tweet(dictionary: dictionary)
success(tweet)
}, failure: { (task, error) -> Void in
print("like tweet error: \(error.localizedDescription)")
failure?(error)
})
Try this.:)
let url: URLStringConvertible = isFavorite ? "1.1/favorites/create.json" : "1.1/favorites/destroy.json"
You can edit a breakpoint in the callback method, to see what happens

Code extraction on Afnetworking post request

I am trying to extract a post request so it can be re-used and keep my code as DRY as possible bu I'm struggling a little. I started off with:
func createAccount() {
let manager = AFHTTPSessionManager()
let dob = self.dobTextField.text!.components(separatedBy: "/")
let URL = "https://splitterstripeservertest.herokuapp.com/account"
let params = [
"first_name": firstNameTextField.text!.trim(),
"last_name": lastNameTextField.text!.trim(),
"line1": addressLine1TextField.text!.trim(),
"city": cityTextField.text!.trim(),
"postal_code": postCodeTextField.text!.trim(),
"email": emailTextField.text!.trim(),
"day": UInt(dob[0])! as UInt,
"month": UInt(dob[1])! as UInt,
"year": UInt(dob[2])! as UInt] as [String : Any]
manager.requestSerializer = AFHTTPRequestSerializer()
manager.responseSerializer = AFHTTPResponseSerializer()
manager.post(URL, parameters: params, progress: nil, success: {(_ task: URLSessionDataTask, _ responseObject: Any) -> Void in
do {
let response = try JSONSerialization.jsonObject(with: responseObject as! Data, options: .mutableContainers) as? [String: Any]
self.stripeAccountID = response?["id"] as! String
self.stopAnimating()
self.goToFinalStage()
} catch {
print("Serialising new account json object went wrong.")
self.stopAnimating()
}
}, failure: { (operation, error) -> Void in
self.handleError(error as NSError)
self.stopAnimating()
})
}
and have it down to:
func createAccount() {
let request = HttpRequest()
let response = request.post(params: setParams(), URLExtension: "account")
if (response != nil) {
successfulRequest(response: response!)
} else {
failedRequest(response: response!)
}
}
func successfulRequest(response: AnyObject) {
self.stripeAccountID = response["id"] as! String
createMainBillSplitter()
self.stopAnimating()
performSegue(withIdentifier: "segueToFinalRegistrationViewController", sender: self)
}
func failedRequest(response: AnyObject) {
self.stopAnimating()
self.handleError(response["failed"] as! NSError)
}
where HTTPRequest is:
class HttpRequest {
let manager = AFHTTPSessionManager()
let baseURL = "https://splitterstripeservertest.herokuapp.com/account"
func post(params: [String: Any], URLExtension: String) -> AnyObject? {
let URL = baseURL + URLExtension
var response = [String: Any]()
manager.requestSerializer = AFHTTPRequestSerializer()
manager.responseSerializer = AFHTTPResponseSerializer()
manager.post(URL, parameters: params, progress: nil, success: {(_ task: URLSessionDataTask, _ responseObject: Any) -> Void in
do {
response = try JSONSerialization.jsonObject(with: responseObject as! Data, options: .mutableContainers) as! [String: Any]
} catch {
print("Serialising new account json object went wrong.")
}
}, failure: { (operation, error) -> Void in
response = ["failed": error]
})
return response as AnyObject?
}
func handleError(_ error: NSError) -> UIAlertController {
let alert = UIAlertController(title: "Please Try Again", message: error.localizedDescription, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: nil))
return alert
}
}
But, I'm getting errors because the response is nil, which I'm sure is because there aren't completion handlers. I just don't understand enough how to implement them in this situation, so would really appreciate a push in the right direction. Thanks in advance!
You are getting confused with sync vs async operations.
The manager.post function will create your http request and will call the success closure when it is done. But since that function is implemented as an async operation, your code will not stop while that http request is being executed. So, your code will continue to be executed, and in your case, the very next line is you returning the response that is basically your empty array of Strings.
func post(params: [String: Any], URLExtension: String) -> AnyObject? {
let URL = baseURL + URLExtension
var response = [String: Any]()
manager.requestSerializer = AFHTTPRequestSerializer()
manager.responseSerializer = AFHTTPResponseSerializer()
manager.post(URL, parameters: params, progress: nil, success: {(_ task: URLSessionDataTask, _ responseObject: Any) -> Void in
// this closure is executed only when the request is completed
do {
response = try JSONSerialization.jsonObject(with: responseObject as! Data, options: .mutableContainers) as! [String: Any]
} catch {
print("Serialising new account json object went wrong.")
}
}, failure: { (operation, error) -> Void in
response = ["failed": error]
})
return response as AnyObject? // <<-- this line is executed right after the manager.post line above, but the success closure was not called yet because the request is still going on.
}
So, what you need to do is to not return the response right after the manager.post was called, but return it from inside the success closure. But you cannot simply use a return response statement. You need to pass the response as a parameter to a callback closure that you would pass to your request.post function.
Something like this:
func createAccount() {
let request = HttpRequest()
let response = request.post(params: setParams(),
URLExtension: "account",
success: {response in
// enter here the code to be executed when request is completed.
successfulRequest(response: response)
},
fail: {response in
failedRequest(response: response)
},)
}
and your class HttpRequest post function would be:
func post(params: [String: Any], URLExtension: String, success:([String: Any] -> Void), fail:([String: Any] -> Void)) -> AnyObject? {
let URL = baseURL + URLExtension
manager.requestSerializer = AFHTTPRequestSerializer()
manager.responseSerializer = AFHTTPResponseSerializer()
manager.post(URL, parameters: params, progress: nil, success: {(_ task: URLSessionDataTask, _ responseObject: Any) -> Void in
do {
response = try JSONSerialization.jsonObject(with: responseObject as! Data, options: .mutableContainers) as! [String: Any]
success(response)
} catch {
print("Serialising new account json object went wrong.")
}
}, failure: { (operation, error) -> Void in
response = ["failed": error]
fail(response)
})
}
PS: your code is assuming that it will always be able to decode the JSON response. Although you are using do / catch, if for some reason the JSON decoding fails, no response is being send back to your calling function. So, the app will just be stuck. I suggest you calling the fail() callback inside your catch block.

AFNetwoking with swift 3.0 & xcode 8 : POST Request & GET Request

I am trying to parse data using AFNetworking & swift 3.0 and xcode 8.0 but i am getting error like below.below code works fine for swift 2.3 but not working in 3.0
Or if is there anyone know about AFNetworking & swift 3.0 using xcode 8.0 for POST & GET request please tell me. with simple example.
Thanks in Advance
You can see below error.
func callApi(apiName: String, param: [String : AnyObject]?, data: NSDictionary?, withMethod type: String, CompletionHandler:#escaping (_ code: Int, _ error:NSError?, _ response:AnyObject?) -> Void)
{
MBProgressHUD.showAdded(to: AppDelegateObj.window, animated: true)
let str_URL : String = kHOSTPATH+apiName
let manager: AFHTTPSessionManager = AFHTTPSessionManager()
if (type == kREQ_POST) {
manager.POST(str_URL, parameters: param, constructingBodyWithBlock: { (formData: AFMultipartFormData!) in
if data?.allValues.count != 0 && data != nil
{
let fileUrl = NSURL(fileURLWithPath: (data?.valueForKey("filePath"))! as! String)
try! formData.appendPartWithFileURL(fileUrl, name: (data?.valueForKey("key"))! as! String)
}
}, progress: { (NSProgress) in
}, success: { (task:URLSessionDataTask, responseObject) -> Void in
CompletionHandler(code: 1, error: nil, response:responseObject)
MBProgressHUD.hideHUDForView(AppDelegateObj.window, animated: true)
}, failure: { (task:URLSessionDataTask?, error:NSError) -> Void in
CompletionHandler(code: 0, error: error, response:nil)
MBProgressHUD.hideHUDForView(AppDelegateObj.window, animated: true)
})
}
else {
manager.GET(str_URL, parameters: param, progress: { (NSProgress) in
}, success: { (task:URLSessionDataTask, responseObject) -> Void in
CompletionHandler(code: 1, error: nil, response:responseObject)
MBProgressHUD.hideHUDForView(AppDelegateObj.window, animated: true)
}, failure: { (task:URLSessionDataTask?, error:NSError) -> Void in
CompletionHandler(code: 0, error: error, response:nil)
MBProgressHUD.hideHUDForView(AppDelegateObj.window, animated: true)
})
}
}
but i am getting error like this
cannot convert the value of type (URLSessionDataTask?,NSError)->Void to expected argument type '((URLSessionDataTask?,NSError)->Void)?'
Change your error type from NSERROR to ERROR. Type compatibility changed from Swift2.3 to Swift 3.0
Try this:
func Get()
{
let url = NSURL(string: Webserive.UserProfileInfoList + "/" + String(300))
let request = NSURLRequest(url: url! as URL)
NSURLConnection.sendAsynchronousRequest(request as URLRequest, queue: OperationQueue.main) {(response, data, error) in
self.GetData(data: data! as NSData)
}
}
func GetData(data:NSData){
let dict: NSDictionary!=(try! JSONSerialization.jsonObject(with: data as Data, options: JSONSerialization.ReadingOptions.mutableContainers)) as! NSDictionary
print(dict)
self.PostData()
}
func PostData(){
let parameters : NSMutableDictionary? = [
"UserID": String(300),
"UserProfileID": String(356)]
let manager = AFHTTPSessionManager()
let serializerRequest = AFJSONRequestSerializer()
serializerRequest.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
manager.requestSerializer = serializerRequest
let serializerResponse = AFJSONResponseSerializer()
serializerResponse.readingOptions = JSONSerialization.ReadingOptions.allowFragments
serializerResponse.acceptableContentTypes = ((((NSSet(object: "application/json") as! Set<String>) as Set<String>) as Set<String>) as Set<String>) as Set<String>;
manager.responseSerializer = serializerResponse
manager.post(Webserive.DefaultProfile, parameters: parameters, progress: nil, success: { (task: URLSessionDataTask, responseObject: Any?) in
if (responseObject as? [String: AnyObject]) != nil {
print("responseObject \(responseObject)")
}
}) { (task: URLSessionDataTask?, error: Error) in
print("POST fails with error \(error)")
}
}
I had similar issue. Do not know what is the root of the problem but you can help yourself by not specifying some parameter's type, in this case 'error' in POST method call:
manager.post("http://...", parameters: [:], progress: nil, success: { (operation: URLSessionDataTask, responseObject: Any?) in
print("Success")
}, failure: { (operation: URLSessionDataTask?, error) in
let error = error as? Error
print("Failure, error is \(error?.description)")
})
With this code pattern I can build the code with Xcode 8.3 and Swift 3.
EDIT:
If you have some problem with "Cannot convert value of type...", please check type that function expects.
My problem was that I had declaration of Error as CoreData one and AFNetworking certainly does not return that Error type.
class Error: NSManagedObject {
// Insert code here to add functionality to your managed object subclass
}
What I needed to change was:
(operation: URLSessionDataTask?, error: Error?)
to:
(operation: URLSessionDataTask?, error: Swift.Error?)

Swift: multipart formdata with image got issues

let manager = AFHTTPRequestOperationManager(baseURL: NSURL(string: strURL))
print("pram is \(manager)")
manager.requestSerializer = AFJSONRequestSerializer()
manager.responseSerializer = AFJSONResponseSerializer()
// let jsonData = try! NSJSONSerialization.dataWithJSONObject(param!, options: .PrettyPrinted)
let imageData = UIImageJPEGRepresentation(UIImage(named: "placeholder.png")!, 0.5)
let op : AFHTTPRequestOperation = manager.POST("data", parameters: param, constructingBodyWithBlock: { (formData: AFMultipartFormData!) -> Void in
formData.appendPartWithFileData(imageData!, name: "photo", fileName: "photo.png", mimeType: "image/jpeg")
// print(op)
},
success:
{
(operation: AFHTTPRequestOperation!, responseObject: AnyObject!) in
print(responseObject)
},
failure: { (operation, error) in
print("Error: " + error.localizedDescription)
})!
op.start()
}
I try this code to multiform but return 400
I want to send like
{"first_name":"John","last_name":"Doe","dob":"1990-01-02","gender":"M","ph
one_no":"9988475","email":"j.d#gmail.com","password":"john.doe","user_type":"U","facebook_id":""}
----WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="photo"; filename=""
Content-Type:
----WebKitFormBoundary7MA4YWxkTrZu0gW
I want to send my data to server something like what I posted.I tried this code to acheive but I can't.Please help me solve this issues

Resources