Skip to content

Commit 36df266

Browse files
committed
feat: add SwiftChart module
1 parent 0066b3e commit 36df266

File tree

14 files changed

+305
-46
lines changed

14 files changed

+305
-46
lines changed

ChartView.swift

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//
2+
// ChartView.swift
3+
// swift-charts
4+
//
5+
// Created by Oskar Kwaśniewski on 14/03/2023.
6+
//
7+
8+
import SwiftUI
9+
import Charts
10+
11+
struct ValuePerCategory {
12+
var category: String
13+
var value: Double
14+
}
15+
16+
struct ChartView: View {
17+
@EnvironmentObject var dataStore: DataStore
18+
19+
var body: some View {
20+
VStack {
21+
Chart(dataStore.data, id: \.category) { item in
22+
BarMark(
23+
x: .value("Category", item.category),
24+
y: .value("Value", item.value)
25+
)
26+
.foregroundStyle(dataStore.color.gradient)
27+
}
28+
}
29+
}
30+
}

Color+hex.swift

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
//
2+
// Color+hex.swift
3+
// swift-charts
4+
//
5+
// Created by Oskar Kwaśniewski on 15/03/2023.
6+
//
7+
8+
import SwiftUI
9+
import UIKit
10+
11+
extension Color {
12+
func toHex() -> String? {
13+
let uic = UIColor(self)
14+
guard let components = uic.cgColor.components, components.count >= 3 else {
15+
return nil
16+
}
17+
let r = Float(components[0])
18+
let g = Float(components[1])
19+
let b = Float(components[2])
20+
var a = Float(1.0)
21+
22+
if components.count >= 4 {
23+
a = Float(components[3])
24+
}
25+
26+
if a != Float(1.0) {
27+
return String(format: "%02lX%02lX%02lX%02lX", lroundf(r * 255), lroundf(g * 255), lroundf(b * 255), lroundf(a * 255))
28+
} else {
29+
return String(format: "%02lX%02lX%02lX", lroundf(r * 255), lroundf(g * 255), lroundf(b * 255))
30+
}
31+
}
32+
33+
init?(hex: String) {
34+
var hexSanitized = hex.trimmingCharacters(in: .whitespacesAndNewlines)
35+
hexSanitized = hexSanitized.replacingOccurrences(of: "#", with: "")
36+
37+
var rgb: UInt64 = 0
38+
39+
var r: CGFloat = 0.0
40+
var g: CGFloat = 0.0
41+
var b: CGFloat = 0.0
42+
var a: CGFloat = 1.0
43+
44+
let length = hexSanitized.count
45+
46+
guard Scanner(string: hexSanitized).scanHexInt64(&rgb) else { return nil }
47+
48+
if length == 6 {
49+
r = CGFloat((rgb & 0xFF0000) >> 16) / 255.0
50+
g = CGFloat((rgb & 0x00FF00) >> 8) / 255.0
51+
b = CGFloat(rgb & 0x0000FF) / 255.0
52+
53+
} else if length == 8 {
54+
r = CGFloat((rgb & 0xFF000000) >> 24) / 255.0
55+
g = CGFloat((rgb & 0x00FF0000) >> 16) / 255.0
56+
b = CGFloat((rgb & 0x0000FF00) >> 8) / 255.0
57+
a = CGFloat(rgb & 0x000000FF) / 255.0
58+
59+
} else {
60+
return nil
61+
}
62+
63+
self.init(red: r, green: g, blue: b, opacity: a)
64+
}
65+
}
66+

SwiftChartsView.swift

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
//
2+
// SwiftChartsView.swift
3+
// swift-charts
4+
//
5+
// Created by Oskar Kwaśniewski on 15/03/2023.
6+
//
7+
8+
import SwiftUI
9+
10+
class DataStore: ObservableObject {
11+
@Published var data: [ValuePerCategory] = []
12+
@Published var color: Color = .accentColor
13+
}
14+
15+
class SwiftChartsView: UIView {
16+
private var dataStore: DataStore = DataStore()
17+
18+
@objc var data: NSArray = [] {
19+
didSet {
20+
dataStore.data = processData(data)
21+
}
22+
}
23+
24+
@objc var color: String = "" {
25+
didSet {
26+
dataStore.color = Color(hex: color) ?? .accentColor
27+
}
28+
}
29+
30+
override init(frame: CGRect) {
31+
super.init(frame: frame)
32+
let hostingController = UIHostingController(rootView: ChartView().environmentObject(dataStore))
33+
addSubview(hostingController.view)
34+
}
35+
36+
required init?(coder: NSCoder) {
37+
fatalError("init(coder:) has not been implemented")
38+
}
39+
40+
override func layoutSubviews() {
41+
subviews.first?.frame = self.frame
42+
}
43+
44+
private func processData(_ data: NSArray) -> [ValuePerCategory] {
45+
let dictArray = data.compactMap { $0 as? NSDictionary }
46+
return dictArray.map { data in
47+
return ValuePerCategory(
48+
category: data["category"] as? String ?? "",
49+
value: data["value"] as? Double ?? 0
50+
)
51+
}
52+
}
53+
}

