Skip to content

Instantly share code, notes, and snippets.

@SoundBlaster
Created August 5, 2025 21:56
Show Gist options
  • Save SoundBlaster/b8436977a3292d615220e780553dd6e4 to your computer and use it in GitHub Desktop.
Save SoundBlaster/b8436977a3292d615220e780553dd6e4 to your computer and use it in GitHub Desktop.
GPT-OSS-20B SwiftUI App Test
// ──────────────────────────────────────────────────────────────
// 1️⃣ Project‑Structure (all files in one block for demo)
// ──────────────────────────────────────────────────────────────
//
// AppDelegate.swift – UIApplicationDelegate (optional)
// SceneDelegate.swift – UIWindowSceneDelegate (if needed)
// MainApp.swift – @main entry point
// Models/CounterEntity.swift
// Domain/Protocols/CounterRepositoryProtocol.swift
// Domain/UseCases/IncrementCounter.swift
// Domain/UseCases/ResetCounter.swift
// Data/UserDefaultsCounterRepository.swift
// Presentation/Store/CounterStore.swift
// Presentation/ViewModel/CounterViewModel.swift
// Presentation/View/CounterView.swift
// Presentation/View/OnboardingView.swift
// Presentation/Coordinator/AppCoordinator.swift
// Presentation/Theme/ColorPalette.swift
// Utils/Logger.swift
// Utils/Extensions/UIHostingController+Coordinator.swift
//
// ──────────────────────────────────────────────────────────────
import SwiftUI
import Combine
// MARK: - 1️⃣ Models ---------------------------------------------------------
struct CounterEntity {
var value: Int = 0
}
// MARK: - 2️⃣ Domain Layer ---------------------------------------------------
protocol CounterRepositoryProtocol: AnyObject {
var counterPublisher: Published<CounterEntity>.Publisher { get }
func increment() throws
func reset()
}
enum CounterError: Error, LocalizedError {
case failedToPersist
var errorDescription: String? {
switch self {
case .failedToPersist: return "Could not persist counter value."
}
}
}
// MARK: - 3️⃣ Use Cases ------------------------------------------------------
final class IncrementCounter {
private let repository: CounterRepositoryProtocol
init(repository: CounterRepositoryProtocol) { self.repository = repository }
func execute() throws {
try repository.increment()
}
}
final class ResetCounter {
private let repository: CounterRepositoryProtocol
init(repository: CounterRepositoryProtocol) { self.repository = repository }
func execute() {
repository.reset()
}
}
// MARK: - 4️⃣ Data Layer -----------------------------------------------------
final class UserDefaultsCounterRepository: CounterRepositoryProtocol {
@Published private(set) var counter = CounterEntity()
private let key = "tap_counter"
private let logger = Logger.shared
init() {
let stored = UserDefaults.standard.integer(forKey: key)
self.counter.value = stored
}
var counterPublisher: Published<CounterEntity>.Publisher { $counter }
func increment() throws {
counter.value += 1
guard persist() else { throw CounterError.failedToPersist }
}
func reset() {
counter.value = 0
_ = persist()
}
private func persist() -> Bool {
UserDefaults.standard.set(counter.value, forKey: key)
// In normal usage this is effectively always true.
return true
}
}
// MARK: - 5️⃣ Presentation Layer – Store ------------------------------------
final class CounterStore: ObservableObject {
// 1️⃣ Provide an initial value → _counter is ready.
@Published private(set) var counter = CounterEntity()
private let incrementUseCase: IncrementCounter
private let resetUseCase: ResetCounter
// <‑‑ Add a logger reference
private let logger = Logger.shared
init(repository: CounterRepositoryProtocol) {
self.incrementUseCase = IncrementCounter(repository: repository)
self.resetUseCase = ResetCounter(repository: repository)
// 2️⃣ Now we can safely subscribe.
repository.counterPublisher
.receive(on: DispatchQueue.main)
.assign(to: &$counter) // OK – $counter exists now
}
func increment() {
do { try incrementUseCase.execute() }
catch {
logger.error("Increment failed: $error)") // fixed interpolation
}
}
func reset() { resetUseCase.execute() }
}
// MARK: - 6️⃣ Presentation Layer – ViewModel --------------------------------
final class CounterViewModel: ObservableObject {
@Published private(set) var counterValue: Int = 0
private let store: CounterStore
private var cancellables = Set<AnyCancellable>()
init(store: CounterStore) { self.store = store }
func bind() {
store.$counter
.map(\.value)
.receive(on: DispatchQueue.main)
.assign(to: &$counterValue)
}
// Commands
func tapButtonPressed() { store.increment() }
func resetButtonPressed() { store.reset() }
}
// MARK: - 7️⃣ Presentation Layer – Views ------------------------------------
struct CounterView: View {
@EnvironmentObject private var store: CounterStore
@StateObject private var vm = CounterViewModel(store: CounterStore(repository: UserDefaultsCounterRepository()))
init() {
_vm = StateObject(wrappedValue: CounterViewModel(store: CounterStore(repository: UserDefaultsCounterRepository())))
}
var body: some View {
VStack(spacing: 40) {
Text("You tapped \(vm.counterValue) times")
.font(.largeTitle)
.fontWeight(.semibold)
HStack(spacing: 20) {
Button(action: vm.tapButtonPressed) {
Label("Tap me!", systemImage: "hand.tap.fill")
.font(.title2)
.padding()
.background(ColorPalette.primary.opacity(0.1))
.cornerRadius(12)
}
Button(action: vm.resetButtonPressed) {
Label("Reset", systemImage: "arrow.counterclockwise")
.font(.title3)
.foregroundColor(.red)
.padding()
.background(Color.red.opacity(0.1))
.cornerRadius(12)
}
}
}
.onAppear { vm.bind() }
.padding()
}
}
struct OnboardingView: View {
let onContinue: () -> Void
var body: some View {
VStack(spacing: 30) {
Text("Welcome to Tap Counter")
.font(.title)
.multilineTextAlignment(.center)
Button("Get Started") { onContinue() }
.buttonStyle(.borderedProminent)
}
.padding()
}
}
// MARK: - 8️⃣ Presentation Layer – Coordinator --------------------------------
final class AppCoordinator: ObservableObject {
@Published var isFirstLaunch = true
let store: CounterStore
private let logger = Logger.shared
init() {
// Shared repository
let repo = UserDefaultsCounterRepository()
self.store = CounterStore(repository: repo)
// Check onboarding flag
if UserDefaults.standard.bool(forKey: "has_seen_onboarding") {
isFirstLaunch = false
} else {
logger.debug("First launch detected")
}
}
func completeOnboarding() {
UserDefaults.standard.set(true, forKey: "has_seen_onboarding")
withAnimation { isFirstLaunch = false }
}
}
// MARK: - 9️⃣ Presentation Layer – Theme ------------------------------------
struct ColorPalette {
static let primary = Color.accentColor // or any system color
static let secondary = Color.secondary // e.g. .secondary, .systemBlue, etc.
}
// MARK: - 🔟 Utilities -------------------------------------------------------
final class Logger {
static let shared = Logger()
private init() {}
func debug(_ message: String) { print("[DEBUG] $message)") }
func error(_ message: String) { print("[ERROR] $message)") }
}
/// A small helper that sets the hosting controller’s view background.
extension UIHostingController {
/// Sets a system background color on the host’s view.
func setSystemBackground() -> Self {
self.view.backgroundColor = .systemBackground
return self
}
}
// MARK: - 📦 Main App Entry --------------------------------------------------
@main
struct TapCounterApp: App {
@StateObject private var coordinator = AppCoordinator()
var body: some Scene {
WindowGroup {
Group {
if coordinator.isFirstLaunch {
OnboardingView(onContinue: { coordinator.completeOnboarding() })
.environmentObject(coordinator.store)
} else {
CounterView()
.environmentObject(coordinator.store)
}
}
.animation(.default, value: coordinator.isFirstLaunch)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment