iOS Uploading Images with Alamofire
Fullstack Social iOS NodeJS REST
Now that our server is able to handle image uploads, let's test out the ability to upload images from our iOS application. This process is normally very tedious if you're writing the upload code yourself through URLSession. However since we have Alamofire installed, the upload is performed a lot simpler us a simple upload with multipart form data method. Make sure to look at your server logs to see if things are happening smoothly.

Comments (7)
pdefilippi
4 years ago
Hey Brian, I hit a few snags that I can't seem to resolve. I had the key and secret exposed from my AWS bucket. I was able to create a new key /secret and update all the variables pertaining to the .env file. I Also had to reset my AWS password per Amazon request. I cant seem to get the images to upload from the web project when I create a new post. The only error I am getting is error: Sending 500 ("Server Error") response: CredentialsError: Missing credentials in config. I tried creating a new user in Sails thinking that would link all the new creds. Is there something else that I should update after resetting my AWS password? I had everything working correct after I created a new key / secret. I am running into the issues only after I created a new AWS pwd.
lbta
4 years ago
alejandro
3 years ago
Hello Brian i get the following error: Ambiguous reference to member 'upload(_:to:method:headers:interceptor:fileManager:)'
ajayxsingh@gmail.com
3 years ago
I used this, it worked for me: AF.upload(multipartFormData: { (formData) in //for post text formData.append(Data(text.utf8), withName: "postBody") //for post image guard let imageData = self.selectedImage.jpegData(compressionQuality: 0.5) else {return} formData.append(imageData, withName:"imagefile", fileName:"DoesNotMatter", mimeType: "image/jpg") }, to: urlString).uploadProgress(queue: .main) { (progress) in print("Upload progress: \(progress.fractionCompleted)") }.response { (dataResp) in switch dataResp.result{ case .failure(let err): print("Failed to hit server:", err) case .success: if let code = dataResp.response?.statusCode,code >= 300 { print("Failed to upload with status: ", code) return } let respString = String(data: dataResp.data ?? Data(), encoding: .utf8) print("Successfully created post, here is the response:") print(respString ?? "") self.fetchPosts() } }
david_twin
3 years ago
This worked for me. Thanks
pris
3 years ago
Works for me too! Thank you so much for sharing.
kevingalloway
3 years ago
Hey Brian, I want to get a better understanding of how you broke the file into services. Is there a way to look at the code from your Xcode project? When I click download project its only giving me the files from the node.js - nothing from the Xcode project
Danny S
3 years ago
hi Brian, I'm trying to use Ajayxsingh's code, but I got exception like below, the upload is actually completed and I can see the file(no idea why it without suffix .jpg) on S3 side, but it didn't insert the data into my local mysql table. Upload progress: 1.0 2020-04-14 16:45:18.330256+0800 myjourney[35254:1047506] Task <2826E76B-6328-4966-8382-F2D377B31C5E>.<2> finished with error [-1001] Error Domain=NSURLErrorDomain Code=-1001 "The request timed out." UserInfo={_kCFStreamErrorCodeKey=-2102, NSUnderlyingError=0x600001fd43f0 {Error Domain=kCFErrorDomainCFNetwork Code=-1001 "(null)" UserInfo={_kCFStreamErrorCodeKey=-2102, _kCFStreamErrorDomainKey=4}}, _NSURLErrorFailingURLSessionTaskErrorKey=LocalUploadTask <2826E76B-6328-4966-8382-F2D377B31C5E>.<2>, _NSURLErrorRelatedURLSessionTaskErrorKey=( "LocalUploadTask <2826E76B-6328-4966-8382-F2D377B31C5E>.<2>" ), NSLocalizedDescription=The request timed out., NSErrorFailingURLStringKey=http://localhost:1337/post, NSErrorFailingURLKey=http://localhost:1337/post, _kCFStreamErrorDomainKey=4} Failed to hit server: sessionTaskFailed(error: Error Domain=NSURLErrorDomain Code=-1001 "The request timed out." UserInfo={_kCFStreamErrorCodeKey=-2102, NSUnderlyingError=0x600001fd43f0 {Error Domain=kCFErrorDomainCFNetwork Code=-1001 "(null)" UserInfo={_kCFStreamErrorCodeKey=-2102, _kCFStreamErrorDomainKey=4}}, _NSURLErrorFailingURLSessionTaskErrorKey=LocalUploadTask <2826E76B-6328-4966-8382-F2D377B31C5E>.<2>, _NSURLErrorRelatedURLSessionTaskErrorKey=( "LocalUploadTask <2826E76B-6328-4966-8382-F2D377B31C5E>.<2>" ), NSLocalizedDescription=The request timed out., NSErrorFailingURLStringKey=http://localhost:1337/post, NSErrorFailingURLKey=http://localhost:1337/post, _kCFStreamErrorDomainKey=4})
Danny S
3 years ago
I have set the AF session timeout as : AF.sessionConfiguration.timeoutIntervalForRequest = 240
Danny S
3 years ago
it's working, but why the uploaded file with no suffix?
Danny S
3 years ago
problem solved by change formData.append(imageData, withName:"imagefile", fileName:"imagefile.jpg", mimeType: "image/jpg")
hamontge1
3 years ago
For Alamofire 5.1.0 it seems that the upload multipartFormData function is different, any pointers on how the code should be written for this? If I follow your code exactly it does not upload the picture to the server or the text data to the server from iOS.
hamontge1
3 years ago
Fixed the issue with the below code, however the case .failure and case .success code does not work now... func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { guard let image = info[.originalImage] as? UIImage else { return } dismiss(animated: true) { // we'll be uploading our image here let url = "http://localhost:1337/post" let headers: HTTPHeaders = ["Content-type": "multipart/form-data", "Accept": "application/json"] AF.upload(multipartFormData: { (formData) in // post text formData.append(Data("Coming from iPhone Sim".utf8), withName: "postBody") // post image guard let imageData = image.jpegData(compressionQuality: 0.5) else { return } formData.append(imageData, withName: "imagefile", fileName: "DoesntMatterSoMuch", mimeType: "image/jpg") }, to: url, method: .post, headers: headers) .responseJSON { (resp) in print("resp is \(resp)")
hamontge1
3 years ago
now the trouble shutting, status reports and .success and .failure code works. see below func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { guard let image = info[.originalImage] as? UIImage else { return } dismiss(animated: true) { // we'll be uploading our image here let url = "http://localhost:1337/post" let headers: HTTPHeaders = ["Content-type": "multipart/form-data", "Accept": "application/json"] AF.upload(multipartFormData: { (formData) in // post text formData.append(Data("Coming from iPhone Sim".utf8), withName: "postBody") // post image guard let imageData = image.jpegData(compressionQuality: 0.5) else { return } formData.append(imageData, withName: "imagefile", fileName: "DoesntMatterSoMuch", mimeType: "image/jpg") }, to: url, method: .post, headers: headers) .uploadProgress(queue: .main, closure: { progress in print("Upload Progress: \(progress.fractionCompleted)") }).responseJSON(completionHandler: { data in print("upload finished: \(data)") }).response { (response) in switch response.result { case .success(let resut): print("upload success result: \(String(describing: resut))") self.fetchPosts() case .failure(let err): print("upload err: \(err)") } } } }
Setwork44
3 years ago
Bruv you are an absolute legend!!!!! Thank you so much
omq78
3 years ago
thank you you saved days for me
JMan
2 years ago
This was very helpful, thanks!
Lazaro Ambrosio
2 years ago
Thanks!
Setwork44
3 years ago
For those with a bug complaining about "Result" being generic, change it to "(AFResult<[Post]>)" and it will fix
Lazaro Ambrosio
2 years ago
Thanks I was wondering how to fix this issue!
omq78
3 years ago
hi brian now create.js file takes only uploads with image if am trying to send a post without image now it gives error
omq78
3 years ago
here is my create.js file excluding the comments and console.log lines //========================================================= module.exports = async function(req, res) { const postBody = req.body.postBody const file = req.file('imagefile') const options = { // This is the usual stuff adapter: require('skipper-better-s3') , key: '**************' , secret: '*********************** , bucket: '***********************' // , region: 'Asia Pacific (Singapore)' // Optional - default is 'us-standard' // Let's use the custom s3params to upload this file as publicly // readable by anyone , s3params: { ACL: 'public-read' } // , s3params: { ACL: 's3config' } // And while we are at it, let's monitor the progress of this upload , onProgress: progress => sails.log.verbose('Upload progress:', progress) } file.upload(options, async (err, files) => { if (err) { return res.serverError(err.toString()) } const fileURL = files[0].extra.Location const userId = req.session.userId await Post.create({text: postBody, user: userId, fileUrl: fileURL}).fetch() res.redirect('/post') }); } //=========================================================
HELP & SUPPORT