mycode/myApp/Expiranner/IOS/ExpirannerApp.swift
songyc macbook 9902d44848 feat(expiranner): rebuild as store-based expiry photo checker
- 매장 선택 → 연/월 → 월 캘린더(날짜별 제품 사진 썸네일) → 날짜 상세 플로우로 전면 재작성
- 스캔 플로우: 촬영 → 재촬영/사용 → 그 달 날짜만 선택 → 저장 후 카메라 자동 복귀 (손전등 지원)
- 날짜 상세 다중 선택 삭제 (전체 선택/해제 + 일괄 삭제 확인)
- 다크 전용 민트 테마, SwiftData 모델(Store ↔ ProductRecord, 원본+썸네일 분리 저장)
- 밝은 민트 앱 아이콘(SVG 원본 포함) + 런치 스크린/인앱 스플래시
- CLAUDE.md 새 구조 기준으로 재작성

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 15:53:14 +09:00

96 lines
2.9 KiB
Swift

import SwiftUI
import SwiftData
@main
struct ExpirannerApp: App {
private let container: ModelContainer
init() {
let schema = Schema([Store.self, ProductRecord.self])
do {
container = try ModelContainer(for: schema)
} catch {
// .
print("저장소 열기 실패, 데이터를 재설정합니다: \(error)")
Self.removeDefaultStoreFiles()
do {
container = try ModelContainer(for: schema)
} catch {
fatalError("SwiftData 저장소를 만들 수 없습니다: \(error)")
}
}
}
private static func removeDefaultStoreFiles() {
let dir = URL.applicationSupportDirectory
for name in ["default.store", "default.store-shm", "default.store-wal"] {
try? FileManager.default.removeItem(at: dir.appending(path: name))
}
}
var body: some Scene {
WindowGroup {
RootView()
.preferredColorScheme(.dark)
.tint(Theme.accent)
}
.modelContainer(container)
}
}
/// .
private struct RootView: View {
@State private var showSplash = true
var body: some View {
ZStack {
StoreListView()
if showSplash {
SplashView()
.transition(.opacity)
.zIndex(1)
}
}
.task {
try? await Task.sleep(for: .seconds(1.0))
withAnimation(.easeOut(duration: 0.4)) {
showSplash = false
}
}
}
}
private struct SplashView: View {
@State private var appeared = false
var body: some View {
ZStack {
Theme.bg.ignoresSafeArea()
VStack(spacing: 18) {
Image("LaunchIcon")
.resizable()
.scaledToFit()
.frame(width: 110, height: 110)
.scaleEffect(appeared ? 1 : 0.9)
.shadow(color: Theme.accent.opacity(0.25), radius: 30)
VStack(spacing: 6) {
Text("Expiranner")
.font(.system(size: 26, weight: .bold, design: .rounded))
.foregroundStyle(Theme.textPrimary)
Text("유통기한 점검")
.font(.footnote.weight(.semibold))
.foregroundStyle(Theme.accent)
.kerning(1)
}
.opacity(appeared ? 1 : 0)
.offset(y: appeared ? 0 : 8)
}
}
.onAppear {
withAnimation(.spring(duration: 0.6)) {
appeared = true
}
}
}
}