example/Gemfile.lock

+98
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
GEM
2+
remote: https://rubygems.org/
3+
specs:
4+
CFPropertyList (3.0.6)
5+
rexml
6+
activesupport (7.0.4.3)
7+
concurrent-ruby (~> 1.0, >= 1.0.2)
8+
i18n (>= 1.6, < 2)
9+
minitest (>= 5.1)
10+
tzinfo (~> 2.0)
11+
addressable (2.8.1)
12+
public_suffix (>= 2.0.2, < 6.0)
13+
algoliasearch (1.27.5)
14+
httpclient (~> 2.8, >= 2.8.3)
15+
json (>= 1.5.1)
16+
atomos (0.1.3)
17+
claide (1.1.0)
18+
cocoapods (1.12.0)
19+
addressable (~> 2.8)
20+
claide (>= 1.0.2, < 2.0)
21+
cocoapods-core (= 1.12.0)
22+
cocoapods-deintegrate (>= 1.0.3, < 2.0)
23+
cocoapods-downloader (>= 1.6.0, < 2.0)
24+
cocoapods-plugins (>= 1.0.0, < 2.0)
25+
cocoapods-search (>= 1.0.0, < 2.0)
26+
cocoapods-trunk (>= 1.6.0, < 2.0)
27+
cocoapods-try (>= 1.1.0, < 2.0)
28+
colored2 (~> 3.1)
29+
escape (~> 0.0.4)
30+
fourflusher (>= 2.3.0, < 3.0)
31+
gh_inspector (~> 1.0)
32+
molinillo (~> 0.8.0)
33+
nap (~> 1.0)
34+
ruby-macho (>= 2.3.0, < 3.0)
35+
xcodeproj (>= 1.21.0, < 2.0)
36+
cocoapods-core (1.12.0)
37+
activesupport (>= 5.0, < 8)
38+
addressable (~> 2.8)
39+
algoliasearch (~> 1.0)
40+
concurrent-ruby (~> 1.1)
41+
fuzzy_match (~> 2.0.4)
42+
nap (~> 1.0)
43+
netrc (~> 0.11)
44+
public_suffix (~> 4.0)
45+
typhoeus (~> 1.0)
46+
cocoapods-deintegrate (1.0.5)
47+
cocoapods-downloader (1.6.3)
48+
cocoapods-plugins (1.0.0)
49+
nap
50+
cocoapods-search (1.0.1)
51+
cocoapods-trunk (1.6.0)
52+
nap (>= 0.8, < 2.0)
53+
netrc (~> 0.11)
54+
cocoapods-try (1.2.0)
55+
colored2 (3.1.2)
56+
concurrent-ruby (1.2.2)
57+
escape (0.0.4)
58+
ethon (0.16.0)
59+
ffi (>= 1.15.0)
60+
ffi (1.15.5)
61+
fourflusher (2.3.1)
62+
fuzzy_match (2.0.4)
63+
gh_inspector (1.1.3)
64+
httpclient (2.8.3)
65+
i18n (1.12.0)
66+
concurrent-ruby (~> 1.0)
67+
json (2.6.3)
68+
minitest (5.18.0)
69+
molinillo (0.8.0)
70+
nanaimo (0.3.0)
71+
nap (1.1.0)
72+
netrc (0.11.0)
73+
public_suffix (4.0.7)
74+
rexml (3.2.5)
75+
ruby-macho (2.5.1)
76+
typhoeus (1.4.0)
77+
ethon (>= 0.9.0)
78+
tzinfo (2.0.6)
79+
concurrent-ruby (~> 1.0)
80+
xcodeproj (1.22.0)
81+
CFPropertyList (>= 2.3.3, < 4.0)
82+
atomos (~> 0.1.3)
83+
claide (>= 1.0.2, < 2.0)
84+
colored2 (~> 3.1)
85+
nanaimo (~> 0.3.0)
86+
rexml (~> 3.2.4)
87+
88+
PLATFORMS
89+
ruby
90+
91+
DEPENDENCIES
92+
cocoapods (~> 1.11, >= 1.11.3)
93+
94+
RUBY VERSION
95+
ruby 2.7.6p219
96+
97+
BUNDLED WITH
98+
2.1.4

example/ios/Podfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
require_relative '../node_modules/react-native/scripts/react_native_pods'
22
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
33

4-
platform :ios, min_ios_version_supported
4+
platform :ios, 16.0
55
prepare_react_native_project!
66

77
# If you are using a `react-native-flipper` your iOS build will fail when `NO_FLIPPER=1` is set.

example/ios/Podfile.lock

+3-3
Original file line numberDiff line numberDiff line change
@@ -620,10 +620,10 @@ SPEC CHECKSUMS:
620620
React-runtimeexecutor: 7bf0dafc7b727d93c8cb94eb00a9d3753c446c3e
621621
ReactCommon: 6f65ea5b7d84deb9e386f670dd11ce499ded7b40
622622
SocketRocket: fccef3f9c5cedea1353a9ef6ada904fde10d6608
623-
swift-charts: 6fb5041b918f27fba6c9ff00d98726f7f61114b4
623+
swift-charts: f8671b2606b84b48efca8353f7ae820d66ca51ca
624624
Yoga: 5ed1699acbba8863755998a4245daa200ff3817b
625625
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
626626

