Skip to content

Commit 1d2d6d2

Browse files
committed
Display SwiftUI view change counts
1 parent 584c8fa commit 1d2d6d2

File tree

3 files changed

+129
-34
lines changed

3 files changed

+129
-34
lines changed

sample/iosApp/KMPObservableViewModelSample.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
1DCF89AB2933929400A4C54A /* KMPObservableViewModelSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 1DCF89AA2933929400A4C54A /* KMPObservableViewModelSwiftUI */; };
1818
1DDAF2202935470A0049C114 /* KMPObservableViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DDAF21F2935470A0049C114 /* KMPObservableViewModel.swift */; };
1919
1DDAF222293548A60049C114 /* TimeTravelViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DDAF221293548A60049C114 /* TimeTravelViewModel.swift */; };
20+
1DDE18852E114DE800227AE6 /* ChangeCounter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DDE18842E114DE500227AE6 /* ChangeCounter.swift */; };
2021
1DF227B02C2856BC00D8B3A7 /* ContentViewMP.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DF227AF2C2856BC00D8B3A7 /* ContentViewMP.swift */; };
2122
/* End PBXBuildFile section */
2223

@@ -52,6 +53,7 @@
5253
1DCF89A82933928600A4C54A /* KMP-ObservableViewModel */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = "KMP-ObservableViewModel"; path = ../..; sourceTree = "<group>"; };
5354
1DDAF21F2935470A0049C114 /* KMPObservableViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KMPObservableViewModel.swift; sourceTree = "<group>"; };
5455
1DDAF221293548A60049C114 /* TimeTravelViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeTravelViewModel.swift; sourceTree = "<group>"; };
56+
1DDE18842E114DE500227AE6 /* ChangeCounter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChangeCounter.swift; sourceTree = "<group>"; };
5557
1DF227AF2C2856BC00D8B3A7 /* ContentViewMP.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentViewMP.swift; sourceTree = "<group>"; };
5658
/* End PBXFileReference section */
5759

