I have a segmentControl with time range (1D, 3M, 1Y, 5Y) to perform network call and fetch data from Yahoo. First, I had to add a method for my String values for x-axis with the following:
class XAxisStringValueFormatter: NSObject, IAxisValueFormatter {
private var sValues: [String] = []
init(values: [String]) {
sValues = values
}
func stringForValue(_ value: Double, axis: AxisBase?) -> String {
return sValues[Int(value)]
}
}
In my LineChartViewController, I have a delegate for the timeRange change to perform my networkCall:
func networkCall(range: ChartRange) {
let symbol = self.symbol
YahooFinanceApiClient.requestEquityChartpoints(symbol: symbol, range: range, onSuccess: { chartpts in
self.createLineChart(range: range, chartpoints: chartpts)
}, onError: { error in
print("Error: \(error.localizedDescription)")
})
}
Here is my create Line ChartView:
func createLineChart(range: ChartRange, chartpoints: [EquityChartpoint]) {
// set data
var xValues: [String] = []
var yValues: [Double] = []
for object in chartpoints {
switch range {
case .OneDay:
let x = Formatters.sharedInstance.stringFromHours(date: object.date)
xValues.append(x!)
case .ThreeMonth, .OneYear, .FiveYear:
let x = Formatters.sharedInstance.stringFromDate(date: object.date)
xValues.append(x!)
}
let y = object.close
yValues.append(y!)
}
// charting
let formatter: XAxisStringValueFormatter = XAxisStringValueFormatter(values: xValues)
let xaxis: XAxis = XAxis()
var dataEntries: [ChartDataEntry] = []
for i in 0..<xValues.count {
let dataEntry = ChartDataEntry(x: Double(i), y: yValues[i], data: xValues as AnyObject)
dataEntries.append(dataEntry)
}
xaxis.valueFormatter = formatter
let lineChartDataSet = LineChartDataSet(values: dataEntries, label: "Price")
let data: LineChartData = LineChartData(dataSets: [lineChartDataSet])
self.lineChartView.data = data
self.lineChartView.xAxis.valueFormatter = xaxis.valueFormatter
}
I have a Fatal Error - Index out of range when I moved from "3M" to "1Y". I checked the xValues count to match yValues for each time range. It appears the error arrived at that xAxisStringValueFormatter where yValues count does not match the xValues. Seems strange to me. Any idea what I did wrong?
where the error happens? for a wild guess, return sValues[Int(value)] ?
@liuxuan30 That's correct. You can see the attached screenshot where the values don't match up to sValues. I already checked to make sure each xValues count matches yValues count for each segment.

value is not an index, it's a value. It could be _any_ value. It may not be your only crash - you may encounter values that Swift will refuse to cast to Int just like that, because they are in the 64bit dimension. You may encounter decimal values or values our of your expected range.
If you insist on using value as an index, you should put that in a variable first, and check for being "in range", obviously, to avoid the "Index out of range" exception.
I actually follow this suggestion - https://github.com/danielgindi/Charts/issues/1340
To follow up your comment, I searched around to figure out a solution to this issue. Still, I'm not sure what I need to do to fix this. Can you offer some suggestion? Apologize for any inconvenience.
A fix i did was the following code hopefully it helps.
`
@objc(ChartFormatter)
class ChartFormatter: NSObject, IAxisValueFormatter {
// The array of values to show on x axis
private var myArr: [String]!
init(myArr: [String]) {
self.myArr = myArr
}
func stringForValue(_ value: Double, axis: AxisBase?) -> String {
let val = Int(value)
if val >= 0 && val <= myArr.count {
return myArr[Int(val)]
}
return ""
}
}
`
@tunds I think there is a little error in your workaround. It should be
if val >= 0 && val < myArr.count {
return myArr[Int(val)]
}
otherwise the app would throw an Index out of range error when val = myArr.count
@tunds Plz do the following changes your code will work..Crash occurs when there is only one reading ....
func stringForValue(_ value: Double, axis: AxisBase?) -> String {
return myArr[Int(value) % myArr.count]
}
Most helpful comment
@tunds I think there is a little error in your workaround. It should be
if val >= 0 && val < myArr.count { return myArr[Int(val)] }otherwise the app would throw an Index out of range error when
val = myArr.count