0

I'm supposed to parse a data inside the header(wtf) instead of the body. The response header format looks like this

{
    "InferResponse":
    {
        "modelName": "aixyolov2",
        "modelVersion": "1",
        "batchSize": 1,
        "output":
        [
            {
                "name": "OUTPUT2",
                "raw":
                {
                    "dims": ["1"],
                    "batchByteSize": "4"
                }
            },
            {
                "name": "OUTPUT0",
                "raw":
                {
                    "dims": ["1","4"],
                    "batchByteSize": "16"
                }
            },
            {
                "name": "OUTPUT1",
                "raw":
                {
                    "dims": ["1"],
                    "batchByteSize": "4"
                }
            }
        ]
    },
    "Status":
    {
        "code": "SUCCESS",
        "serverId": "inference:0",
        "requestId": "12"
    },
    "Content-Length": 320,
    "Content-Type": "application/octet-stream"
}

I made three structs to contain the data

struct Raw: Decodable {
    
    let dims: [String]?
    let batchByteSize: String?
}

struct Output: Decodable {
    
    let name: String?
    let raw: Raw?
}

struct InferResponse: Decodable {
    
    let modelName: String?
    let modelVersion: String?
    let batchSize: String?
    let output: [Output]?
}

So the I implemented an APIManager class to send a post and parse the http response header. The code is as the following.

class APIManager {
    
    static private let apiKey = "fill in key" // paste in Key(Token)
    static private let apiUrl = "fill in url" // paste in URL

    func postData(image: UIImage) {
        
        let urlString = APIManager.apiUrl

        let url = URL(string: urlString)

        guard url != nil else {

            print("URL nil")
            return
        }

        // header
        let token = "Bearer \(APIManager.apiKey)"

        var urlRequest = URLRequest(url: url!, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 1)
        urlRequest.httpMethod = "POST"
        urlRequest.setValue(token, forHTTPHeaderField: "Authorization")
        urlRequest.setValue("batch_size: 1 input { name: \"input\" } output { name: \"Darknet\" cls { count: 1 } }", forHTTPHeaderField: "InferRequest") // fix this when we get actual value
        
        // body
        let imageData = image.pngData()
        
        urlRequest.httpBody = imageData?.base64EncodedData()
        
        // handle the request
        let dataTask = URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
            
            if (error == nil && response != nil) {
                
                let httpResponse = response as? HTTPURLResponse
                
                if let inferResponse = httpResponse?.allHeaderFields["InferResponse"] as? String {
                    
                    let newData = inferResponse.data(using: .utf8)! // this is the part I'm having trouble with 
                    
                    do {
                    
                        let parsedResponse = try JSONDecoder().decode(InferResponse.self, from: newData)
                    }
                    catch {
                        
                        
                    }
                }
                else {
                    
                    print("failed to obtain 'InferResponse' field from response header")
                }
            }
        }
    }
}

However, I'm confused of the HTTP header data. My manager sent me a binary header response that she said would be helpful. So, does this mean that the HTTP response header data is a binary? Then should I convert it into a string using utf encoding and then parse it to JSON? Plz give me some advice on how to improve this code. Thanks.

if let inferResponse = httpResponse?.allHeaderFields["InferResponse"] as? String {

    let newData = inferResponse.data(using: .utf8)! // this is the part I'm having trouble with 

    do {

        let parsedResponse = try JSONDecoder().decode(InferResponse.self, from: newData)                    
    }

    catch {


    }
}

1 Answers1

1

You're almost there. The decode operation is throwing an error due to data types:

typeMismatch(Swift.String, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "batchSize", intValue: nil)], debugDescription: "Expected to decode String but found a number instead.", underlyingError: nil))

It suggests that your InferResponse.batchSize is a Int, not a String:

struct InferResponse: Decodable {
    let modelName: String?
    let modelVersion: String?
    let batchSize: Int?
    let output: [Output]?
}

In the future, make sure to handle your errors from the do/catch (or at least print them out). That should help with debugging.

richardpiazza
  • 1,487
  • 10
  • 23
  • Thanks for the wonderful info. But can you also confirm if other data types in my script are correct? I'm really not that sure if I've set it correctly. I'm supposed to send a binary image data to the server. That's why I added the base64EncodedData() after the image data. Thanks. – MingBot Sep 08 '20 at 13:09
  • Without knowing the spec for your service layer, I can't answer the question of the format for your images. Are they supposed to be Base64 encoded? It's a fair bet that that is correct. What you have looks fairly correct. – richardpiazza Sep 08 '20 at 13:13
  • Thank you so much. May God bless you. – MingBot Sep 08 '20 at 13:20
  • No worries, make sure to up-vote the answer if it solves your issue. – richardpiazza Sep 08 '20 at 13:21