Swift Alamofire file upload with signed request: how to send authorization headers?
<p>Scenario:</p>

<ul>
<li>iPhone iOS 8+ app</li>
<li>Logged in user will upload a profile picture</li>
</ul>

<p>The app already uses Alamofire to make signed requests to the backend API. Really simple: the app sends three specific HTTP headers (<code>Authorization</code>, <code>X-Api-Key</code> and <code>timestamp</code>) for the request to be signed. Calling <code>Alamofire.request</code> it's easy to send <code>headers</code> as parameter so it's working beautifully.</p>

<p>Now the users need to be able to <strong>upload</strong> their profile picture. Since the user is already logged into the app, the backend API will know which user is sending the picture by it's <strong>signed request</strong> - and that's the tricky part I've been struggling with for the past few hours. <code>Alamofire.upload</code> accepts completely different parameters from <code>.request</code>, so I can't figure out how to send headers when uploading a file. </p>

<p>Tried the old <code>Alamofire.Manager.session.configuration.HTTPAdditionalHeaders</code>, but it's <a href="https://github.com/Alamofire/Alamofire/issues/111#issuecomment-97558029" rel="nofollow">no longer supported</a>. Found tons code examples of file upload, none consider sending custom headers.</p>

<p>How can I send custom headers when using <code>Alamofire.upload</code> method?</p>

<pre><code>typealias requestDataType = [String:AnyObject]
private func signRequest(data: requestDataType) -&gt; [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 &lt; $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)
    })
}
</code></pre>

<p>The requests goes through. In the backend log I can see the request returned <code>HTTP 403</code> - Not authorized as it wasn't possible to sign the request. Printing the request headers, no custom auth headers were received by the server.</p>