Swift - "Alamofire.upload" -> "AF.upload" issue with the Upload function - swift

I have upgraded the pod Alamofire 5.0 from an older version,
From the Migrating Guide
https://github.com/Alamofire/Alamofire/blob/master/Documentation/Alamofire%205.0%20Migration%20Guide.md
"MultipartFormData’s API has changed and the top level upload methods to create and upload MultipartFormData have been updated to match other request APIs, so it’s not longer necessary to deal with the Result of the multipart encoding."
I have the below function written, i am unable to re-write this in the new version, Can anyone help me on this regard?
note:
I think i need to change the "Alamofire.upload" -> "AF.upload", however its not along is enough to fix the problem,
func uploadPhoto(token:String, userID: String, data: Data, dataUrl: URL) -> Observable<AnyObject?> {
return Observable<AnyObject?>.create({ (observer) -> Disposable in
print(userID);
print(data);
do {
let urlRequest = try URLRequest(url: ResourcePath.Upload.path + "/\(userID)" , method: .post)
Alamofire.upload(multipartFormData: { (multipartData) in
multipartData.append(data, withName: "Filedata", fileName: dataUrl.absoluteURL.lastPathComponent, mimeType: "image/jpeg")
//append(dataUrl, withName: "Filedata", fileName: dataUrl.absoluteString, mimeType: "image/png")
}, with: urlRequest, encodingCompletion: { (result) in
switch result {
case .success(let value, let t1, let t2) :
print(value)
value.responseData(completionHandler: { (dataResponse) in
switch (dataResponse.result) {
case .success(let value) :
print(value)
do {
let jsonData = JSON(dictData)
print("Upload photo response: \(jsonData)")
observer.onNext(jsonData as AnyObject?)
observer.onCompleted()
}
catch {
print(error)
observer.onError(error)
return
}
break
case .failure(let error) :
print(error)
observer.onError(error)
break
}
})
break
case .failure(let error) :
print(error)
observer.onError(error)
break
default:
break
}
})
} catch {
print(error)
observer.onError(error)
}
return Disposables.create {
print("disposed")
}
})
}

Alamofire 5 has removed the need for the encodingCompletion closure when using multipart form encoding. Instead, you can use the normal response handling as seen in other use cases. For example:
AF.upload(multipartFormData: { data in
// Build your multipart form.
}).responseDecodable(of: SomeType.self) { response in
// Handle response.
}

Related

Module 'Alamofire' has no member named 'upload' error after migrating

After migrating to 5.0 receive this error - > Module 'Alamofire' has no member named 'upload'
Changing Alamofire to AF doesn't help.
Can't run build in xcode due to this error.
Alamofire.upload(
multipartFormData: { (multipartFormData) in
formData.forEach({ (key, value) in
guard let valueData = "\(value)".data(using: .utf8) else {
return
}
multipartFormData.append(valueData, withName: key)
})
switch uploadOption {
case .data(let data, let meta):
multipartFormData.append(
data,
withName: "file",
fileName: meta.fileName,
mimeType: meta.mimeType
)
case .stream(let stream, let length, let meta):
multipartFormData.append(
stream,
withLength: length,
name: "file",
fileName: meta.fileName,
mimeType: meta.mimeType
)
}
},
to: url,
encodingCompletion: { (encodingResult) in
switch encodingResult {
case .failure(let error):
completion(.failure(error: error))
case .success(let request, _, _):
cancelable.request = request
request.responseData(completionHandler: { (response) in
if let error = response.error {
completion(.failure(error: error))
return
}
completion(.success)
})
}
})
I have Alamofire version 5.2, it seems ok,
some syntax changed after you migrate to 5.x,
plz check it or update it to 5.2

'Invalid type in JSON write (UIImage)' Alamofire API request

