Home>
Overview

I am using SWIFT to build API communication without using libraries such as Alamofire.

Introduction to virtual currency system trading starting with Swift
With this site as a reference, the entire API structure was advanced to the point where no build errors occurred.

There are the following errors and I can't find a solution.

Error details

Build Succeeded->Run launches the simulator and triggers sending requests
When the button is clicked, the following is output to the debug console.
The response from the communication destination is displayed as a string.

Optional ("{\" error \ ": {\" message \ ": \" Authorization Required \ ", \" name \ ": \" HTTPError \ "}}")


Recognizing that it is an error that requires authentication, creating breakpoints in the debug console
Looking at the contents of the request, the flow to create authentication in the program is working firmly, the request URL is
Looks like it ’s done.

The following is the value of urlRequest created at the break console and confirmed by the debug console.
Access: https://testnet.bitmex.com/api/

Printing description of urlRequest:
▿ https:/(access destination) .com/api/v1/order? OrderType = Limit&symbol = XBTUSD&orderQty = 1&price = 590
  ▿ url: Optional<URL>
    ▿ some: https:/(access destination) .com/api/v1/order? OrderType = Limit&symbol = XBTUSD&orderQty = 1&price = 590
      -_url: https:/(access destination) .com/api/v1/order? orderType = Limit&symbol = XBTUSD&orderQty = 1&price = 590
  -cachePolicy: 0
  -timeoutInterval: 60.0
  -mainDocumentURL: nil
  -networkServiceType: __C.NSURLRequestNetworkServiceType
  -allowsCellularAccess: true
  Method httpMethod: Optional<String>
    -some: "POST"
  HTTP allHTTPHeaderFields: Optional<Dictionary<String, String >>
    ▿ some: 4 elements
      ▿ 0: 2 elements
        -key: "Content-Type"
        -value: "application/json"
      ▿ 1: 2 elements
        -key: "ACCESS-SIGN"
        -value: "b2dce06bcb67b6db3418b5018b45cf9f4442986ed05b76fc91bfb4303788332f"
      ▿ 2: 2 elements
        -key: "ACCESS-TIMESTAMP"
        -value: "1575546"
      ▿ 3: 2 elements
        -key: "ACCESS-KEY"
        -value: "Access key is output"
  -httpBody: nil
  -httpBodyStream: nil
  -httpShouldHandleCookies: true
  -httpShouldUsePipelining: false
Authentication code

Program execution flow

  1. User action (button press)
  2. Session class call
  3. Execute buildURLRequest method (extension Requestable in RequestAPI)
  4. Start creation of authentication header if isAuthorizedRequest is true in buildURLRequest (confirmed to be true in console)
  5. Create HMAC SHA256 authentication hash with makeAccessSignWith
  6. Return to buildURLRequest, pack everything and complete as urlRequest
  7. Return to Session class and execute with task.resume () using URLSession
enum Result<T, E: Error>{
    case success (T)
    case failure (E)
}
enum ResponseError: Error {
    case unexpectedResponse
}
// Protocol definition
protocol Requestable {
    (Omitted)
}
extension Requestable {
    func buildURLRequest ()->URLRequest {
        let url = Self.baseURL.appendingPathComponent (Self.path)
        var urlRequest = URLRequest (url: url)
        var header: [String: String] = Self.headerField
        urlRequest.httpMethod = Self.httpMethod
        if Self.isAuthorizedRequest {
            header ["ACCESS-KEY"] = "You have entered an access key"
            let now = (Date (). timeIntervalSince1970/1000) + 60
            let timeStamp = String (Int (now.rounded ())
            header ["ACCESS-TIMESTAMP"] = timeStamp
            header ["ACCESS-SIGN"] = makeAccessSignWith (accessKey: "You have entered your access key",
                                                       timeStamp: timeStamp,
                                                       method: Self.httpMethod,
                                                       path: Self.path,
                                                       queryParams: Self.queryParameters,
                                                       body: Self.httpBody)
            header ["Content-Type"] = "application/json"
        }
        header.forEach {key, value in
            urlRequest.addValue (value, forHTTPHeaderField: key)
        }
        if let body = Self.httpBody {
            urlRequest.httpBody = body
        }
        guard var urlComponents = URLComponents (url: url, resolvingAgainstBaseURL: true) else {
            return urlRequest
        }
        urlComponents.query = Self.queryParameters
            .map {"\ ($0.key) = \ ($0.value)"}
            .joined (separator: "&")
        urlRequest.url = urlComponents.url
        return urlRequest
    }
}
final class Session {
    static let shared = Session ()
    private init () {}
    func send<T: Requestable>(_ request: T, closure: @escaping (Result<T.Response, ResponseError>)->Void) {
        let urlRequest = request.buildURLRequest ()
        let task = URLSession.shared.dataTask (with: urlRequest) {(data, rawResponse, error) in
            // If an error is occurred.if error! = nil {
                closure (.failure (.unexpectedResponse))
                return
            }
            // If the data is empty.
            guard let data = data else {
                closure (.failure (.unexpectedResponse))
                return
            }
            // Decode the value.
            do {
                let decoder = JSONDecoder ()
                let result = try decoder.decode (T.Response.self, from: data)
                closure (.success (result))
            } catch {
                let errorResult = String (data: data, encoding: .utf8) // Convert to String for debugging
                print (errorResult as Any)
            }
        }
        task.resume ()
    }
}
private func makeAccessSignWith (accessKey: String, timeStamp: String, method: String, path: String, queryParams: [String: Any],

 body: Data?)->String? {
    let key = "Entering secret key"
    var bytes: [UInt8] = []
    bytes + = timeStamp.bytes
    bytes + = method.bytes
    bytes + = path.bytes
    func makeSign (queryParams: [String: Any])->String {
        let requestBody = "?" + queryParams
            .map {"\ ($0.key) = \ ($0.value)"}
            .joined (separator: "&")
        return requestBody
    }
    if! queryParams.isEmpty {
        let str = makeSign (queryParams: ViewController.PostNewOrderRequest.queryParameters)
        let data: Data? = str.data (using: .utf8)
        let hexStr: String = data! .map {String (format: "% .2hhx", $0)}. joined ()
        var bytes = [UInt8] ()
        for char in hexStr.utf8 {
            bytes + = [char]
        }
    }
    if body? .isEmpty == false {
        if let bodyParameter = body,
            let bodyString = String (data: bodyParameter, encoding: .utf8) {
            bytes + = bodyString.utf8
        }
    }
    let signedString = try! HMAC (key: key, variant: .sha256) .authenticate (bytes)
    return signedString.toHexString ()
}
  • Answer # 1

    Thank you very much.

    From this document, it seems that [permanent API key] is required: Bitmex REST API

    And according to Authenticating with an API Key, you need to send the following header:

    api-expires
    api-key
    api-signature


    The sample code provided by masayoshi555 seems to have a slightly different header.
    Please confirm.