Skip to content

Commit bbda8c2

Browse files
committed
Add observability event log to demo
1 parent 3807f75 commit bbda8c2

4 files changed

Lines changed: 119 additions & 2 deletions

File tree

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import Foundation
2+
import LiveLocalizationCore
3+
4+
actor DemoLocalizationEventStore {
5+
private let entryLimit = 50
6+
private var entries: [String] = []
7+
private var continuations: [UUID: AsyncStream<[String]>.Continuation] = [:]
8+
9+
func record(_ event: LocalizationEvent) {
10+
entries.insert(Self.description(for: event), at: 0)
11+
if entries.count > entryLimit {
12+
entries.removeLast(entries.count - entryLimit)
13+
}
14+
15+
let snapshot = entries
16+
for continuation in continuations.values {
17+
continuation.yield(snapshot)
18+
}
19+
}
20+
21+
func snapshots() -> AsyncStream<[String]> {
22+
AsyncStream { continuation in
23+
let id = UUID()
24+
continuations[id] = continuation
25+
continuation.yield(entries)
26+
27+
continuation.onTermination = { [weak self] _ in
28+
Task {
29+
await self?.removeContinuation(withID: id)
30+
}
31+
}
32+
}
33+
}
34+
35+
private func removeContinuation(withID id: UUID) {
36+
continuations[id] = nil
37+
}
38+
39+
private static func description(for event: LocalizationEvent) -> String {
40+
switch event {
41+
case .sharedConfigurationStarted:
42+
return "Shared configuration started"
43+
case .sharedConfigurationFinished:
44+
return "Shared configuration finished"
45+
case .cacheWarmupStarted:
46+
return "Cache warmup started"
47+
case .cacheWarmupFinished:
48+
return "Cache warmup finished"
49+
case .cacheHit(let key):
50+
return "Cache hit: \(key)"
51+
case .cacheMiss(let key):
52+
return "Cache miss: \(key)"
53+
case .cacheStoreWrite(let key):
54+
return "Cache write: \(key)"
55+
case .cacheInvalidated(let key):
56+
return "Cache invalidated: \(key)"
57+
case .cacheCleared:
58+
return "Cache cleared"
59+
case .providerTranslationStarted(let request):
60+
return "Provider started: \(request.sourceText)"
61+
case .providerTranslationSucceeded(let request, let localizedText):
62+
return "Provider succeeded: \(request.sourceText) -> \(localizedText)"
63+
case .providerTranslationFailed(let request, let fallbackText):
64+
return "Provider fallback: \(request.sourceText) -> \(fallbackText)"
65+
}
66+
}
67+
}

LiveLocalizationKit/LiveLocalizationKitApp.swift

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import LiveLocalizationTranslationSupport
55
@main
66
struct LiveLocalizationKitApp: App {
77
private let shouldPresentPreparationGate: Bool
8+
private let eventStore = DemoLocalizationEventStore()
89
@State private var isConfigured = false
910

1011
init() {
@@ -15,7 +16,10 @@ struct LiveLocalizationKitApp: App {
1516
WindowGroup {
1617
Group {
1718
if isConfigured {
18-
RootDemoView(shouldPresentPreparationGate: shouldPresentPreparationGate)
19+
RootDemoView(
20+
shouldPresentPreparationGate: shouldPresentPreparationGate,
21+
eventStore: eventStore
22+
)
1923
} else {
2024
ProgressView()
2125
}
@@ -28,7 +32,10 @@ struct LiveLocalizationKitApp: App {
2832
cachePolicy: LocalizationCachePolicy(
2933
namespace: "demo",
3034
providerIdentifier: "apple-translation"
31-
)
35+
),
36+
logger: ClosureLocalizationLogger { event in
37+
await eventStore.record(event)
38+
}
3239
)
3340
isConfigured = true
3441
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import SwiftUI
2+
3+
struct LocalizationEventLogView: View {
4+
let eventStore: DemoLocalizationEventStore
5+
6+
@State private var entries: [String] = []
7+
8+
var body: some View {
9+
NavigationStack {
10+
Group {
11+
if entries.isEmpty {
12+
ContentUnavailableView(
13+
"No Events Yet",
14+
systemImage: "list.bullet.rectangle",
15+
description: Text("Run the SwiftUI or UIKit demos to generate localization events.")
16+
)
17+
} else {
18+
List(entries, id: \.self) { entry in
19+
Text(entry)
20+
.font(.footnote.monospaced())
21+
.textSelection(.enabled)
22+
}
23+
}
24+
}
25+
.navigationTitle("Events")
26+
}
27+
.task {
28+
for await snapshot in await eventStore.snapshots() {
29+
entries = snapshot
30+
}
31+
}
32+
}
33+
}
34+
35+
#Preview {
36+
LocalizationEventLogView(eventStore: DemoLocalizationEventStore())
37+
}

LiveLocalizationKit/RootDemoView.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import LiveLocalizationTranslationSupport
33

44
struct RootDemoView: View {
55
let shouldPresentPreparationGate: Bool
6+
let eventStore: DemoLocalizationEventStore
67

78
var body: some View {
89
TranslationPreparationGate(isEnabled: shouldPresentPreparationGate) {
@@ -21,6 +22,11 @@ struct RootDemoView: View {
2122
.tabItem {
2223
Label("UIKit", systemImage: "square.stack.3d.up")
2324
}
25+
26+
LocalizationEventLogView(eventStore: eventStore)
27+
.tabItem {
28+
Label("Events", systemImage: "list.bullet.rectangle")
29+
}
2430
}
2531
}
2632
}

0 commit comments

Comments
 (0)