I am trying to design an ios app to display json data in a line chart.
First of all, this is my json data.
{
TH_5min: [
{
Data: "2019-02-23T00:00:00",
Time: "11:00:00",
XTP_A: 10.5, //temperature 1
XHP_A: 11.5, //humidity 1
XTP_B: 33.5,
XHP_B: 44.6,
XTP_C: 88.9,
XHP_C: 66.6,
XTP_D: 77.9,
XHP_D: 99.6,
XTP_E: 87.87,
XHP_E: 66.66
},
{
Data: "2019-02-23T00:00:00",
Time: "11:05:00",
XTP_A: 55.2, //temperature 1
XHP_A: 44.3, //humidity 1
XTP_B: 66.6,
XHP_B: 77.87,
XTP_C: 87.77,
XHP_C: 87.87,
XTP_D: 8.87,
XHP_D: 78.78,
XTP_E: 87.78,
XHP_E: 87.87
}
]
}
This is my implementation of the swift code showing json data.
override func viewDidLoad() {
super.viewDidLoad()
apiip = APIip
getlatestTh_5min()
#objc func getlatestTh_5min(){
guard let th_5minUrl = URL(string: "http://" + apiip + "/api/Th_5min") else{
return
}
let request = URLRequest(url: th_5minUrl)
let task = URLSession.shared.dataTask(with: request, completionHandler: {(data,response,error) -> Void in
if let error = error {
print(error)
return
}
if let data = data {
self.th_5mins = self.pardrJsonData(data: data)
self.getchat()
}
})
task.resume()
//getchat()
}
func pardrJsonData(data: Data) -> [Th_5min]{
var th_5mins = [Th_5min]()
do {
let jsonResult = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers) as? NSDictionary
let jsonTh_5mins = jsonResult?["TH_5min"] as! [AnyObject]
print(jsonTh_5mins)
print(th_5mins.count)
for jsonTh_5min in jsonTh_5mins{
var th_5min = Th_5min()
th_5min.Data = jsonTh_5min["Data"] as! String
th_5min.Time = jsonTh_5min["Time"] as! String
th_5min.XTP_A = jsonTh_5min["XTP_A"] as! Double
th_5min.XHP_A = jsonTh_5min["XHP_A"] as! Double
print(th_5min)
th_5mins.append(th_5min)
//getchat()
} }catch{
print(error)
}
//getchat()
return th_5mins
}
This is how I draw the line chart, using swift code.
#objc func getchat(){
chartView = LineChartView()
chartView.frame = CGRect(x: 20, y: 80, width: self.view.bounds.width-20,height: self.view.bounds.height-100)
self.view.addSubview(chartView)
var dataEntries1 = [ChartDataEntry]()
for i in 0..<th_5mins.count {
chartView.xAxis.valueFormatter = IndexAxisValueFormatter(values: [th_5mins[i].Time])
let y = th_5mins[i].XTP_A
let entry = ChartDataEntry.init(x: Double(i), y: Double(y))
dataEntries1.append(entry)
}
let chartDataSet1 = LineChartDataSet(entries: dataEntries1, label: "temperature")
chartDataSet1.colors = [NSUIColor.red]
var dataEntries2 = [ChartDataEntry]()
for i in 0..<th_5mins.count {
chartView.xAxis.valueFormatter = IndexAxisValueFormatter(values: [th_5mins[i].Time])
let y = th_5mins[i].XHP_A
let entry = ChartDataEntry.init(x: Double(i), y: Double(y))
dataEntries2.append(entry)
}
let chartDataSet2 = LineChartDataSet(entries: dataEntries2, label: "humidity")
chartDataSet2.colors = [NSUIColor.black]
let chartData = LineChartData(dataSets: [chartDataSet1, chartDataSet2])
chartView.data = chartData
}
}
This is the result of my work.
enter image description here
Although the json data is successfully displayed, I don't know why it is loading for a long time, and I hope that the "time" in my json data can be displayed on the X axis above, marked with my temperature and humidity, and cannot be successful.
I also hope that my line chart view can be implemented as a layout.
"I don't know why it is loading for a long time". Do you mean that the graph does not load immediately upon opening the view? This is because the data is loading asynchronously from a remote source (correctly now, well done). It may well take a few seconds for your JSON to download over the web. That is ok. You can test the endpoint in a browser and see how long the response takes.
"I hope that the 'time' in my json data can be displayed on the X axis above". Yes. You can take the assignation of IndexAxisValueFormatter outside of the loop and you should pass all labels as values into the constructor. Try this code, replacing the equivalent loop:-
var labels: [String] = []
for i in 0..<th_5mins.count {
let y = th_5mins[i].XTP_A
let entry = ChartDataEntry.init(x: Double(i), y: Double(y))
dataEntries1.append(entry)
labels.append(th_5mins[i].Time)
}
chartView.xAxis.valueFormatter = IndexAxisValueFormatter(values: labels)
Note that the method you use for plotting against dates will result in an evenly spread graph, irrespective of your time gaps (e.g. if readings are 5 mins apart between the first two, but 5 years apart for the next two, they will still appear with even gaps between them.
Related
I have a bar chart to show item using Chart library. I got an error that the explain description show multiple colors for each item. Please help me how can i fix this, i want to show exactly the color like column (only one color), not multiple like this. Thanks for your helping.
My Screen
Here is my code, note that items is dynamic, i got from API:
func setDataCount(items: [Item]) {
let dataEntry = (0..<items.count).map { (i) -> ChartDataEntry in
let value = Double(items[i].soldQuantity)
return BarChartDataEntry(x: Double(i), y: value)
}
var arrayDataSet = [IChartDataSet]()
var arrayColor = [UIColor]()
var set = BarChartDataSet()
for i in 0...items.count - 1 {
set = BarChartDataSet(entries: dataEntry, label: items[i].name)
arrayColor.append(UIColor.random)
set.colors = arrayColor
// CONVERT DOUBLE -> INT
set.valueFormatter = DefaultValueFormatter(decimals: 0)
arrayDataSet.append(set)
}
let data = BarChartData(dataSets: arrayDataSet)
data.setValueFont(.systemFont(ofSize: 10, weight: .bold))
data.barWidth = 0.2
chartView.data = data
}
var dataEntries: [ChartDataEntry] = []
let financialData = data
for chartData in financialData{
let dataEntry = CandleChartDataEntry(x: chartData.date, shadowH: chartData.High, shadowL: chartData.Low, open: chartData.Open, close: chartData.Close)
dataEntries.append(dataEntry)
}
let chartDataSet = CandleChartDataSet(values: dataEntries, label: "")
chartDataSet.axisDependency = .left
chartDataSet.drawValuesEnabled = false
chartDataSet.increasingColor = UIColor.green
chartDataSet.increasingFilled = false
chartDataSet.decreasingColor = UIColor.red
chartDataSet.decreasingFilled = true
chartDataSet.barSpace = 0.25
chartDataSet.shadowColor = UIColor.darkGray
chartDataSet.neutralColor = UIColor.blue
let chartData = CandleChartData(dataSet: chartDataSet)
chartView.data = chartData
let xaxis = chartView.xAxis
xaxis.valueFormatter = axisFormatDelegate
Below is the sample data set that is being received from server and the image showing the chart being displayed.
https://api.cryptowat.ch/markets/gdax/btcusd/ohlc?periods=60
[ CloseTime, OpenPrice, HighPrice, LowPrice, ClosePrice, Volume ]
I need help in fixing the display of data as currently it doesn't resemble a candle stick chart in any way. Can someone please help me in what I am doing wrong here.
CandleStick chart data being displayed in the app
So just in case anyone is having the same problem, i was having issues with the candles too and i found a solution based on this post:
https://github.com/danielgindi/Charts/issues/2742
Create a class for your chartView.xAxis.valueFormatter that supports your entries with your init.
Once you have your results entries, i used the enumerated value for the x on the CandleChartDataEntry
In the stringForValue method inside your class that inherits from IAxisValueFormatter work the way you want to present your string, in my case was dates.
First two points:
//Check nil results
if result != nil {
//Save to Holder for future use
self.historyData = result
// Set the formatter class for the chartView
self.chartView.xAxis.valueFormatter = dateFormatter(elementsForFormatter: self.historyData!)
//Create a new chart data entry
var chartEntries = [ChartDataEntry]()
//Loop results
result?.enumerated().forEach({ (i, element) in
// I'm force unwrapping, but please take care of this don't do me...
let newDataEntryValue = CandleChartDataEntry(x:Double(i),
shadowH: element["high"] as! Double,
shadowL: element["low"] as! Double,
open: element["open"] as! Double,
close: element["close"] as! Double)
chartEntries.append(newDataEntryValue)
})
}
// Do rest of conf. for your chart....
Last point (class implementation):
class dateFormatter: IAxisValueFormatter {
var elements:Array<Dictionary<String,Any>>
init(elementsForFormatter:Array<Dictionary<String,Any>>) {
self.elements = elementsForFormatter
}
func stringForValue(_ value: Double, axis: AxisBase?) -> String {
let formatter = DateFormatter()
formatter.dateStyle = .short
//Get the date based on the value which is the index
if let time = self.elements[Int(value)]["time"] as? Double {
print(time)
let dateString = formatter.string(from: Date(timeIntervalSince1970: TimeInterval(time)))
return dateString
} else {
return ""
}
}
}
Hope this helps someone!
Problem Statement: Every time whenever I refresh viewController, then I get different values.
Required Solution: Whenever I refresh, it should fetch same values.
Coding Stuff:
Below I have get values from JSON parsing via url. After fetching values, it pass to the 'setChart() function as a parameter.'
override func viewDidLoad()
{
super.viewDidLoad()
var completedCalls = 0
for i in 0..<strDates.count {
Alamofire.request("http://api.fixer.io/\(strDates[i])?base=USD").responseJSON { response in
if let arr = response.result.value as? [String:AnyObject]
{
completedCalls += 1
let inrc = (arr["rates"]?["INR"] as? Double)!
print(inrc)
self.sValues.append(inc)
print(sValues)
//It prints values here.
if completedCalls = strDates.count {
DispatchQueue.main {
setChart(dataPoints: strDates, values: sValues)
}
}
}
}
}
setChart() Function
func setChart(dataPoints: [String], values: [Double]) {
barChartView.noDataText = "You need to provide data for the chart."
for i in 0..<dataPoints.count {
let dataEntry = BarChartDataEntry(x: Double(i), yValues: [values[i]])
dataEntries.append(dataEntry)
}
let chartDataSet = BarChartDataSet(values: dataEntries, label: "INR Rates(₹)/$")
let chartData = BarChartData(dataSet: chartDataSet)
barChartView.data = chartData
barChartView.leftAxis.axisMinimum = 65
barChartView.leftAxis.axisMaximum = 70
barChartView.xAxis.labelPosition = .bottom
barChartView.rightAxis.enabled = false
barChartView.data?.setDrawValues(false)
}
}
You're issuing a sequence of asynchronous requests, but you have no assurances the order in which they'll complete.
I would suggest capturing the results in a dictionary, with the strDates as the key. Then you can extract them in the correct order when presenting the results.
So, I'm assuming that assuming that sValues is defined as:
var sValues = [Double]()
Change that to:
var sValues = [String: Double]()
And then, rather than:
self.sValues.append(inc)
You would do:
self.sValues[strDates[i]] = inc
Then, in your charting code, iterate through strDates array, and look up the sValues using those date strings.
I have a bar chart, which I am trying to populate through data pulled in from an array.
I am trying to iterate through this array in order to populate my chart.
Please note: in this example below there are 3 winningStreakDM variables hardcoded for testing purposes, but in 'real life' the number could be different each API call as it depends on how many 'users' are in that league.
This is why I need to iterate through the array and structure the data to suit.
let winningStreakDM1 : [String : Any] = [
"id" : 2,
"user_name" : "Dicky",
"winning_streak" : 5
]
let winningStreakDM2 : [String : Any] = [
"id" : 6,
"user_name" : "G",
"winning_streak" : 2
]
let winningStreakDM3 : [String : Any] = [
"id" : 5,
"user_name" : "Sultan",
"winning_streak" : 0
]
My issue is that I don't know how to iterate through the initial array to structure my data for it to work with the above code.
This is my full script:
import UIKit
import Charts
class CommunityLeagueStatsVC: UIViewController {
// GRAPHS *********
#IBOutlet weak var chartView: BarChartView!
var values = [BarChartDataEntry]()
// ****************
//********CHART VARIABLES**************//
//WINS LOSSES DRAWS
var winStreak: Double = 0.0
#IBOutlet weak var leagueStatsScrollView: UIScrollView!
var noDefeats: Bool?
var noWins: Bool?
var biggestWin: String?
var biggestWinTeams: String?
var passedCommunityName: String?
#IBOutlet weak var communityName: UILabel!
var playerId2: String?
var communityId2: String?
var eMail2: String?
override func viewDidLoad() {
super.viewDidLoad()
let winningStreak = ["Wins"]
let gamesWon = [winStreak]
setWinStreakChart(dataPoints: winningStreak, values: gamesWon)
let defaults = UserDefaults.standard
let Email = defaults.string(forKey: "userEmail")
let playerId = defaults.string(forKey: "playerId")
let commsId = defaults.string(forKey: "communityId")
self.playerId2 = playerId
self.communityId2 = commsId
self.eMail2 = Email
}
func setWinStreakChart(dataPoints: [String], values: [BarChartDataEntry]){
xAxis.valueFormatter = WinningStreakFormatter(chartView: self.chartView)
let barChartDataSet = BarChartDataSet(values: values, label: "Winning Streak")
barChartDataSet.colors = ChartColorTemplates.material()
let barChartData = BarChartData(dataSet: barChartDataSet)
barChartData.setValueFont(UIFont.systemFont(ofSize: 12.0))
self.chartView.data = barChartData
}
override func viewDidAppear(_ animated: Bool) {
let myUrl = URL(string: "http://www.xxx.uk/xxx/getLeagueStats.php")
var request = URLRequest(url:myUrl!)
request.httpMethod = "POST"
let postString = "player_id=\(self.playerId2!)&community_id=\(communityId2!)";
request.httpBody = postString.data(using: String.Encoding.utf8);
let task = URLSession.shared.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in
DispatchQueue.main.async
{
do{
let json = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String:AnyObject]
print (json!)
if let dict = json?["leagueStats"] as? [String:AnyObject] {
//WINNING STREAK
var values = [BarChartDataEntry]()
if let dataWinStreak = dict["winningStreak"] as? [[String : Any]] {
print ("test one")
for (index,item) in dataWinStreak.enumerated() {
if let yValue = item["winning_streak"] as? Int, let userName = item["user_name"] as? String {
print ("test two")
let barChartDataEntry = BarChartDataEntry(x: Double(index), y: Double(yValue), data: userName as AnyObject?)
values.append(barChartDataEntry)
}
}
self.setWinStreakChart(dataPoints: ["wins"], values: values)
}
catch{
print(error)
}
}
}
task.resume()
}
}
UPDATE:
These are the errors that I am currently receiving:
With a bit of playing around and commenting out the line containing the first error I have got to this stage which correctly shows a graph with values, but no xAxis containing the userNames of each player.
You can create array of BarChartDataEntry this way.
var values = [BarChartDataEntry]()
if let dataWinStreak = dict["winningStreak"] as? [[String : Any]] {
for (index,item) in dataWinStreak.enumerated() {
if let yValue = item["winning_streak"] as? Int, let userName = item["user_name"] as? String {
let barChartDataEntry = BarChartDataEntry(x: index, y: yValue, data: userName)
values.append(barChartDataEntry)
}
}
}
//Now use values array
Edit: You just need to change setWinStreakChart function because now you are working with dynamic data not static one.
func setWinStreakChart(dataPoints: [String], values: [BarChartDataEntry]){
let xAxis : XAxis = self.chartView.xAxis;
xAxis.labelFont = UIFont(name: "HelveticaNeue-Light", size: 10.0)!
xAxis.labelTextColor = UIColor.black
xAxis.drawAxisLineEnabled = false
xAxis.drawGridLinesEnabled = true
xAxis.granularity = 1;
xAxis.labelPosition = .bottom
xAxis.valueFormatter = WinningStreakFormatter(chartView: self.chartView)
let barChartDataSet = BarChartDataSet(values: values, label: "Winning Streak")
barChartDataSet.colors = ChartColorTemplates.material()
let barChartData = BarChartData(dataSet: barChartDataSet)
barChartData.setValueFont(UIFont.systemFont(ofSize: 12.0))
self.chartView.data = barChartData
}
And now call this function after the for loop where we are creating array of BarChartDataEntry.
self.setWinStreakChart(dataPoints: ["wins"], values: values)
I am using swiftyjson to extract the json but i cant able to extract specification json that is label and value inside specification. I need it quickly.
((
{
code = "NPR 1515";
description = "With its enhanced power and performance the NPR1515 provides a genuine \"workhorse\" that will take almost anything in its stride.
\nThe full 1500 Watt motor unit packs almost 50% more power than normally required and this power is transmitted to the floor through our long established 150rpm, oil filled, low load, planetary gearbox .
\nThe big advantage of excess power is to provide scope for many additional tasks where the excess power is both advantageous and needed.";
id = 16;
name = "Scrubbers & Polisher - NPR 1515";
specification = "[{\"label\":\"Model No\",\"value\":\"PR 1515\\t\\t\\r\"},{\"label\":\"\\nMotor\",\"value\":\"500W\\t\\r\"},{\"label\":\"\\nPad\",\"value\":\"00mm\\t\\r\"},{\"label\":\"\\nPower\",\"value\":\"30V AC 50Hz\\r\"},{\"label\":\"\\nBrush\",\"value\":\"50mm\\t\\r\"},{\"label\":\"\\nSpeed\",\"value\":\"50 rpm\\r\"},{\"label\":\"\\nVacuum\",\"value\":\"T130\\t\\r\"},{\"label\":\"\\nRange\",\"value\":\"2m\\t\\t\\r\"},{\"label\":\"\\nWeight\",\"value\":\"0 Kgs\\t\\r\"},{\"label\":\"\\nSize\",\"value\":\"185 x 580x 450mm\"}]";
"video_url" = "<null>";
}
))
let json2 = JSON(data3!)
for (index, object) in json2 {
let name = object["name"].stringValue
let code = object["code"].stringValue
let description = object["description"].stringValue
let specification = object["specification"].stringValue
does not run this part.
let json3 = JSON(specification)
for (index, object3) in json3 {
println("in this loop")
if let specification2 = object3["label"].string {
println(specification2)
}
else {
println(object3["label"].error)
}
let specification3 = object3["value"].stringValue
println(specification3)
}
ok now it is working but label are not able to print in label they are working in println but if i do this value works but not label.
let str = self.labelArray[i]
let label8 = UILabel(frame: CGRectMake(2, 0, 0, 0))
label8.backgroundColor = UIColor.whiteColor()
label8.textColor = UIColor.blackColor().colorWithAlphaComponent(0.7)
label8.frame = CGRect(x: 10, y: setheight , width: screenWidth/2, height: 25)
label8.textAlignment = NSTextAlignment.Left
label8.text = str
self.scrollview_add.addSubview(label8)
var label7 = UILabel(frame: CGRectMake(2, 0, 0, 0))
label7.backgroundColor = UIColor.whiteColor()
label7.textColor = UIColor.blackColor().colorWithAlphaComponent(0.7)
label7.frame = CGRect(x: screenWidth/2, y: setheight , width: screenWidth/2, height: 25)
label7.textAlignment = NSTextAlignment.Right
label7.text = self.valueArray[i]
self.scrollview_add.addSubview(label7)
setheight += 25
As posted in your question value of specification is a string so first you have to convert it into NSData then convert it into json:
if let jsonData = specification.dataUsingEncoding(NSUTF8StringEncoding){
let json = JSON(data:jsonData)
for (index, object3) in json {
println("in this loop")
if let specification2 = object3["label"].string {
println(specification2)
}
else {
println(object3["label"].error)
}
let specification3 = object3["value"].stringValue
println(specification3)
}
}
According to the data structure you posted, the value inside "specification" is a string, not a JSON object. There're two ways to do it properly:
1, format the JSON data correctly, i.e, instead of a string, the "specification" should also be a JSON object (array with dictionaries), depending on where you get this JSON data, you need to modify the generation logic of this data to accomplish this.
2, you can manually parse the specification using JSON parser, for example:
let error: NSErrorPointer = nil;
let specificationString = "[{\"label\":\"Model No\",\"value\":\"PR 1515\\t\\t\\r\"},{\"label\":\"\\nMotor\",\"value\":\"500W\\t\\r\"},{\"label\":\"\\nPad\",\"value\":\"00mm\\t\\r\"},{\"label\":\"\\nPower\",\"value\":\"30V AC 50Hz\\r\"},{\"label\":\"\\nBrush\",\"value\":\"50mm\\t\\r\"},{\"label\":\"\\nSpeed\",\"value\":\"50 rpm\\r\"},{\"label\":\"\\nVacuum\",\"value\":\"T130\\t\\r\"},{\"label\":\"\\nRange\",\"value\":\"2m\\t\\t\\r\"},{\"label\":\"\\nWeight\",\"value\":\"0 Kgs\\t\\r\"},{\"label\":\"\\nSize\",\"value\":\"185 x 580x 450mm\"}]"
if let specificationData = specificationString.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true) {
let specificationObject: AnyObject? = NSJSONSerialization.JSONObjectWithData(specificationData, options: .MutableContainers, error: error)
//Now you have a correct Array, do whatever you want with it.
}