- 폰 첫 화면: 시작 버튼 위 워치 연결 배지(연결됨/미연결/미연동 — iOS 제약상 '연결됨'은 워치 앱 포그라운드 기준, 설정·도움말에 안내), 미연결 시작 시 확인 창 (기록은 동일함을 안내), 설정 > 애플워치 > '워치 미연결 경고' 토글. 단축어·autoStart는 경고 없이 통과 - 세팅 동기화 전면 제거(기기마다 진동 질감이 달라 각자 최적화) — 동기화는 기록만. 워치 시작 화면 왼쪽 스와이프로 전용 세팅 페이지(TabView .page): 준비/수축/이완/ 횟수 스테퍼(2줄 컴팩트 라벨), 진동 2종 피커(선택 즉시 미리 울림), 운동 유형·강도. 요약 라인 즉시 반영(SettingsStore EnvironmentObject) - 워치 진동 강화 재설계: 기본=.start 1방, 톡톡=.start 2방(0.35s), 지이이잉=.retry 2방 연결(~1.3s), 완주=.retry×3+.success(~2.5s), 세대 토큰 취소 - 도움말 4건·번역(en/ja) 갱신, CLAUDE.md 현행화 - 시뮬 검증: 배지·경고 창·설정 토글, 워치 세팅 페이지(46mm·40mm)·요약 반영, 3종 빌드 그린·경고 0·l10n 0/0/0 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UPmp29DQHY6Ahfss5SRopT
57 lines
1.7 KiB
Swift
57 lines
1.7 KiB
Swift
//
|
|
// PhoneSync.swift
|
|
// Keging
|
|
//
|
|
// 워치 연동 (iOS 측) — 워치에서 완주한 기록을 받아 병합하고, 연결 상태를 알린다.
|
|
// 세팅은 기기별 독립(2026-09-03, 7차 — 기기마다 진동 질감이 달라 각자 최적화)이라
|
|
// 더 이상 push/응답하지 않는다.
|
|
//
|
|
|
|
import WatchConnectivity
|
|
|
|
final class PhoneSync: NSObject, WCSessionDelegate {
|
|
static let shared = PhoneSync()
|
|
|
|
private override init() { super.init() }
|
|
|
|
func activate() {
|
|
guard WCSession.isSupported() else { return }
|
|
let session = WCSession.default
|
|
session.delegate = self
|
|
session.activate()
|
|
}
|
|
|
|
private nonisolated func refreshLinkStatus() {
|
|
Task { @MainActor in WatchLinkStatus.shared.refresh() }
|
|
}
|
|
|
|
// MARK: WCSessionDelegate
|
|
|
|
nonisolated func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
|
|
refreshLinkStatus()
|
|
}
|
|
|
|
nonisolated func sessionDidBecomeInactive(_ session: WCSession) {}
|
|
|
|
nonisolated func sessionDidDeactivate(_ session: WCSession) {
|
|
session.activate()
|
|
}
|
|
|
|
nonisolated func sessionReachabilityDidChange(_ session: WCSession) {
|
|
refreshLinkStatus()
|
|
}
|
|
|
|
/// 페어링·워치 앱 설치 여부 변화
|
|
nonisolated func sessionWatchStateDidChange(_ session: WCSession) {
|
|
refreshLinkStatus()
|
|
}
|
|
|
|
nonisolated func session(_ session: WCSession, didReceiveUserInfo userInfo: [String: Any] = [:]) {
|
|
guard let data = userInfo["record"] as? Data,
|
|
let record = try? JSONDecoder().decode(SessionRecord.self, from: data) else { return }
|
|
Task { @MainActor in
|
|
RecordStore.shared.append(record)
|
|
}
|
|
}
|
|
}
|