i am trying to send an image to an api using alamofire heres what i got:
var uploadedProfileImage: UIImage = UIImage()
let body: Parameters = [
"profilePic": uploadedProfileImage,
"name": "John Doe"
]
Alamofire.request(BASE_URL",method: .post,parameters: body,encoding: JSONEncoding.default).responseData { response in
debugPrint("All Response Info: \(response)")
if let data = response.result.value, let utf8Text = String(data: data, encoding: .utf8) {
print("Data: \(utf8Text)")
}
}
so this is the code i am using uploadProfileImage has an image that the user picked from the library and my api receives a json body parameter in which it has profilePic which is off type file when i run this its giving me an error saying 'Invalid type in JSON write (UIImage)'.theres is also a terminating with uncaught exception of type NSException error. what am i doing wrong and how to fix it?
You can't upload image with
"profilePic": uploadedProfileImage
as it's used for types that are encode-able such as strings , you need
upload image to server using Alamofire
func uploadImageAndData(_ url:String,parameters1:Parameters,headers1:HTTPHeaders,images:[UIImage]){
Alamofire.upload(multipartFormData: { multipartFormData in
// import image to request
var i=0
for imageData in images {
// multipartFormData.append(self.resizeImage(image: imageData, targetSize: CGSize(width: 400, height:
multipartFormData.append(imageData.pngData()!, withName: "image", fileName: "image"+".jpeg", mimeType: "image/jpeg")
i += 1
}
for (key, value) in parameters1 {
multipartFormData.append((value as AnyObject).data(using: String.Encoding.utf8.rawValue)!, withName: key)
}
} ,to: url,method:.post,
headers:[:],
encodingCompletion: { encodingResult in
switch encodingResult {
case .success(let upload, _, _):
upload.uploadProgress(closure: { (Progress) in
print("Upload Progress: \(Progress.fractionCompleted)")
//SwiftSpinner.show(progress: Progress.fractionCompleted*100, title: "تحميل")
})
upload.responseJSON { response in
switch response.response?.statusCode {
case 200 :
break
case 400 :
break
case 401 :
break
case 302 :
break
case 500 :
break
default: break
}
}
return
case .failure(let encodingError):
}
})
}
var head = [String:Any]()
head["Accept"]="application/json"
head["Authorization"] = "Bearer \(tokenIfExists)"

getting error message from server during API call

I have an app where I used RxSwift for my networking by extending ObservableType this works well but the issue I am having now is when I make an API request and there is an error, I am unable to show the particular error message sent from the server. Now how can I get the particular error response sent from the server
extension ObservableType {
func convert<T: EVObject>(to observableType: T.Type) -> Observable<T> where E: DataRequest {
return self.flatMap({(request) -> Observable<T> in
let disposable = Disposables.create {
request.cancel()
}
return Observable<T>.create({observer -> Disposable in
request.validate().responseObject { (response: DataResponse<T>) in
switch response.result {
case .success(let value):
if !disposable.isDisposed {
observer.onNext(value)
observer.onCompleted()
}
case .failure(let error):
if !disposable.isDisposed {
observer.onError(NetworkingError(httpResponse: response.response,
networkData: response.data, baseError: error))
observer.onCompleted()
}
}
}
return disposable
})
})
}
}
let networkRetryPredicate: RetryPredicate = { error in
if let err = error as? NetworkingError, let response = err.httpResponse {
let code = response.statusCode
if code >= 400 && code < 600 {
return false
}
}
return true
}
// Use this struct to pass the response and data along with
// the error as alamofire does not do this automatically
public struct NetworkingError: Error {
let httpResponse: HTTPURLResponse?
let networkData: Data?
let baseError: Error
}
response from the server could be
{
"status" : "error",
"message" : " INSUFFICIENT_FUNDS"
}
or
{
"status" : "success",
"data" : " gghfgdgchf"
}
my response is handled like this
class MaxResponse<T: NSObject>: MaxResponseBase, EVGenericsKVC {
var data: T?
public func setGenericValue(_ value: AnyObject!, forUndefinedKey key: String) {
switch key {
case "data":
data = value as? T
default:
print("---> setGenericValue '\(value)' forUndefinedKey '\(key)' should be handled.")
}
}
public func getGenericType() -> NSObject {
return T()
}
}
the error is
return ApiClient.session.rx.request(urlRequest: MaxApiRouter.topupWall(userId: getUser()!.id!, data: body))
.convert(to: MaxResponse<Wall>.self)
In the official Alamofire docs it is mentioned that validate(), without any parameters:
Automatically validates status code within 200..<300 range, and that
the Content-Type header of the response matches the Accept header of
the request, if one is provided.
So if you do not include Alamofire's validate() you are saying that no matter the status code, if the request did get through, you will consider it successful, so that's why it shows nothing in the failure block.
However if you prefer to use it, yes, it will give you an ResponseValidationFailureReason error, but you still have access to the response.data. Try printing it, you should see the expected error response from the server:
if let responseData = response.data {
print(String(data: responseData, encoding: .utf8))
}