@@ -106,6 +108,7 @@
106108
1D44EF76292C066B00465C43 /* KMPObservableViewModelSample */ = {
107109
isa = PBXGroup;
108110
children = (
111+
1DDE18842E114DE500227AE6 /* ChangeCounter.swift */,
109112
1D44EF77292C066B00465C43 /* KMPObservableViewModelSampleApp.swift */,
110113
1D44EF79292C066B00465C43 /* ContentView.swift */,
111114
1D44EF7B292C066C00465C43 /* Assets.xcassets */,
@@ -316,6 +319,7 @@
316319
files = (
317320
1DDAF2202935470A0049C114 /* KMPObservableViewModel.swift in Sources */,
318321
1DDAF222293548A60049C114 /* TimeTravelViewModel.swift in Sources */,
322+
1DDE18852E114DE800227AE6 /* ChangeCounter.swift in Sources */,
319323
1D44EF7A292C066B00465C43 /* ContentView.swift in Sources */,
320324
1DF227B02C2856BC00D8B3A7 /* ContentViewMP.swift in Sources */,
321325
1D44EF78292C066B00465C43 /* KMPObservableViewModelSampleApp.swift in Sources */,
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
//
2+
// ChangeCounter.swift
3+
// KMPObservableViewModelSample
4+
//
5+
// Created by Rick Clephas on 29/06/2025.
6+
//
7+
8+
import SwiftUI
9+
10+
@MainActor
11+
class Counter: ObservableObject {
12+
13+
private var count = 0
14+
15+
func incrementAndGet() -> Int {
16+
count += 1
17+
return count
18+
}
19+
}
20+
21+
struct ChangeCounter<Content: View>: View {
22+
23+
var count: Int
24+
var content: Content
25+
26+
init(_ count: Int, @ViewBuilder _ content: () -> Content) {
27+
self.count = count
28+
self.content = content()
29+
}
30+
31+
var body: some View {
32+
ZStack(alignment: .bottom) {
33+
content.padding(.bottom, 32)
34+
Text("Changes: \(count)")
35+
.foregroundStyle(.foreground.opacity(0.6))
36+
.dynamicTypeSize(.small)
37+
.padding(.horizontal, 16)
38+
.background(.background)
39+
}
40+
.frame(minWidth: 0, maxWidth: .infinity)
41+
.padding(.vertical, 16)
42+
.padding(.horizontal, 8)
43+
.background { RoundedRectangle(cornerRadius: 16).stroke(.foreground.opacity(0.3), lineWidth: 2).padding(.bottom, 24) }
44+
.padding(.horizontal, 8)
45+
}
46+
}

sample/iosApp/KMPObservableViewModelSample/ContentView.swift

Lines changed: 79 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -11,57 +11,102 @@ import KMPObservableViewModelSwiftUI
1111
struct ContentView: View {
1212

1313
@StateViewModel var viewModel = TimeTravelViewModel()
14-
15-
private var isFixedTimeBinding: Binding<Bool> {
16-
Binding { viewModel.isFixedTime } set: { isFixedTime in
17-
if isFixedTime {
18-
viewModel.stopTime()
19-
} else {
20-
viewModel.startTime()
21-
}
22-
}
14+
@StateObject var counter = Counter()
15+
16+
var body: some View {
17+
Spacer()
18+
ChangeCounter(counter.incrementAndGet()) {
19+
VStack {
20+
ActualTimeView(viewModel: viewModel)
21+
Spacer().frame(height: 24)
22+
TravelEffectView(viewModel: viewModel)
23+
Spacer().frame(height: 24)
24+
CurrentTimeView(viewModel: viewModel)
25+
Spacer().frame(height: 24)
26+
IsFixedTimeView(viewModel: viewModel)
27+
Spacer().frame(height: 24)
28+
Button("Time travel") {
29+
viewModel.timeTravel()
30+
}
31+
Spacer().frame(height: 24)
32+
Button("Reset") {
33+
viewModel.resetTime()
34+
}.foregroundColor(viewModel.isResetDisabled ? Color.red : Color.green)
35+
}.frame(minWidth: 0, maxWidth: .infinity)
36+
}.padding(.horizontal, 8)
37+
Spacer()
2338
}
39+
}
2440

41+
struct ActualTimeView: View {
42+
43+
@ObservedViewModel var viewModel: TimeTravelViewModel
44+
@StateObject var counter = Counter()
45+
2546
var body: some View {
26-
VStack{
27-
Spacer()
28-
Group {
47+
ChangeCounter(counter.incrementAndGet()) {
48+
VStack {
2949
Text("Actual time:")
3050
Text(viewModel.actualTime)
3151
.font(.system(size: 20))
3252
}
33-
Group {
34-
Spacer().frame(height: 24)
53+
}
54+
}
55+
}
56+
57+
struct TravelEffectView: View {
58+
59+
@ObservedViewModel var viewModel: TimeTravelViewModel
60+
@StateObject var counter = Counter()
61+
62+
var body: some View {
63+
ChangeCounter(counter.incrementAndGet()) {
64+
VStack {
3565
Text("Travel effect:")
3666
Text(viewModel.travelEffect?.description ?? "nil")
3767
.font(.system(size: 20))
3868
}
39-
Group {
40-
Spacer().frame(height: 24)
69+
}
70+
}
71+
}
72+
73+
struct CurrentTimeView: View {
74+
75+
@ObservedViewModel var viewModel: TimeTravelViewModel
76+
@StateObject var counter = Counter()
77+
78+
var body: some View {
79+
ChangeCounter(counter.incrementAndGet()) {
80+
VStack {
4181
Text("Current time:")
4282
Text(viewModel.currentTime)
4383
.font(.system(size: 20))
4484
}
45-
Group {
46-
Spacer().frame(height: 24)
47-
HStack {
48-
Toggle("", isOn: isFixedTimeBinding).labelsHidden()
49-
Text("Fixed time")
50-
}
51-
}
52-
Group {
53-
Spacer().frame(height: 24)
54-
Button("Time travel") {
55-
viewModel.timeTravel()
56-
}
85+
}
86+
}
87+
}
88+
89+
struct IsFixedTimeView: View {
90+
91+
@ObservedViewModel var viewModel: TimeTravelViewModel
92+
@StateObject var counter = Counter()
93+
94+
private var isFixedTimeBinding: Binding<Bool> {
95+
Binding { viewModel.isFixedTime } set: { isFixedTime in
96+
if isFixedTime {
97+
viewModel.stopTime()
98+
} else {
99+
viewModel.startTime()
57100
}
58-
Group {
59-
Spacer().frame(height: 24)
60-
Button("Reset") {
61-
viewModel.resetTime()
62-
}.foregroundColor(viewModel.isResetDisabled ? Color.red : Color.green)
101+
}
102+
}
103+
104+
var body: some View {
105+
ChangeCounter(counter.incrementAndGet()) {
106+
HStack {
107+
Toggle("", isOn: isFixedTimeBinding).labelsHidden()
108+
Text("Fixed time")
63109
}
64-
Spacer()
65110
}
66111
}
67112
}

0 commit comments

Comments
 (0)