iosarraysswiftuiswiftui-chartsswift-dictionary

Swift/ContiguousArrayBuffer.swift:600: Fatal error: Index out of range, how do i solve this?


I am new to coding and SwiftUI in general, I am coding a crypto portfolio app I am doing the candle stick chart using apple chart API, get my API data fetched. sorry if my code is messy.

JSON from market data rest API the "60" means the number of seconds and so on till the number of sec of 1 week with prices of the coin in the squared brackets

  {
"result": {
    "60": [
      [
    1665841920,
    19131.1,
    19131.8,
    19131.1,
    19131.8,
    0.1343188,
    2569.67054912
  ],
  [
    1665841980,
    19130.8,
    19130.8,
    19130.8,
    19130.8,
    0.05614383,
    1074.076382964



]
       ],
          "180": [ ] 
       },
         "allowance": {
           "cost": 0.015,
           "remaining": 7.33,
           "upgrade": "For unlimited API access, create an account at https://cryptowat.ch"
         }
       }

here is my data model

import Foundation
import SwiftUI
// MARK: - Result
struct Result: Codable {

let result: [String: [[Double]]]?
let allowance: Allowance
}

// MARK: - Allowance
struct Allowance: Codable {

let cost, remaining: Double
let upgrade: String
}

here is my CoinNetworkManager where i do the networking

func loadData(){
        // the url to request data from
    let urlAlone = "https://api.cryptowat.ch/markets/kraken/btcusd/ohlc"
    
    if let url = URL(string: urlAlone) {
            // requesting data using URL session and dataTask, getting 3 parameters back, and put em in dat, response, error
            // data is what the data we get back that we will be decoding and put em into our data object
        let session = URLSession.shared
        let task = session.dataTask(with: url){ data,response,error in
                // if no error do this
                // we will unwrap the optional data into unwrappedData and decode it using JSONDecoder
            if error == nil{
                print("task complete")
                

let decoder = JSONDecoder()
                    if let safeData = data {
                        do{
                            
                        let decodedData = try decoder.decode(Result.self, from: safeData)
                        DispatchQueue.main.async {
                                // and pass the result to coinsData
                            self.result = decodedData
                            print(self.result.result?["3600"]?[1][2] ?? 0)
                            print(self.result.result?["3600"]?[1] ?? 0)
                        }
                    } catch {
                        print(error.localizedDescription)
                    }
                }
                
            }
            
        }
        task.resume()
            
    }
    
    
}

printing above get me this in the console

19727.0 [1662278400.0, 19701.4, 19727.0, 19631.7, 19637.1, 24.43309418, 480989.386594268]

but when i try to put it in a chart i where i assume i will access through the index of the data array but i get: Fatal error: Index out of range how do i properly access the dict/ array [String:[Double]], was i formatted incorrectly or the method?

thank you in advance everyone 🙏

Chart code here

struct ContentView: View {
@ObservedObject var coinNM: CoinNetworkManager
    //  var pricesArray: [Double]

var body: some View {
        //      pricesArray = coinNM.result.result?["3600"] as! [Double]
    
VStack {
    Chart(coinNM.result.result?["60"] ?? [[0.4],[0.5]], id: \.self) { price in
        RectangleMark(x: .value("Close Time", price[0]),
                      yStart: .value("Low Price", price[1]),
                      yEnd: .value("High Price", price[0]), width: 4)

    }

Solution

  • In ContentView inside Chart, try this approach where you check the price (an array of Double) size, to solve your index out of range error:

    Chart(coinNM.result.result?["60"] ?? [[0.4],[0.5]], id: \.self) { price in
        if price.count > 1 {  // <-- here
            RectangleMark(x: .value("Close Time", price[0]),
                          yStart: .value("Low Price", price[1]),  // <-- because
                          yEnd: .value("High Price", price[0]), width: 4)
        }
    } 
    

    Or this, depending on what you want to show:

    Chart(coinNM.result.result?["60"] ?? [[0.4],[0.5]], id: \.self) { price in
        if price.count > 3 {  // <-- here
            RectangleMark(x: .value("Close Time", price[0]),
                          yStart: .value("Low Price", price[3]),  // <-- because
                          yEnd: .value("High Price", price[2]), width: 4)
        }
    }