Alamofire request body nil when uploading image using multipartFormData

I seem to have a problem regarding image upload and passing parameters using Alamofire. I have a quite simple function for multipart data which looks like:
sessionManager.upload(multipartFormData: { (multipartFormData) in
for (key, value) in parameters {
print("\(value)")
multipartFormData.append("\(value)".data(using: String.Encoding.utf8)!, withName: key as String)
}
multipartFormData.append(UIImagePNGRepresentation(image)!, withName: "document", mimeType: "image/png")
}, to: baseURL + "/documents", encodingCompletion: { (result) in
switch result{
case .success(let upload, _, _):
upload.validate().responseJSON { response in
print("Succesfully uploaded")
}
case .failure(let error):
print("Error in upload: \(error.localizedDescription)")
}
})
I have OAuth2Handler which i've implemented for headers and authorisation and it works for all other requests. I have also tried to implement this without wrapper using Alamofire object directly but still no luck. When I inspect the request i notice that httpBody is always nil, which corresponds to error I get from the server and the message says that I do not pass required parameters.
This is Working for me Swift 4
func callPostApiImage(api:String, parameters:[String:AnyObject]?,image:UIImage,Name:String, mime_type:String = "image/jpg", complition:#escaping (AnyObject)->Void){
// Encode Data
let base64EncodedString = toBase64EncodedString(toJsonString(parameters: parameters!))
let File_name = "image_" + String(arc4random()) + ".jpg"
Alamofire.upload(multipartFormData: { (multipartFormData) in
multipartFormData.append(UIImageJPEGRepresentation(image, 0.5)!, withName: Name, fileName: File_name, mimeType: mime_type)
multipartFormData.append(base64EncodedString.data(using: String.Encoding.utf8)!, withName: "jsondata")
}, to:api){ (result) in
switch result {
case .success(let upload, _, _):
upload.uploadProgress(closure: { (progress) in
print(progress)
})
upload.responseJSON { response in
print(response.result)
}
case .failure(let encodingError):
print("",encodingError.localizedDescription)
break
}
}
}
// Base64EncodedString
func toBase64EncodedString(_ jsonString : String) -> String
{
let utf8str = jsonString.data(using: .utf8)
let base64Encoded = utf8str?.base64EncodedString(options: [])
return base64Encoded!
}

While Loop increments and re-runs before Promisekit can finish running and return the proper value in Swift

