Home > Enterprise >  Why can I not convert my JSON encoded data in my swift project
Why can I not convert my JSON encoded data in my swift project

Time:03-02

I am trying to convert JSON with my custom structs using the codable protocol but I get the error message The data couldn’t be read because it is missing.

Here is my network call method;

    func fetchSocketData(symbols: [String], completion: @escaping ([StockInfo])->()) {
        
        let urlSession = URLSession(configuration: .default)
        
        lazy var webSocketTask = urlSession.webSocketTask(with: FINNHUB_SOCKET_API_URL!)
        
        var stock = [StockInfo]()
        
        for symbol in symbols {
            
            let string = "{\"type\":\"subscribe\",\"symbol\":\"\(symbol)\"}"
            
            let message = URLSessionWebSocketTask.Message.string(string)
            
            webSocketTask.send(message) { error in
                if let error = error {
                    print("Error sending message: \(error.localizedDescription)")
                }
                webSocketTask.receive { result in

                    switch result {
                    case .failure(let error):
                        print("Error receiving message: \(error.localizedDescription)")
                        
                    case .success(.string(let jsonData)):
                        print(jsonData)
                        guard let stockData = jsonData.data(using: .utf8) else { return }
                        let decoder = JSONDecoder()
                        do {
                            let data = try decoder.decode(SocketData.self, from: stockData)
                            print(data)
                        } catch {
                            print("Error converting JSON: \(error.localizedDescription)")
                        }
                        
                    default:
                        print("default")
                    }
                }
            }
        }
//        completion(stock)
        webSocketTask.resume()
    }
}

and here is my model code;

struct SocketData: Codable {
    var data: [StockInfo]
    var type: String
}

struct StockInfo: Codable {
    var tradeConditions: [String]
    var price: Double
    var symbol: String
    var timestamp: Double
    var volume: Double
}

enum CodingKeys: String, CodingKey {
    case symbol = "s"
    case price = "p"
    case timestamp = "t"
    case volume = "v"
    case tradeConditions = "c"
    case data
    case type
}

Here is some sample JSON;

{
   "data":[
      {
         "c": ["1", "12"],
         "p": 164.18,
         "s": "AAPL",
         "t": 1646157925188,
         "v": 1
      },
      {
         "c": ["1", "12"],
         "p": 164.18,
         "s": "AAPL",
         "t": 1646157925188,
         "v": 1
      }
   ],
   "type":"trade"
}

I feel I have tried everything to get this to work but to no avail. I am sure I have written the custom structs correctly.

CodePudding user response:

print("Error converting JSON: \(error.localizedDescription)")

=>

print("Error converting JSON: \(error)")

It will give you more useful information, like:

keyNotFound(CodingKeys(stringValue: "tradeConditions", intValue: nil), 
                       Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "data", intValue: nil),
                                                               _JSONKey(stringValue: "Index 0", intValue: 0)], 
                                                   debugDescription: "No value associated with key CodingKeys(stringValue: \"tradeConditions\", intValue: nil) (\"tradeConditions\").", 
                                                   underlyingError: nil))

In other words, it's saying that's it's not finding a value for the key tradeConditions in you JSON.

What's wrong in fact:

Let's see the doc of Encoding and Decoding Custom Types, especially the part "Choose Properties to Encode and Decode Using Coding Keys":

Codable types can declare a special nested enumeration named CodingKeys that conforms to the CodingKey protocol.

I put in bold the important part.

In other words, you have currently:

struct StructA: Codable {}
struct structB: Codable {}

enum CodingKeysForSructAAndStructB: String, CodingKey {}

Okay, so, now let's imagine this:

struct StructA: Codable {
    let propertyA: String
}
struct StructB: Codable {
    let propertyA: Int
}

How could you have two properties with the same name in different struct, but with different coding keys?

enum CodingKeysForSructAAndStructB: String, CodingKey {
    case propertyA = "something"     //For StructA
    case propertyA = "somethingElse" //For StructB
}
//That's invalid, and that wouldn't make really sense that you need to rename one of the properties...

So you need to nest them, to let the compiler know which one use which key:

struct SocketData: Codable {
    var data: [StockInfo]
    var type: String

    enum CodingKeys: String, CodingKey {
        case data
        case type
    }
}

struct StockInfo: Codable {
    var tradeConditions: [String]
    var price: Double
    var symbol: String
    var timestamp: Double
    var volume: Double

    enum CodingKeys: String, CodingKey {
        case symbol = "s"
        case price = "p"
        case timestamp = "t"
        case volume = "v"
        case tradeConditions = "c"
    }
}

CodePudding user response:

Placed enum declarations inside custom structs.

  • Related