To upload images to the server using Alamofire multipart in swift - swift

During resistration of a user, i am uploading some photoes to the server. The first one is the profile photo. And the rest photoes i want to upload after the resistration process once completed. Means i want to upload the photo after getting the userid and the access token.Means after a successfully image upload, the user will enter to the "matchesViewController" and then only the upload process will start. Also i want to upload these photoes in background thread. Also if the app rinning in background then also the upload process should be continue. All the uploading process is done by Alamofire.
By writting the following code the photos are uploading successfully, but it is taking more time. Which is not a good user experience.
Also please tell me how to continue the upload process when the app running in back ground thread.
Also if the code is not good please help me to modify any changes in code if needed.
My code is: -
private func handleRegistration (_ parameterDict : [String : Any]){
let url = USER_REGISTER_URL
let headers: HTTPHeaders = [
"Content-Type": "application/x-www-form-urlencoded"
]
Alamofire.upload(multipartFormData: { (multipartFormData) in
for (key, value) in parameterDict {
multipartFormData.append("\(value)".data(using: String.Encoding.utf8)!, withName: key as String)
}
let random = randomString(length: 7)
multipartFormData.append(self.selectedProfilePic.image!.jpegData(compressionQuality: 0.4)!, withName: "fileset",fileName: "\(random).jpg", mimeType: "image/jpg")
}, usingThreshold: UInt64.init(), to: url, method: .post, headers: headers) { (result) in
switch result{
case .success(let upload, _, _):
upload.responseJSON { response in
print(response)
var userId: String?
var token: String?
if let result = response.result.value {
let JSON = result as! NSDictionary
guard let accessToken = JSON["access_token"] as? String
else{ return }
guard let uid = JSON["User_Id"] as? String
else{return}
userId = uid
token = "bearer " + accessToken
UserDefaults.standard.set(Int(uid), forKey: "User_Id")
UserDefaults.standard.set(token, forKey: "access_token")
let phone = JSON["Phone_Number"] as? String
let FirstName = JSON["FirstName"] as? String
let email = uid + "#gmail.com"
let tempDictionary : Dictionary = [kFIRSTNAME : FirstName!, kLASTNAME : "", kFULLNAME : FirstName!, kPHONE : phone!, kREGISTERUSEID: userId, kEMAIL: email] as [String : Any]
self.checkDeviceTokenAvailibility(uid: Int(userId!)!)
self.startRegistrationWithFirebase( detailDict: tempDictionary)
let storyBoard = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "MainTabbarView")
storyBoard.modalPresentationStyle = .fullScreen
self.present(storyBoard, animated: true, completion: nil)
if let message = JSON["Message"] as? String{
if message == "Please Upload a image."{
}
}
}
if userId != nil && token != nil{
self.multiImageUpload(userId: Int(userId!)!, token: token!)
}
print("Succesfully uploaded")
if let err = response.error{
// onError?(err)
// print(err.localizedDescription)
return
}
// onCompletion?(nil)
}
case .failure(let error):
print("Error in upload: \(error.localizedDescription)")
// onError?(error)
}
}
}

One way to make the upload time a lot less is to send multiple parts at once using a for loop and using Alamofire to send a specific part. That way, it breaks it up into parts that take a lot less time to send. The con is that on the server side you will have to reconstruct the image.
Also if the code is not good please help me to modify any changes in code if needed.
It seems you have a lot of arbitrary space in your code.

Related

Alamofire request is send twice