I'm trying to use a while loop with Promisekit with Alamofire to chain four GET requests, return a value, and rerun the four requests with a new parameter. This is the current code that I'm using:
var index = 0
var count = classDictionary["class"]!.count-1
while index <= count {
firstly {
parseBooksXML(index)
}.then { abrevCode in
self.parseBooksXML2(index, key: abrevCode)
}.then { courseNumber in
self.parseBooksXML3(index, key: courseNumber)
}.then { instructorCode in
self.parseBooksXML4(instructorCode)
}
index += 1
}
Each of the first three functions returns a promised string value which is then passed onto the next function until the fourth and final function calls another function to parse the downloaded HTML using Kanna.
Ideally I would like all four functions to be called and completed after which the index will increment and run the loop again using the new index number. As a note, the index in passed onto the functions as a way to identify which index in an array a value should be compared to.
For clarity, I have included the code for the parseBooksXML functions below:
func parseBooksXML(index: Int) -> Promise<String> {
return Promise {fulfill, reject in
let headers = [
"Referer": "URL"
]
Alamofire.request(.GET, "URL", headers: headers)
.responseData { response in
switch response.result {
case .Success:
let xml = SWXMLHash.parse(response.data!)
do {
let range = self.classDictionary["class"]![index].rangeOfString("[a-zA-Z]{2,4}", options: .RegularExpressionSearch)
let result = self.classDictionary["class"]![index].substringWithRange(range!)
try self.abrevCode = (xml["departments"]["department"].withAttr("abrev", result).element!.attribute(by: "id")!.text)
}
catch {
print("Error: \(error)")
}
fulfill(self.abrevCode)
case .Failure(let error):
print(error)
}
}
}
}
func parseBooksXML2(index: Int, key: String) -> Promise<String> {
return Promise {fulfill, reject in
let headers = [
"Referer": "URL"
]
Alamofire.request(.GET, "URL", headers: headers)
.responseData { response in
switch response.result {
case .Success:
let xml = SWXMLHash.parse(response.data!)
do {
let range = self.classDictionary["class"]![index].rangeOfString("\\d\\d\\d", options: .RegularExpressionSearch)
let result = self.classDictionary["class"]![index].substringWithRange(range!)
try self.courseNumber = (xml["courses"]["course"].withAttr("name", result).element?.attribute(by: "id")?.text)!
}
catch {
print("Error: \(error)")
}
fulfill(self.courseNumber)
case .Failure(let error):
print(error)
}
}
}
}
func parseBooksXML3(index: Int, key: String) -> Promise<String> {
return Promise {fulfill, reject in
let headers = [
"Referer": "URL"
]
Alamofire.request(.GET, "URL", headers: headers)
.responseData { response in
switch response.result {
case .Success:
let xml = SWXMLHash.parse(response.data!)
do {
let range = self.classDictionary["class"]![index].rangeOfString("[a-zA-Z]{1,3}?\\d?\\d?\\d?$", options: .RegularExpressionSearch)
let result = self.classDictionary["class"]![index].substringWithRange(range!)
try self.instructorCode = (xml["sections"]["section"].withAttr("instructor", self.classTeacher[index]).element?.attribute(by: "id")?.text)!
}
catch {
print("Error: \(error)")
}
fulfill(self.instructorCode)
case .Failure(let error):
print(error)
}
}
}
}
func parseBooksXML4(key: String) -> Void {
let headers = [
"Referer": "URL"
]
Alamofire.request(.GET, "URL", headers: headers)
.responseData { response in
switch response.result {
case .Success:
self.parseISBN(String(data: response.data!, encoding: NSUTF8StringEncoding)!)
case .Failure(let error):
print(error)
}
}
}
Any help would be appreciated!
You need to use when:
let count = classDictionary["class"]!.count-1
let promises = (0..<count).map { index -> Promise<ReplaceMe> in
return firstly {
parseBooksXML(index)
}.then { abrevCode in
self.parseBooksXML2(index, key: abrevCode)
}.then { courseNumber in
self.parseBooksXML3(index, key: courseNumber)
}.then { instructorCode in
self.parseBooksXML4(instructorCode)
}
}
when(fulfilled: promises).then {
//…
}
Since parseBooksXML4 call is async, you should wrap parseBooksXML4() call to return promise and wait for that to finish before increment index.
firstly {
parseBooksXML(index)
}.then { abrevCode in
self.parseBooksXML2(index, key: abrevCode)
}.then { courseNumber in
self.parseBooksXML3(index, key: courseNumber)
}.then { instructorCode in
self.parseBooksXML4(instructorCode)
}.then { _ in
index += 1
}
}