Skip to content

Commit

Permalink
Merge pull request #289 from Binlogo/feat/bridge_echo_enhancement
Browse files Browse the repository at this point in the history
feat(examples): add sparkline and stop/start for bridge_echo on iOS
  • Loading branch information
charypar authored Dec 17, 2024
2 parents 7c4836a + c86db03 commit b2aa55f
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 13 deletions.
6 changes: 6 additions & 0 deletions examples/bridge_echo/iOS/BridgePerf.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
objects = {

/* Begin PBXBuildFile section */
072EE0FC2CFF2594008ABD8F /* SparklineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 072EE0FB2CFF2594008ABD8F /* SparklineView.swift */; };
0787064227803E37303F8850 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 973DE79248816287A24759FA /* ContentView.swift */; };
1D91DEB92273DE4FC7EE2D8F /* uniffi-bindgen in Resources */ = {isa = PBXBuildFile; fileRef = BBD3AE0E65859A8BDC264878 /* uniffi-bindgen */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
2391A638B2F2968ACA44FD2D /* core.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6815780EE0429EC63F67067 /* core.swift */; };
Expand All @@ -22,6 +23,8 @@
compilerSpec = com.apple.compilers.proxy.script;
filePatterns = "*.udl";
fileType = pattern.proxy;
inputFiles = (
);
isEditable = 1;
name = "Generate FFI";
outputFiles = (
Expand Down Expand Up @@ -74,6 +77,7 @@
/* End PBXContainerItemProxy section */

/* Begin PBXFileReference section */
072EE0FB2CFF2594008ABD8F /* SparklineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SparklineView.swift; sourceTree = "<group>"; };
5714ACFE617CB6EE596AD894 /* Shared */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Shared; path = ../shared/shared.xcodeproj; sourceTree = "<group>"; };
973DE79248816287A24759FA /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
9CC45FB589F9964637A15D5C /* shared.udl */ = {isa = PBXFileReference; lastKnownFileType = text; path = shared.udl; sourceTree = "<group>"; };
Expand Down Expand Up @@ -130,6 +134,7 @@
children = (
E902CC2F9431673D47029C6C /* BridgePerfApp.swift */,
973DE79248816287A24759FA /* ContentView.swift */,
072EE0FB2CFF2594008ABD8F /* SparklineView.swift */,
F6815780EE0429EC63F67067 /* core.swift */,
CC862345F0CDB156048B1417 /* Info.plist */,
);
Expand Down Expand Up @@ -261,6 +266,7 @@
buildActionMask = 2147483647;
files = (
C4CD26945C055EF9827876AE /* BridgePerfApp.swift in Sources */,
072EE0FC2CFF2594008ABD8F /* SparklineView.swift in Sources */,
0787064227803E37303F8850 /* ContentView.swift in Sources */,
2391A638B2F2968ACA44FD2D /* core.swift in Sources */,
5425067E3C89678E1AEF3F79 /* shared.udl in Sources */,
Expand Down
86 changes: 76 additions & 10 deletions examples/bridge_echo/iOS/BridgePerf/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,97 @@ import SwiftUI

struct ContentView: View {
@ObservedObject var core: Core

@State private var isRunning = true
@State private var tickTask: Task<Void, Never>? = nil
@State private var periodTask: Task<Void, Never>? = nil

init(core: Core) {
self.core = core
Task.init {
while(true) {
core.update(.tick)
}

private func startTasks() {
tickTask = Task {
while !Task.isCancelled {
if isRunning {
core.update(.tick)
}
await Task.yield()
}
}

Task.init {
while(true) {
try await Task.sleep(nanoseconds: UInt64(Double(NSEC_PER_SEC)))
core.update(.newPeriod)
periodTask = Task {
while !Task.isCancelled {
try? await Task.sleep(nanoseconds: UInt64(Double(NSEC_PER_SEC)))
if isRunning {
core.update(.newPeriod)
}
}
}
}

private func toggleRunning() {
isRunning.toggle()
if isRunning {
startTasks()
} else {
core.update(.reset)
tickTask?.cancel()
periodTask?.cancel()
tickTask = nil
periodTask = nil
}
}

private func calculateStatistics() -> (average: Double, min: UInt64, max: UInt64)? {
guard !core.view.log.isEmpty else { return nil }

let sum = core.view.log.reduce(0, +)
let average = Double(sum) / Double(core.view.log.count)
let min = core.view.log.min() ?? 0
let max = core.view.log.max() ?? 0

core.update(.tick)
return (average, min, max)
}

var body: some View {
VStack {
Text(String(core.view.count)).font(.largeTitle)
Text(String(core.view.count))
.font(.largeTitle)

SparklineView(data: core.view.log)
.frame(height: 200)
.padding()

if let stats = calculateStatistics() {
VStack(alignment: .leading) {
Text("Average: \(stats.average, specifier: "%.2f") /s")
Text("Min: \(stats.min) /s")
Text("Max: \(stats.max) /s")
}
.font(.caption)
.padding()
.background(Color.white.opacity(0.8))
.cornerRadius(8)
}

Button(action: toggleRunning) {
Text(isRunning ? "Stop" : "Start")
.padding()
.background(isRunning ? Color.red : Color.green)
.foregroundColor(.white)
.cornerRadius(8)
}
.padding()
}
.onAppear {
startTasks()
}
.onDisappear {
tickTask?.cancel()
periodTask?.cancel()
}
}

}

struct ContentView_Previews: PreviewProvider {
Expand Down
63 changes: 63 additions & 0 deletions examples/bridge_echo/iOS/BridgePerf/SparklineView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import SwiftUI

struct SparklineView: View {
var data: [UInt64]
@State private var currentIndex: Int? = nil

var body: some View {
GeometryReader { geometry in
let height = geometry.size.height
let width = geometry.size.width
let maxData = data.max() ?? 1
let scale = height / CGFloat(maxData)

let path = Path { path in
guard let firstPoint = data.first else { return }
path.move(to: CGPoint(x: 0, y: height - CGFloat(firstPoint) * scale))

for (index, value) in data.enumerated() {
let x = width * CGFloat(index) / CGFloat(data.count - 1)
let y = height - CGFloat(value) * scale
path.addLine(to: CGPoint(x: x, y: y))
}
}

path.stroke(Color.blue, lineWidth: 2)
.background(
Color.clear
.contentShape(Rectangle())
.gesture(
DragGesture(minimumDistance: 0)
.onChanged { value in
let index = Int((value.location.x / width) * CGFloat(data.count))
if index >= 0 && index < data.count {
currentIndex = index
}
}
)
)
.overlay(
Group {
if let index = currentIndex, index < data.count {
let x = width * CGFloat(index) / CGFloat(data.count - 1)
let y = height - CGFloat(data[index]) * scale
Text("\(data[index])")
.font(.caption)
.padding(5)
.background(Color.white.opacity(0.8))
.cornerRadius(5)
.position(x: x, y: y - 20)
}
}
)
}
}
}

struct SparklineView_Previews: PreviewProvider {
static var previews: some View {
SparklineView(data: [10, 20, 15, 30, 25, 40, 35])
.frame(height: 200)
.padding()
}
}
21 changes: 18 additions & 3 deletions examples/bridge_echo/shared/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use serde::{Deserialize, Serialize};
pub enum Event {
Tick,
NewPeriod,
Reset,
}

#[derive(Default, Debug, PartialEq)]
Expand All @@ -17,6 +18,7 @@ pub struct Model {
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
pub struct ViewModel {
pub count: usize,
pub log: Vec<usize>,
}

#[cfg_attr(feature = "typegen", derive(crux_core::macros::Export))]
Expand All @@ -42,13 +44,20 @@ impl crux_core::App for App {
model.log.push(model.count);
model.count = 0;
}
Event::Reset => {
model.count = 0;
model.log.clear();
}
};

caps.render.render();
}

fn view(&self, model: &Self::Model) -> Self::ViewModel {
ViewModel { count: model.count }
ViewModel {
count: model.count,
log: model.log.clone(),
}
}
}
// ANCHOR_END: impl_app
Expand All @@ -66,7 +75,10 @@ mod test {
let model = Model::default();

let actual_view = app.view(&model);
let expected_view = ViewModel { count: 0 };
let expected_view = ViewModel {
count: 0,
log: vec![],
};

assert_eq!(actual_view, expected_view);
}
Expand All @@ -81,7 +93,10 @@ mod test {
let _ = app.update(Event::Tick, &mut model);

let actual_view = app.view(&model);
let expected_view = ViewModel { count: 3 };
let expected_view = ViewModel {
count: 3,
log: vec![],
};

assert_eq!(actual_view, expected_view);
}
Expand Down

0 comments on commit b2aa55f

Please sign in to comment.