Skip to content

Instantly share code, notes, and snippets.

@ppeelen
Created February 13, 2023 22:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ppeelen/95366a0f6a229ed104aff1738b9a3f41 to your computer and use it in GitHub Desktop.
Save ppeelen/95366a0f6a229ed104aff1738b9a3f41 to your computer and use it in GitHub Desktop.
The end result of the tutorial for multi data type in Swift Chart
import SwiftUI
import PlaygroundSupport
import Charts
// MARK: Chart implementation
struct ChartView: View {
@Binding var consumption: [Consumption]
var body: some View {
VStack(alignment: .leading) {
let strideBy: Double = 6
let costs = consumption.map { $0.cost }
let costMin = costs.min()!
let costMax = costs.max()!
let consumptions = consumption.map { $0.consumption }
let consumptionMin = consumptions.min()!
let consumptionMax = consumptions.max()!
Text("Cost")
.font(.footnote)
Chart(consumption) { item in
LineMark(
x: .value("Hour", item.from),
y: .value("Price", item.cost / costMax)
)
.interpolationMethod(.catmullRom)
.foregroundStyle(.green)
.lineStyle(StrokeStyle(lineWidth: 3))
.foregroundStyle(by: .value("Value", "Cost"))
LineMark(
x: .value("Hour", item.from),
y: .value("Consumption", item.consumption / consumptionMax)
)
.interpolationMethod(.catmullRom)
.foregroundStyle(.blue)
.lineStyle(StrokeStyle(lineWidth: 3))
.foregroundStyle(by: .value("Value", "Consumption"))
}
.chartForegroundStyleScale([
"Consumption": .blue,
"Cost": .green,
])
.chartXAxis {
AxisMarks(values: .stride(by: .hour, count: 4)) { _ in
AxisValueLabel(format: .dateTime.hour(.twoDigits(amPM: .abbreviated)))
}
}
.chartYAxis {
let defaultStride = Array(stride(from: 0, to: 1, by: 1.0/strideBy))
let costsStride = Array(stride(from: costMin,
through: costMax,
by: (costMax - costMin)/strideBy))
AxisMarks(position: .trailing, values: defaultStride) { axis in
AxisGridLine()
let value = costsStride[axis.index]
AxisValueLabel("\(String(format: "%.2F", value)) kr", centered: false)
}
let consumptionStride = Array(stride(from: consumptionMin,
through: consumptionMax,
by: (consumptionMax - consumptionMin)/strideBy))
AxisMarks(position: .leading, values: defaultStride) { axis in
AxisGridLine()
let value = consumptionStride[axis.index]
AxisValueLabel("\(String(format: "%.2F", value)) kWh", centered: false)
}
}
.padding(.bottom, 20)
}
}
}
struct MainView: View {
@State private var consumption: [Consumption]
init(consumption: [Consumption]) {
self.consumption = consumption
}
var body: some View {
VStack {
ChartView(consumption: $consumption)
.frame(minWidth: 450, minHeight: 250)
.padding()
}
.environment(\.colorScheme, .dark)
.background(.black)
}
}
// MARK: - Setting up chart data
let data = """
[{"id":"1","from":"2023-02-12T00:00:00.000+01:00","to":"2023-02-12T01:00:00.000+01:00","cost":2.768758875,"consumption":2.817,"consumptionUnit":"kWh","currency":"SEK"},{"id":"2","from":"2023-02-12T01:00:00.000+01:00","to":"2023-02-12T02:00:00.000+01:00","cost":49.00516435,"consumption":10.794,"consumptionUnit":"kWh","currency":"SEK"},{"id":"3","from":"2023-02-12T02:00:00.000+01:00","to":"2023-02-12T03:00:00.000+01:00","cost":47.8345572125,"consumption":9.967,"consumptionUnit":"kWh","currency":"SEK"},{"id":"4","from":"2023-02-12T03:00:00.000+01:00","to":"2023-02-12T04:00:00.000+01:00","cost":48.62424185,"consumption":10.026,"consumptionUnit":"kWh","currency":"SEK"},{"id":"5","from":"2023-02-12T04:00:00.000+01:00","to":"2023-02-12T05:00:00.000+01:00","cost":46.695987275,"consumption":10.619,"consumptionUnit":"kWh","currency":"SEK"},{"id":"6","from":"2023-02-12T05:00:00.000+01:00","to":"2023-02-12T06:00:00.000+01:00","cost":50.915838875,"consumption":11.201,"consumptionUnit":"kWh","currency":"SEK"},{"id":"7","from":"2023-02-12T06:00:00.000+01:00","to":"2023-02-12T07:00:00.000+01:00","cost":52.9999920625,"consumption":11.317,"consumptionUnit":"kWh","currency":"SEK"},{"id":"8","from":"2023-02-12T07:00:00.000+01:00","to":"2023-02-12T08:00:00.000+01:00","cost":42.7862852,"consumption":10.698,"consumptionUnit":"kWh","currency":"SEK"},{"id":"9","from":"2023-02-12T08:00:00.000+01:00","to":"2023-02-12T09:00:00.000+01:00","cost":37.242389225,"consumption":9.377,"consumptionUnit":"kWh","currency":"SEK"},{"id":"10","from":"2023-02-12T09:00:00.000+01:00","to":"2023-02-12T10:00:00.000+01:00","cost":32.778883225,"consumption":8.171,"consumptionUnit":"kWh","currency":"SEK"},{"id":"11","from":"2023-02-12T10:00:00.000+01:00","to":"2023-02-12T11:00:00.000+01:00","cost":21.0670651,"consumption":4.436,"consumptionUnit":"kWh","currency":"SEK"},{"id":"12","from":"2023-02-12T11:00:00.000+01:00","to":"2023-02-12T12:00:00.000+01:00","cost":0.98445655,"consumption":2.156,"consumptionUnit":"kWh","currency":"SEK"},{"id":"13","from":"2023-02-12T12:00:00.000+01:00","to":"2023-02-12T13:00:00.000+01:00","cost":0.68222785,"consumption":1.523,"consumptionUnit":"kWh","currency":"SEK"},{"id":"14","from":"2023-02-12T13:00:00.000+01:00","to":"2023-02-12T14:00:00.000+01:00","cost":0.5496218,"consumption":1.288,"consumptionUnit":"kWh","currency":"SEK"},{"id":"15","from":"2023-02-12T14:00:00.000+01:00","to":"2023-02-12T15:00:00.000+01:00","cost":0.5298075,"consumption":1.269,"consumptionUnit":"kWh","currency":"SEK"},{"id":"16","from":"2023-02-12T15:00:00.000+01:00","to":"2023-02-12T16:00:00.000+01:00","cost":0.677347125,"consumption":1.61,"consumptionUnit":"kWh","currency":"SEK"},{"id":"17","from":"2023-02-12T16:00:00.000+01:00","to":"2023-02-12T17:00:00.000+01:00","cost":1.1193823,"consumption":2.488,"consumptionUnit":"kWh","currency":"SEK"},{"id":"18","from":"2023-02-12T17:00:00.000+01:00","to":"2023-02-12T18:00:00.000+01:00","cost":1.155690375,"consumption":2.445,"consumptionUnit":"kWh","currency":"SEK"},{"id":"19","from":"2023-02-12T18:00:00.000+01:00","to":"2023-02-12T19:00:00.000+01:00","cost":1.15172305,"consumption":2.468,"consumptionUnit":"kWh","currency":"SEK"},{"id":"20","from":"2023-02-12T19:00:00.000+01:00","to":"2023-02-12T20:00:00.000+01:00","cost":0.81017805,"consumption":1.803,"consumptionUnit":"kWh","currency":"SEK"},{"id":"21","from":"2023-02-12T20:00:00.000+01:00","to":"2023-02-12T21:00:00.000+01:00","cost":0.7772117375,"consumption":1.837,"consumptionUnit":"kWh","currency":"SEK"},{"id":"22","from":"2023-02-12T21:00:00.000+01:00","to":"2023-02-12T22:00:00.000+01:00","cost":0.6472297625,"consumption":1.567,"consumptionUnit":"kWh","currency":"SEK"},{"id":"23","from":"2023-02-12T22:00:00.000+01:00","to":"2023-02-12T23:00:00.000+01:00","cost":0.5633025,"consumption":1.416,"consumptionUnit":"kWh","currency":"SEK"},{"id":"24","from":"2023-02-12T23:00:00.000+01:00","to":"2023-02-13T00:00:00.000+01:00","cost":0.4434144,"consumption":1.292,"consumptionUnit":"kWh","currency":"SEK"}]
""".data(using: .utf8)!
let decoder = JSONDecoder()
decoder.dateDecodingStrategyFormatters = [ DateFormatter.json ]
let consumption = try decoder.decode([Consumption].self, from: data)
// MARK: - View
let view = MainView(consumption: consumption)
PlaygroundPage.current.setLiveView(view)
// MARK: - Helpers
struct Consumption: Codable, Identifiable {
let id: String
let from: Date
let to: Date
let cost: Double
let consumption: Double
let consumptionUnit: String
let currency: String
}
extension DateFormatter {
static let json: DateFormatter = {
var dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS+01:00"
return dateFormatter
}()
}
extension JSONDecoder {
var dateDecodingStrategyFormatters: [DateFormatter]? {
@available(*, unavailable, message: "This variable is meant to be set only")
get { return nil }
set {
guard let formatters = newValue else { return }
self.dateDecodingStrategy = .custom { decoder in
let container = try decoder.singleValueContainer()
let dateString = try container.decode(String.self)
for formatter in formatters {
if let date = formatter.date(from: dateString) {
return date
}
}
throw DecodingError.dataCorruptedError(in: container, debugDescription: "Cannot decode date string \(dateString)")
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment