- DataStore: App Group 컨테이너의 HaruDanim.store 사용, 기존 default.store 1회 자동 이관 - 프리미엄 + 동기화 토글 시 CloudKit(.private) 구성으로 컨테이너 생성, 실패하면 로컬 폴백 - CloudKit 요건(모든 관계 optional)에 맞춰 to-many 관계를 optional 저장 + 기존 이름 접근자로 리팩터링 (originalName 매핑으로 기존 데이터 유지) - Tag/Action/Goal/Quest에 uuid 추가 (시리 단축어·위젯·워치 식별자) + 중복 보정 - 모델 레이어(Models/Settings/DayMath/QuestProgress/Formatters/Theme)를 Shared/로 이동 — 위젯 확장과 공유 - 집계 설정(주 시작 요일·하루 시작 시간·대표 시간·짧은 기록 무시)을 App Group defaults로 이관해 위젯·워치와 공유, 설정의 '기기 간 동기화' 토글 활성화 - Goal.combinedSpanRatio 공용화 (모음 탭 카드·위젯·시리가 동일 산식 사용) - DEBUG 검증 인자: -cloudSync YES, -settingsScrollPremium YES Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
122 lines
4.0 KiB
Swift
122 lines
4.0 KiB
Swift
//
|
|
// Settings.swift
|
|
// Haru_Danim
|
|
//
|
|
// 앱 설정 키, 무료 제한 (CLAUDE.md §6.6, §7)
|
|
//
|
|
|
|
import Foundation
|
|
|
|
enum SettingsKeys {
|
|
/// "light" | "dark"
|
|
static let theme = "settings.theme"
|
|
/// "ko" | "en" | "ja"
|
|
static let language = "settings.language"
|
|
/// Calendar.weekday 값 (1=일요일 ... 7=토요일). 기본값 2(월요일)
|
|
static let weekStartWeekday = "settings.weekStartWeekday"
|
|
/// 자정 기준 분 단위 오프셋. 기본값 0 (00:00)
|
|
static let dayStartMinutes = "settings.dayStartMinutes"
|
|
/// "latest"(가장 나중에 시작) | "earliest"(가장 먼저 시작). 기본값 latest
|
|
static let liveActivityMode = "settings.liveActivityMode"
|
|
// 프리미엄 여부는 Shared/Premium.swift의 PremiumGate.cacheKey (App Group defaults) 사용
|
|
/// 메인 탭 그리드 한 줄에 표시할 행동 버튼 개수. 기본값 3
|
|
static let gridColumns = "settings.gridColumns"
|
|
/// 이 시간(초)보다 짧게 측정된 세션은 기록하지 않음. 0 = 사용 안 함
|
|
static let minSessionSeconds = "settings.minSessionSeconds"
|
|
/// 탭바에 직접 노출할 탭(더보기 제외 1~3개). AppTab rawValue를 쉼표로 연결한 문자열
|
|
static let visibleTabs = "settings.visibleTabs"
|
|
/// 모음 탭 목표 카드 표시 방식. GoalCardStyle rawValue ("perQuest" | "combined")
|
|
static let goalCardStyle = "settings.goalCardStyle"
|
|
}
|
|
|
|
/// 모음 탭 목표 진행 현황 카드의 표시 방식 (선택된 모든 목표 카드에 일괄 적용)
|
|
enum GoalCardStyle: String, CaseIterable, Identifiable {
|
|
/// 다짐(최대 3개)별로 하루/주간/월간 진행률을 각각 표시
|
|
case perQuest
|
|
/// 소속 다짐 전체의 하루/주간/월간 진행률을 합산(평균)해 한 번에 표시
|
|
case combined
|
|
|
|
var id: String { rawValue }
|
|
|
|
var label: String {
|
|
switch self {
|
|
case .perQuest: return "다짐별로 각각"
|
|
case .combined: return "전체 다짐 합산"
|
|
}
|
|
}
|
|
}
|
|
|
|
enum TabBarConfig {
|
|
/// 기본 노출 탭: 메인 · 행동 · 기록 (+더보기)
|
|
static let defaultVisibleTabs = "main,action,history"
|
|
static let maxVisible = 3
|
|
static let minVisible = 1
|
|
}
|
|
|
|
/// 짧은 기록 무시 옵션 선택지 (초)
|
|
enum MinSessionOption {
|
|
static let choices: [(seconds: Int, label: String)] = [
|
|
(0, "사용 안 함"),
|
|
(3, "3초"),
|
|
(5, "5초"),
|
|
(10, "10초"),
|
|
(30, "30초"),
|
|
(60, "1분"),
|
|
]
|
|
}
|
|
|
|
enum LiveActivityMode: String, CaseIterable, Identifiable {
|
|
case latest
|
|
case earliest
|
|
|
|
var id: String { rawValue }
|
|
|
|
var label: String {
|
|
switch self {
|
|
case .latest: return "가장 나중에 시작한 행동"
|
|
case .earliest: return "가장 먼저 시작한 행동"
|
|
}
|
|
}
|
|
}
|
|
|
|
enum AppLanguage: String, CaseIterable, Identifiable {
|
|
case ko, en, ja
|
|
|
|
var id: String { rawValue }
|
|
|
|
var label: String {
|
|
switch self {
|
|
case .ko: return "한국어"
|
|
case .en: return "English"
|
|
case .ja: return "日本語"
|
|
}
|
|
}
|
|
}
|
|
|
|
// 무료 사용 한도(FreeLimits)는 Shared/Premium.swift로 이동 (위젯·인텐트에서도 사용)
|
|
|
|
/// 집계에 필요한 설정 스냅숏
|
|
struct TrackingSettings {
|
|
var weekStartWeekday: Int
|
|
var dayStartMinutes: Int
|
|
|
|
static func current() -> TrackingSettings {
|
|
// App Group 우선(위젯·인텐트와 공유), 이관 전 값은 standard에서 폴백
|
|
let group = AppGroup.defaults
|
|
let standard = UserDefaults.standard
|
|
func intValue(_ key: String, default defaultValue: Int) -> Int {
|
|
(group.object(forKey: key) ?? standard.object(forKey: key)) as? Int ?? defaultValue
|
|
}
|
|
return TrackingSettings(
|
|
weekStartWeekday: intValue(SettingsKeys.weekStartWeekday, default: 2),
|
|
dayStartMinutes: intValue(SettingsKeys.dayStartMinutes, default: 0)
|
|
)
|
|
}
|
|
|
|
var calendar: Calendar {
|
|
var cal = Calendar.current
|
|
cal.firstWeekday = weekStartWeekday
|
|
return cal
|
|
}
|
|
}
|