こんにちはsuzukiです。本日はSwiftUIで、Chartライブラリを使う方法についてまとめました。
Swift UIでChartsを利用してチャートを書きます。
以前の記事で何度か触れている、無料でチャート描画が可能なライブラリです。
サンプルはiOSはGitHub上のdemoから確認できます。
今回はCocoaPodsを利用しインストールしました。pod 'Charts'
上記をPodfileに記述し、pod installを行います。
SwiftUIでChartライブラリを利用する手順はこちらでも説明がございます。
SwiftUIでチャートの描画を行うためにラッパークラスを作成します。※structなので、クラスという表現は間違っているかもしれませんが、ラッパーストラクトとかラッパー構造体に違和感があり、ラッパークラスと表現してます。
今回はCandleStickChartViewというローソク足のラッパークラスを作成します。
下記手順で描画するための準備を行います。
1、クラスファイルの追加
今回はCandleという名前でファイルの追加
2、ライブラリのインポート分の描画
import Charts import SwiftUI
3、Candleという構造体を定義
struct Candle: UIViewRepresentable { //描画するViewのエイリアスを設定 typealias UIViewType = CandleStickChartView //UIViewRepresentableプロトコル Chartsフレームワークを返却(エイリアスで記述したクラス) func makeUIView(context: UIViewRepresentableContext<Candle>) -> CandleStickChartView { } //UIViewRepresentableプロトコル チャートの入力データが変更された場合にビューを更新するコードを記述 func updateUIView(_ uiView: CandleStickChartView, context: UIViewRepresentableContext<Candle>) { } }
4、makeUIViewでCandleStickChartViewを返却
func makeUIView(context: UIViewRepresentableContext<Candle>) -> CandleStickChartView { let chart = CandleStickChartView() return chart }
ここまででSwiftUIで画面上に配置する準備ができました。試しにCandle()を呼び出すと、No chart data availableと表示されます。
データの取得と更新について、詳細は以前のSwift ChartsライブラリとCryptowatchAPIでローソク足を描画してみる。の記事で行ったことを元に移植しています。
1、通信のレスポンス用の構造体を定義
//一月ごとのデータ取得 struct MonthOHLCListResult: Codable{ let result: OHLCList } struct OHLCList: Codable{ let list: [[Double]] private enum CodingKeys: String, CodingKey { case list = "86400"//ここで取得データの範囲を決定 } }
2、CryptowatchAPIでからOHLCデータを取得
func fetchOHLCData(completionHandler: @escaping (OHLCList) -> Void ){ guard let url = URL(string: "https://api.cryptowat.ch/markets/coinbase-pro/btcusd/ohlc?periods=86400") else { return } URLSession(configuration: .default).dataTask(with: url) { (data, response, error) in guard let data = data else{ return } //JSONDecoder let decoder = JSONDecoder() //JSONDecoderでレスポンス→MonthOHLCListResultクラスとして変換 if let response = try? decoder.decode(MonthOHLCListResult.self, from: data){ print(response.result) DispatchQueue.main.async{ completionHandler(response.result) } } }.resume() }
3、OHLCデータからローソク足用のデータを作成
チャートの描画用の範囲などは要調整です。
func generateCandleData(_ ohlcList: OHLCList) -> CandleChartData{ let candleChartDataEntries:[CandleChartDataEntry] = ohlcList.list.enumerated().compactMap{ return CandleChartDataEntry(x: Double($0.offset), shadowH: $0.element[2], shadowL: $0.element[2], open: $0.element[1], close: $0.element[4]) } let set = CandleChartDataSet(entries: candleChartDataEntries, label: "Candle DataSet") set.setColor(.lightText) set.decreasingColor = .blue set.decreasingFilled = true set.increasingColor = .red set.increasingFilled = true set.shadowColor = .darkGray set.valueFont = .systemFont(ofSize: 10) set.drawValuesEnabled = false return CandleChartData(dataSet: set) }
4、updateUIViewで上記を使用
//UIViewRepresentableプロトコル チャートの入力データが変更された場合にビューを更新するコードを記述 func updateUIView(_ uiView: CandleStickChartView, context: UIViewRepresentableContext<Candle>) { fetchOHLCData { ohlc in uiView.data = self.generateCandleData(ohlc) } }
5、Candle()を使用してチャートを表示
struct ContentView: View { var body: some View { VStack{ Candle() } } }
表示領域が調整が微妙ですが、拡大すると下記のスクショのようにローソク足が表示されています。
//インポート文の追加 import Charts import SwiftUI //wrapper structの追加 struct Candle: UIViewRepresentable { //描画するViewのエイリアスを設定 typealias UIViewType = CandleStickChartView //UIViewRepresentableプロトコル Chartsフレームワークを返却(エイリアスで記述したクラス) func makeUIView(context: UIViewRepresentableContext<Candle>) -> CandleStickChartView { let chart = CandleStickChartView() return chart } //UIViewRepresentableプロトコル チャートの入力データが変更された場合にビューを更新するコードを記述 func updateUIView(_ uiView: CandleStickChartView, context: UIViewRepresentableContext<Candle>) { fetchOHLCData { ohlc in uiView.data = self.generateCandleData(ohlc) } } func generateCandleData(_ ohlcList: OHLCList) -> CandleChartData{ let candleChartDataEntries:[CandleChartDataEntry] = ohlcList.list.enumerated().compactMap{ return CandleChartDataEntry(x: Double($0.offset), shadowH: $0.element[2], shadowL: $0.element[2], open: $0.element[1], close: $0.element[4]) } let set = CandleChartDataSet(entries: candleChartDataEntries, label: "Candle DataSet") set.setColor(.lightText) set.decreasingColor = .blue set.decreasingFilled = true set.increasingColor = .red set.increasingFilled = true set.shadowColor = .darkGray set.valueFont = .systemFont(ofSize: 10) set.drawValuesEnabled = false return CandleChartData(dataSet: set) } func fetchOHLCData(completionHandler: @escaping (OHLCList) -> Void ){ guard let url = URL(string: "https://api.cryptowat.ch/markets/coinbase-pro/btcusd/ohlc?periods=86400") else { return } URLSession(configuration: .default).dataTask(with: url) { (data, response, error) in guard let data = data else{ return } //JSONDecoder let decoder = JSONDecoder() //JSONDecoderでレスポンス→MonthOHLCListResultクラスとして変換 if let response = try? decoder.decode(MonthOHLCListResult.self, from: data){ print(response.result) DispatchQueue.main.async{ completionHandler(response.result) } } }.resume() } } //一月ごとのデータ取得 struct MonthOHLCListResult: Codable{ let result: OHLCList } struct OHLCList: Codable{ //本来であれば別クラスを定義すべきかもしれませんが、処理的にもあまり多くしたくないため[[Double]]としてそのまま利用 let list: [[Double]] private enum CodingKeys: String, CodingKey { case list = "86400" } } //ContentView struct ContentView: View { var body: some View { VStack{ Candle() } } }
最後までありがとうございます。想像していたよりは簡単に、ライブラリのViewを元にSwiftUIが利用できました。
実際に使うとなると更新タイミングどうしようとかまだ考えることはありますが、今回の記事は以上となります。XCodeのBetaがもう少しうまく動いたら、またwidget周りを触っていこうと思います。