I’m alamofire request, and I found the request is sent twice. Is it an alamofire bug?
I tried to put a breakpoint, but it's not called twice and there is no button call this function twice or trigging twice.
Here is my code
ARSLineProgress.show()
guard let url = URL(string: "https://laghmat.kbmediawebites.com/api/rest/account") else {return}
let headers = ["X-Oc-Merchant-Id": MerchantId, "X-Oc-Session": K_Defaults.string(forKey: "sessionID"),"ACCEPT":"application/json"]
let params = [
"firstname": firstName,
"lastname": lastName,
"email": email,
"telephone": phone
] as! [String : String]
Alamofire.request(url, method: .put, parameters: params,encoding: JSONEncoding.default, headers: headers as! HTTPHeaders).responseJSON {
response in
switch response.result {
case .success:
print(response)
guard let data = response.data else { return }
do {
let value = try JSONDecoder().decode(GeneralResponse.self, from: data)
if value.success == 1 {
ARSLineProgress.showSuccess()
var user = GlobalHelpers.getUser()
user?.firstname = firstName
user?.lastname = lastName
user?.telephone = phone
user?.email = email
GlobalHelpers.setUser(user)
self.dismiss(animated: true, completion: nil)
} else {
ARSLineProgress.showFail()
self.alert(msg: LanguageManger.localize(word: "serverError"))
}
} catch let error {
ARSLineProgress.showFail()
self.alert(msg: LanguageManger.localize(word: "serverError"))
print(error)
}
break
case .failure(let error):
ARSLineProgress.showFail()
self.alert(msg: LanguageManger.localize(word: "serverError"))
print(error)
}
}
well I recommend you that lock all the buttons in the view, when the request is made.
for viewW in view.subviews
{
if let buttonV = viewW as? UIButton
{
buttonV.isUserInteractionEnabled = false
}
}
obviously, you have to lock at the beginning and unlock at the end.
if this isn't works, put a breakpoint in this line
switch response.result {
the first time, you will see that you don't have a status code, because the server not yet responding, then press next step, and you will see a status code, because the server already responds to you.
that doesn't mean the request is made twice, I think that this happen because the handshake is happening.

"Invalid file" error while uploading pdf from iCloud drive in swift

I need to upload documents from iPhone to the application server, for that I am using UIDocumentPickerViewController and I have successfully picked the file from iCloud drive but when I am uploading file using alamofire multipart I am getting error msg from backend "invalid file".
I have converted the URL received from UIDocumentPickerViewController to Data before uploading. Here is my complete code:
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentAt url: URL) {
self.urlfilepick = url
self.filePickedBlock?(url)
stringpath = self.urlfilepick.path
print(stringpath)
thefileExtension = (stringpath as NSString).lastPathComponent
thefileExtension = thefileExtension.replacingOccurrences(of: "Response Format :-.", with: "")
UserDefaults.standard.set( thefileExtension , forKey: "fromdocument")
stringcomingFrom = "Files"
parameters = ["user_id":UserDefaults.standard.string(forKey: "userid") ?? "","treatment_id":self.treatmentId!,"document_type_id":strdocumnetid,"label_name":self.txtdocumnetName.text!,"type":"1","ext":thefileExtension,]
let datadat = NSData(contentsOfFile: stringpath)
data = datadat as Data?
self.requestWith(data: data, fileName: "file", pathExtension: thefileExtension, parameters: parameters)
}
and the alamofire upload function is:
func requestWith(
data: Data?,
fileName: String?,
pathExtension: String?,
parameters: [String : Any]){
guard let emailId = UserDefaults.standard.string(forKey: "email") else {return}
guard let token = UserDefaults.standard.string(forKey: "token") else {return}
let headers = ["Content-Type": "application/json","med-token":token,"user":emailId,"device":"1"]
let URL = baseUrl + uploadDocuments
Alamofire.upload(multipartFormData: { (multipartFormData) in
for (key, value) in parameters {
multipartFormData.append("\(value)".data(using: String.Encoding.utf8)!, withName: key as String)
}
if let data = data {
multipartFormData.append(data, withName: "filename", fileName: "\(fileName!).\(pathExtension!)", mimeType: "\(fileName!)/\(pathExtension!)")
}
}, usingThreshold: UInt64.init(), to: URL, method: .post, headers: headers) { (result) in
switch result {
case .success(let upload, _, _):
upload.responseJSON { response in
print(response)
if let err = response.error {
return
}
}
case .failure(let error):
print("Error in upload: \(error.localizedDescription)")
}
}
}
It is working fine in Postman.

Insert Description with Line Breaks via YouTube Data API

I have working code that uploads my video, title and description to YouTube using Swift and Alamofire.
My description uploads to YouTube as one line and I would like to split the line to break after each variable.
My description variable is like so:
myDescription = (price! as! String) + " " + (package! as! String)
When that get's sent to YouTube, it appears as:
"Pricehere PackageName"
I want the PackageName to show in the YouTube description with a line break like:
"Pricehere
PackageName"
I had done this in Objective C on an old project like this:
NSString *description = [NSString stringWithFormat:#"%#\n%#\n%#\n%#\n%#\n%#", str1, str2, str3, str4, str5, str6];
When that was passed to YouTube it added each variable and then did a line break.
Thanks for any help.
edit adding in the function for the YouTube Upload for reference:
func postVideoToYouTube(token: String, callback: #escaping (Bool) -> Void){
let headers = ["Authorization": "Bearer \(token)"]
let urlYoutube = "https://www.googleapis.com/upload/youtube/v3/videos?part=snippet"
let path = videoURL?.path
let videodata: Data = NSData.dataWithContentsOfMappedFile(path!)! as! Data
upload(
multipartFormData: { multipartFormData in
multipartFormData.append("{'snippet':{'title' : '\(self.myTitle)', 'description': '\(self.myDescription)'}}".data(using: String.Encoding.utf8, allowLossyConversion: false)!, withName: "snippet", mimeType: "application/json")
multipartFormData.append(videodata, withName: "video", fileName: "video.mp4", mimeType: "application/octet-stream")
},
to: urlYoutube,
method:Alamofire.HTTPMethod.post,
headers:headers,
encodingCompletion: { encodingResult in
switch encodingResult {
case .success(let upload, _, _):
upload.responseJSON { response in
print(response)
let result = response.result.value
let JSON = result as! NSDictionary
let videoId = JSON.object(forKey: "id") as! String
print("VideoID: ", videoId)
self.addVideoToPlaylist(videoId: videoId, callback: { (result) in
callback(result)
})
}
break
case .failure(let encodingError):
print(encodingError)
callback(false)
break
}
}
)
}
Be careful with force casting variables like you're doing, I would recommend you to do something like this:
if let price = price as? String, let package = package as? String {
myDescription = "\(price)\n\(package)"
}
This way you're safe unwraping and also using String Interpolation to construct the string, you can read more in the documentation.

Only last image in array uploads with Alamofire Swift 3

I need to upload several images to an API at a time. I am using Alamofire to upload images and the API only allows one image to be uploaded per request.
I have an array of images to be uploaded and I am going through the array and making a request each time. Each image appears to upload correctly but then when I check the database only the last image has been uploaded. Each call prints a response with status code 200 and prints "SUCCESSFUL" when it is complete, but only the last in the array actually uploads.
for (index, image) in self.imageArray.enumerated() {
self.uploadImages(photoIndex: index, photo: image.image, fileName: image.imageName, completion: { (true) in
print("SUCCESSFUL")
})
}
I have also tried using a DispatchQueue, but that didn't work either:
let serialQueue = DispatchQueue(label: "serialQueue")
And inside the for loop
serialQueue.async {
//self.uploadImages...
}
I've tried a few more things, with no success. How would I be able to fix this?
Also, here is my uploadImages function
func uploadImages(photoIndex: Int, photo: UIImage, fileName: String, completion: #escaping (_ Success : Bool?) -> ())
{
var success: Bool? = nil
let url : String? = //url
// define parameters
let parameters = [
"photoIndex": "\(photoIndex)"]
Alamofire.upload(multipartFormData: { multipartFormData in
if let imageData = UIImageJPEGRepresentation(photo, 1)
{
multipartFormData.append(imageData, withName: "photo", fileName: fileName, mimeType: "image/png")
}
for (key, value) in parameters {
multipartFormData.append((value.data(using: .utf8))!, withName: key)
}}, to: url!, method: .post,
encodingCompletion: { encodingResult in
switch encodingResult {
case .success(let upload, _, _):
upload.response { [weak self] response in
guard let strongSelf = self else {
return
}
success = true
debugPrint(response)
completion(success)
}
case .failure(let encodingError):
print("error:\(encodingError)")
}
})
}
Use multipart form data to upload the any media file below is the code snippet to upload multiple video
for (key , value) in arrMediaUpload {
if let image = value as? UIImage {
if let imageData = UIImageJPEGRepresentation(image,0.9) {
print("key => \(key)")
multipartFormData.append(imageData as Data, withName: key, fileName: "\(NSDate().timeIntervalSince1970).jpg", mimeType: "image/*")
}
}
}
I am not that good in Swift so take this answer with a grain of salt but I do not see were you are adding any new keys to your dictionary. So since there is only one key there would be only one value. Therefore the for loop would only be done one time.

Download high resolution profile image Facebook Graph API

Using Swift 3.0, I can download the current users image using this graph call function:
fileprivate func getCurrenUserHighResImageURL() -> String? {
var photoURLOutput : String?
let graphRequest : FBSDKGraphRequest = FBSDKGraphRequest(graphPath: "me", parameters: ["fields" : "picture.type(large)"])
graphRequest.start(completionHandler: { connection, result , error -> Void in
if ((error) != nil) {
print("Error: \(error)")
} else {
let castedResult = result as? [String : AnyObject]
if let castedResult = castedResult {
if let photURL = castedResult["picture"] as? [String : AnyObject] {
let photoData = photURL["data"] as? [String : AnyObject]
photoURLOutput = photoData?["url"] as? String
if let photoURLOutput = photoURLOutput {
CURRENT_USER_URL.updateChildValues(["highResImageURL" : "\(photoURLOutput)"])
}
}
}
}
})
return photoURLOutput
}
However this only returns a 200 x 200 pixel image. Is there any way to make a graph call to to a higher resolution?
Ive seen people making calls to the graph API using a URL like this: https://graph.facebook.com/userId/picture?width=640&height=640
as mentioned in this post : Facebook Graph Profile Picture Link
But when i attempt to download an image from that URL...
func loadUserImage(fromURL urlString:String?) {
if urlString != nil {
let imgURL: URL = URL(string: urlString!)!
let request: URLRequest = URLRequest(url: imgURL)
let session = URLSession.shared
let task = session.dataTask(with: request, completionHandler: {
(data, response, error) -> Void in
if (error == nil && data != nil) {
func display_image() {
let userImage = UIImage(data: data!)
self.userImage.image = userImage
}
DispatchQueue.main.async(execute: display_image)
}
})
task.resume()
}
}
there is no image to be found. (I know this function works because it works on all other images). When I type the link into my browser directly it get a JSON error back saying i don't have authorization.
Has anyone had any luck make a graph call like this? Is there some syntax I have overlooked which will return a higher res profile image?
This is what I have to request 200x200 on an iPhone 7 and 300x300 on a plus size. However I don't think you get an image back with those exact sizes. It might be slightly larger.
let deviceScale = Int(UIScreen.main.scale)
let width = 100 * deviceScale
let height = 100 * deviceScale
let parameters = ["fields": "first_name, last_name, picture.width(\(width)).height(\(height))"]
In summary, the syntax for the param to request a 400x400 would be picture.width(400).height(400).