Scenario:
- iPhone iOS 8+ app
- Recorded user upload profile picture
The application already uses Alamofire to execute signed API requests. Actually simple: the application sends three specific HTTP headers ( Authorization , X-Api-Key and timestamp ) for the request to be signed. Calling Alamofire.request easily send headers as a parameter to make it work beautifully.
Users should now be able to upload their profile picture. Since the user is already registered in the application, the backend API will know which user sends the image using a signed request , and this is the difficult part that I have struggled with over the past few hours. Alamofire.upload accepts completely different parameters from .request , so I cannot figure out how to send headers when downloading a file.
I tried the old Alamofire.Manager.session.configuration.HTTPAdditionalHeaders , but was no longer supported . Found code examples for downloading files, no one is considering sending custom headers.
How can I send custom headers when using the Alamofire.upload method?
typealias requestDataType = [String:AnyObject] private func signRequest(data: requestDataType) -> [String:String] { var headers = [String:String]() var authString = "" var signatureHeaders = "" // Iterates over SORTED data dictionary to build headers for (k,v) in (data.sort{$0.0 < $1.0}) { if !authString.isEmpty { authString += "\n" signatureHeaders += " " } authString += "\(k): \(v)" signatureHeaders += "\(k)" headers[k] = "\(v)" } let userApiKey = _loggedInUser!["api_key"].string! let signature = authString.sha256(_loggedInUser!["api_secret"].string!) headers["X-Api-Key"] = userApiKey headers["Authorization"] = "Signature headers=\"\(signatureHeaders)\",keyId=\"\(userApiKey)\",algorithm=\"hmac-sha256\",signature=\"\(signature)\"" return headers } func uploadProfilePicture(photo: UIImage, callback: apiCallback){ guard let userId = _loggedInUser?["pk"].int else { callback(Response(success: false, responseMessage: "User not logged in")) return } let requestData: requestDataType = ["timestamp": "\(Int(NSDate().timeIntervalSince1970))"] let aManager = Manager.sharedInstance print(self.signRequest(requestData)) // Prints correct headers (Authorization, X-Api-Key, timestamp) aManager.session.configuration.HTTPAdditionalHeaders = self.signRequest(requestData) print(aManager.session.configuration.HTTPAdditionalHeaders) // Prints default headers, completely ignoring my custom headers aManager.upload(.POST, "\(_apiBaseUrl)profiles/\(userId)/photo/", multipartFormData: { multipartFormData in if let imageData = UIImageJPEGRepresentation(photo, 0.8) { multipartFormData.appendBodyPart(data: imageData, name: "upload", fileName: "userphoto.jpg", mimeType: "image/jpeg") } for (key, value) in requestData { multipartFormData.appendBodyPart(data: value.dataUsingEncoding(NSUTF8StringEncoding)!, name: key) } }, encodingCompletion: { encodingResult in debugPrint(encodingResult) }) }
Requests pass. In the backend log I see an HTTP 403 return request - not allowed because it was not possible to sign the request. By printing request headers, user headers did not receive the server.