627-
PODFILE CHECKSUM: cc1956d4cff78cb74c88de6506f4e3a8cffbe0f9
627+
PODFILE CHECKSUM: cb078afa94dab6402005c40de584eb4a047198ca
628628

629-
COCOAPODS: 1.11.3
629+
COCOAPODS: 1.12.0

example/ios/SwiftChartsExample.xcodeproj/project.pbxproj

+6-4
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,7 @@
438438
"$(inherited)",
439439
);
440440
INFOPLIST_FILE = SwiftChartsExampleTests/Info.plist;
441-
IPHONEOS_DEPLOYMENT_TARGET = 12.4;
441+
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
442442
LD_RUNPATH_SEARCH_PATHS = (
443443
"$(inherited)",
444444
"@executable_path/Frameworks",
@@ -462,7 +462,7 @@
462462
BUNDLE_LOADER = "$(TEST_HOST)";
463463
COPY_PHASE_STRIP = NO;
464464
INFOPLIST_FILE = SwiftChartsExampleTests/Info.plist;
465-
IPHONEOS_DEPLOYMENT_TARGET = 12.4;
465+
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
466466
LD_RUNPATH_SEARCH_PATHS = (
467467
"$(inherited)",
468468
"@executable_path/Frameworks",
@@ -488,6 +488,7 @@
488488
CURRENT_PROJECT_VERSION = 1;
489489
ENABLE_BITCODE = NO;
490490
INFOPLIST_FILE = SwiftChartsExample/Info.plist;
491+
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
491492
LD_RUNPATH_SEARCH_PATHS = (
492493
"$(inherited)",
493494
"@executable_path/Frameworks",
@@ -514,6 +515,7 @@
514515
CLANG_ENABLE_MODULES = YES;
515516
CURRENT_PROJECT_VERSION = 1;
516517
INFOPLIST_FILE = SwiftChartsExample/Info.plist;
518+
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
517519
LD_RUNPATH_SEARCH_PATHS = (
518520
"$(inherited)",
519521
"@executable_path/Frameworks",
@@ -580,7 +582,7 @@
580582
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
581583
GCC_WARN_UNUSED_FUNCTION = YES;
582584
GCC_WARN_UNUSED_VARIABLE = YES;
583-
IPHONEOS_DEPLOYMENT_TARGET = 12.4;
585+
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
584586
LD_RUNPATH_SEARCH_PATHS = (
585587
/usr/lib/swift,
586588
"$(inherited)",
@@ -645,7 +647,7 @@
645647
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
646648
GCC_WARN_UNUSED_FUNCTION = YES;
647649
GCC_WARN_UNUSED_VARIABLE = YES;
648-
IPHONEOS_DEPLOYMENT_TARGET = 12.4;
650+
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
649651
LD_RUNPATH_SEARCH_PATHS = (
650652
/usr/lib/swift,
651653
"$(inherited)",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>IDEDidComputeMac32BitWarning</key>
6+
<true/>
7+
</dict>
8+
</plist>

example/src/App.tsx

+19-4
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,25 @@ import * as React from 'react';
33
import { StyleSheet, View } from 'react-native';
44
import { SwiftChartsView } from 'swift-charts';
55

6+
const chartData = [
7+
{
8+
category: 'Item 1',
9+
value: 12,
10+
},
11+
{
12+
category: 'Item 2',
13+
value: 25,
14+
},
15+
{
16+
category: 'Item 3',
17+
value: 6,
18+
},
19+
];
20+
621
export default function App() {
722
return (
823
<View style={styles.container}>
9-
<SwiftChartsView color="#32a852" style={styles.box} />
24+
<SwiftChartsView color="#e63946" data={chartData} style={styles.chart} />
1025
</View>
1126
);
1227
}
@@ -17,9 +32,9 @@ const styles = StyleSheet.create({
1732
alignItems: 'center',
1833
justifyContent: 'center',
1934
},
20-
box: {
21-
width: 60,
22-
height: 60,
35+
chart: {
36+
width: '100%',
37+
height: '100%',
2338
marginVertical: 20,
2439
},
2540
});

ios/SwiftCharts.xcodeproj/project.pbxproj

+2-2
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@
168168
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
169169
GCC_WARN_UNUSED_FUNCTION = YES;
170170
GCC_WARN_UNUSED_VARIABLE = YES;
171-
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
171+
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
172172
MTL_ENABLE_DEBUG_INFO = YES;
173173
ONLY_ACTIVE_ARCH = YES;
174174
SDKROOT = iphoneos;
@@ -212,7 +212,7 @@
212212
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
213213
GCC_WARN_UNUSED_FUNCTION = YES;
214214
GCC_WARN_UNUSED_VARIABLE = YES;
215-
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
215+
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
216216
MTL_ENABLE_DEBUG_INFO = NO;
217217
SDKROOT = iphoneos;
218218
VALIDATE_PRODUCT = YES;

0 commit comments

Comments
 (0)