mycode/myApp/Expiranner/IOS/ExpirannerApp.swift
songyc macbook 6c4fe817bd feat(expiranner): 라이트/다크 모드 · 시간 포함 유통기한 · 지난 데이터 정리
- 설정 화면 신규(첫 화면 헤더 톱니 진입): 화면 모드(시스템/라이트/다크) 선택,
  '기간 지난 데이터 삭제'로 만료된 기록 일괄 정리(매장 무관, 확인 후 실행)
- Theme 색상을 라이트/다크 자동 전환 동적 색으로 전환, 기본값은 기존과 동일한 다크
- 유통기한에 시각(시:분) 선택 입력: 날짜 선택 후 '시간 추가' 버튼으로만 노출돼
  기본 사용성(사진→날짜→저장)은 그대로 유지, 유제품 등 필요 시에만 사용
- ProductRecord.hasTime 플래그 추가(기본 false, 라이트웨이트 마이그레이션),
  시각 포함 기록은 날짜 상세 그리드에 시각 배지 표시
- .gitignore에 Xcode 빌드 산출물/파생 데이터/SwiftPM 상태 추가해 상태 오염 방지

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J2HQuA6RPtkh45QTMN83i2
2026-07-23 17:46:12 +09:00

97 lines
3.0 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()
.tint(Theme.accent)
}
.modelContainer(container)
}
}
/// .
private struct RootView: View {
@AppStorage(appearanceStorageKey) private var appearanceRaw = AppearanceMode.dark.rawValue
@State private var showSplash = true
var body: some View {
ZStack {
StoreListView()
if showSplash {
SplashView()
.transition(.opacity)
.zIndex(1)
}
}
.preferredColorScheme(AppearanceMode(rawValue: appearanceRaw)?.colorScheme)
.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
}
}
}
}