다른 기기(아이폰↔아이패드)에서 측정을 시작/종료해도 사용 중인 기기에는 반영되지 않고, 모음 탭 새로고침 버튼을 눌러도 그대로이던 버그 수정. 앱을 나갔다 들어와야만 반영되던 증상의 원인. 원인: 푸시 엔타이틀먼트(aps-environment)는 있었지만 UIBackgroundModes에 remote-notification이 없어 CloudKit의 "데이터 바뀜" 무음 푸시가 앱에 전달되지 않았다. Core Data+CloudKit은 이 푸시로 가져오기(import)를 트리거하므로, 없으면 앱 실행/포그라운드 복귀 때만 가져온다. 새로고침 버튼은 로컬 스토어 재조회일 뿐이라 가져오기 전에는 옛 데이터만 보였다. 수정: iOS 타깃에 IOS/Info.plist 신설(UIBackgroundModes: remote-notification)하고 INFOPLIST_FILE로 지정 — 생성 Info.plist (GENERATE_INFOPLIST_FILE=YES)와 병합된다. IOS 동기화 그룹에 membershipException을 추가해 plist가 리소스로 중복 복사되지 않게 함. 검증: Debug·Store 빌드 성공, 두 빌드 산출물 Info.plist에 UIBackgroundModes=[remote-notification]과 기존 생성 키(표시 이름· Live Activity) 병합 확인, 시뮬레이터 콜드 스타트 정상. 기기 간 동기화 도착은 실기기 2대에서 확인 필요. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
69 lines
2.5 KiB
Swift
69 lines
2.5 KiB
Swift
//
|
||
// AppRefresh.swift
|
||
// Haru_Danim
|
||
//
|
||
// 수동 새로고침 (모음 탭 버튼)
|
||
// - iCloud 동기화로 다른 기기에서 바뀐 데이터가 화면에 바로 안 보일 때, 앱을 껐다 켜는
|
||
// 대신 쓰는 버튼. token을 바꾸면 루트 뷰 identity가 리셋되어 모든 @Query가 스토어에서
|
||
// 다시 조회된다 (재시작과 같은 효과, 데이터는 그대로).
|
||
// - 원격 변경 자동 반영(CloudSyncMonitor)은 위젯·워치·라이브 액티비티만 갱신한다.
|
||
// 화면 전체 리셋은 입력 중인 시트를 닫아버릴 수 있어 수동 버튼으로만 한다.
|
||
// ⚠️ 이 버튼은 "로컬 스토어에 이미 도착한" 변경을 화면에 반영할 뿐, CloudKit 가져오기를
|
||
// 강제할 공식 API는 없다. 가져오기 자체는 무음 푸시(UIBackgroundModes:
|
||
// remote-notification, IOS/Info.plist)가 트리거한다 — 이 모드가 빠져 있으면 앱
|
||
// 실행/포그라운드 복귀 때만 가져와서 새로고침을 눌러도 옛 데이터가 보인다(수정된 버그).
|
||
//
|
||
|
||
import Foundation
|
||
import Observation
|
||
import SwiftData
|
||
import WidgetKit
|
||
|
||
@MainActor
|
||
@Observable
|
||
final class AppRefresh {
|
||
static let shared = AppRefresh()
|
||
|
||
/// RootNavigationView가 .id로 사용 — 바뀌면 화면 전체가 다시 만들어진다
|
||
private(set) var token = UUID()
|
||
private(set) var isRefreshing = false
|
||
|
||
func refreshNow() {
|
||
guard !isRefreshing else { return }
|
||
isRefreshing = true
|
||
token = UUID()
|
||
Task { @MainActor in
|
||
// 화면 재구성과 함께 파생 상태도 최신 데이터로 맞춘다
|
||
LiveActivityManager.sync(context: DataStore.shared.mainContext)
|
||
WidgetCenter.shared.reloadAllTimelines()
|
||
WatchSyncManager.shared.pushSnapshot()
|
||
// 버튼이 눌렸다는 피드백이 보일 만큼만 잠깐 유지
|
||
try? await Task.sleep(for: .milliseconds(700))
|
||
isRefreshing = false
|
||
}
|
||
}
|
||
}
|
||
|
||
// MARK: - 새로고침 버튼 (모음 탭 툴바)
|
||
|
||
import SwiftUI
|
||
|
||
/// 다른 기기에서 바뀐 데이터를 다시 불러오는 작은 툴바 버튼
|
||
struct RefreshButton: View {
|
||
private let refresh = AppRefresh.shared
|
||
|
||
var body: some View {
|
||
Button {
|
||
refresh.refreshNow()
|
||
} label: {
|
||
if refresh.isRefreshing {
|
||
ProgressView()
|
||
} else {
|
||
Image(systemName: "arrow.clockwise")
|
||
}
|
||
}
|
||
.disabled(refresh.isRefreshing)
|
||
.accessibilityLabel(Text("새로고침"))
|
||
}
|
||
}
|