mycode/myApp/HaruDanim/IOS/Core/AppRefresh.swift
songyc macbook 7720ed5925 fix(sync): receive CloudKit silent pushes so cross-device changes arrive while the app is open
다른 기기(아이폰↔아이패드)에서 측정을 시작/종료해도 사용 중인
기기에는 반영되지 않고, 모음 탭 새로고침 버튼을 눌러도 그대로이던
버그 수정. 앱을 나갔다 들어와야만 반영되던 증상의 원인.

원인: 푸시 엔타이틀먼트(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
2026-07-14 16:10:00 +09:00

69 lines
2.5 KiB
Swift
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// 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("새로고침"))
}
}