3

I Want to use Bittrex api. I've read their api docs. There are explanations like the following.

For this version, we use a standard HMAC-SHA512 signing. Append apikey and nonce to your request and calculate the HMAC hash and include it under an apisign header.

$apikey='xxx';
$apisecret='xxx';
$nonce=time();
$uri='https://bittrex.com/api/v1.1/market/getopenorders?apikey='.$apikey.'&nonce='.$nonce;
$sign=hash_hmac('sha512',$uri,$apisecret);
$ch = curl_init($uri);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('apisign:'.$sign));
$execResult = curl_exec($ch);
$obj = json_decode($execResult);

I want to do this with Swift. But I don't want to use Alamofire. I wrote a code. I think I'm doing everything but I'm getting the following error.

{
    message = "APISIGN_NOT_PROVIDED";
    result = "<null>";
    success = 0;
}

I wrote similar code with Delphi. It works fine. So there is no problem with APIKEY. When I use the same parameters in Delphi, the same SecretHex is generated. So there's no problem with Encryption.

I think, I cannot do the Post Request with headers. I can not find the fault. Would you please help me.

func getBalances()
    {
        let apiKeySTR = "01235xxxxxx"
        let secretSTR = "41691xxxxxx"

        let path = "https://bittrex.com/api/v1.1/account/"
        let timeInterval = NSDate().timeIntervalSince1970
        let epochtime = String(floor(timeInterval))

        let urlFull = path + "getbalances" + "?" + "apikey=" + apiKeySTR + "&" + "nonce=" + epochtime

        let secretUInt8 : [UInt8] = Array(urlFull.utf8)
        var secretKey : [UInt8]?

        do {
            try secretKey = HMAC(key: secretSTR, variant: .sha512).authenticate(secretUInt8)
        } catch {
            print ("Error")
        }

        let secretHex = secretKey?.toHexString() ?? ""

        guard let url = URL(string: urlFull) else { return }
        var request = URLRequest(url: url)
        request.addValue("apising", forHTTPHeaderField: (secretHex))
        request.httpMethod = "POST"

        let session = URLSession.shared
        session.dataTask(with: request) { (data, response, error) in
            if let response = response {
                print(response)
            }

            if let data = data {
                do {
                    let json = try JSONSerialization.jsonObject(with: data, options: [])
                    print(json)
                } catch {
                    print(error)
                }
            }

            }.resume()
    }

1 Answer 1

3

First off... you have a typo:

request.addValue("apising", forHTTPHeaderField: (secretHex))

I believe it's apisign, not "apising", right?


And below is a recap on creating REST API requests with a header and body. You can update this method according your needs:

1) Create URLRequest

    var request = URLRequest(url: requestURL)

2) Set headers and http method:

    request.allHTTPHeaderFields = ["Authentication" : "Bearer XYZ..."]
    request.httpMethod = "POST"

3) Set request body:

    // parameters is a simple [String:String] dictionary, just as header
    let jsonData = try? JSONSerialization.data(withJSONObject: parameters)
    request.httpBody = jsonData

Complete example:

  public enum RESTMethod:String {
       case get = "GET"
       case post = "POST"
       case put = "PUT"
  }

  public func sendRequest(_ url: String,
                        method: RESTMethod,
                        headers: [String : String],
                        parameters: [String : Any],
                        completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionTask! {
    let requestURL: URL
    if method == .get {
        let parameterString = parameters.stringFromHttpParameters()
        requestURL = URL(string:"\(url)?\(parameterString)")!
    } else {
        requestURL = URL(string: url)!
    }


    var request = URLRequest(url: requestURL)
    request.allHTTPHeaderFields = headers
    request.httpMethod = method.rawValue
    if method == .post {
        let jsonData = try? JSONSerialization.data(withJSONObject: parameters)
        request.httpBody = jsonData
    }
    request.timeoutInterval = 60


    let task = URLSession.shared.dataTask(with: request) { (data, response, error) in

        completionHandler(data,response,error)
    }

    task.resume()

    return task

}


extension Dictionary {

/// Build string representation of HTTP parameter dictionary of keys and objects

func stringFromHttpParameters() -> String {
    let parameterArray = self.map { (key, value) -> String in
        let percentEscapedKey = (key as! String).addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)
        let percentEscapedValue = (value as? String ?? "\(value)").addingPercentEncodingForURLQueryValue()!
        return "\(percentEscapedKey)=\(percentEscapedValue)"
    }

    return parameterArray.joined(separator: "&")
}

}

Usage:

sendRequest("http://yourserver",
             method: .get, // .post or .put
             headers: [],
             parameters: [],
             completionHandler: { (data, response, error) in

   // Handle response here

})
Sign up to request clarification or add additional context in comments.

2 Comments

You're right. It has to be "apisign". But it was not the only problem. Your code worked great.
PS: I changed that line. Old One: ----> let percentEscapedKey = (key as! String).addingPercentEncodingForURLQueryValue()! New One: -----> let percentEscapedKey = (key as! String).addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.