You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何提前向用户展示NSPersistentCloudKitContainer的iCloud同步状态?

解决iCloud同步进度提前显示的问题

你的代码问题在于只监听了CloudKit事件的类型,却忽略了事件的状态变化——eventChangedNotification在事件开始和结束时都会触发,但你之前只根据类型设置isSyncing,还加了固定2秒的强制关闭,这就导致进度条只能在事件快结束时才显示。

要更早通知用户同步开始,需要从这几个方面调整:

  • 跟踪事件的state(开始/结束/失败),事件启动时立刻标记同步中
  • 处理多个同步事件同时进行的情况,避免单个事件结束就提前关闭进度条
  • 监听首次同步的初始化阶段,覆盖启动时的云数据下载场景

修改后的CloudSyncManager代码

import Combine
import CoreData
import SwiftUI

class CloudSyncManager: ObservableObject {
    @Published var isSyncing: Bool = false
    // 用Set存储正在进行的同步事件ID,避免多事件冲突
    private var activeSyncEvents: Set<UUID> = []
    private var cancellables: Set<AnyCancellable> = []

    init(persistentContainer: NSPersistentCloudKitContainer) {
        // 监听CloudKit事件状态变化
        NotificationCenter.default.publisher(for: NSPersistentCloudKitContainer.eventChangedNotification)
            .sink { [weak self] notification in
                guard let self,
                      let event = notification.userInfo?[NSPersistentCloudKitContainer.eventNotificationUserInfoKey] as? NSPersistentCloudKitContainer.Event
                else { return }

                DispatchQueue.main.async {
                    switch event.state {
                    case .started:
                        // 只关注导入、导出、初始化类型的同步事件
                        if [.import, .export, .setup].contains(event.type) {
                            self.activeSyncEvents.insert(event.id)
                            self.isSyncing = !self.activeSyncEvents.isEmpty
                        }
                    case .ended, .failed:
                        self.activeSyncEvents.remove(event.id)
                        self.isSyncing = !self.activeSyncEvents.isEmpty
                    default:
                        break
                    }
                }
            }
            .store(in: &cancellables)

        // 监听首次iCloud同步完成(针对启动时的初始数据下载)
        persistentContainer.initialCloudKitSyncCompletionHandler = { [weak self] error in
            DispatchQueue.main.async {
                // 如果没有活跃事件了,就关闭同步状态
                if self?.activeSyncEvents.isEmpty ?? true {
                    self?.isSyncing = false
                }
            }
        }

        // 初始化时检查是否有正在进行的同步事件
        checkInitialSyncState(persistentContainer: persistentContainer)
    }

    private func checkInitialSyncState(persistentContainer: NSPersistentCloudKitContainer) {
        persistentContainer.fetchAllEvents { [weak self] events, error in
            guard let self, let events = events else { return }

            DispatchQueue.main.async {
                // 筛选出正在进行的同步事件
                let ongoingEvents = events.filter {
                    [.import, .export, .setup].contains($0.type) && $0.state == .started
                }
                self.activeSyncEvents = Set(ongoingEvents.map { $0.id })
                self.isSyncing = !self.activeSyncEvents.isEmpty
            }
        }
    }
}

使用说明

在初始化Core Data容器时,把容器传给CloudSyncManager:

// 你的Core Data配置代码
let persistentContainer = NSPersistentCloudKitContainer(name: "你的模型名称")
let cloudSyncManager = CloudSyncManager(persistentContainer: persistentContainer)

// SwiftUI视图中使用
struct ContentView: View {
    @StateObject private var syncManager = cloudSyncManager

    var body: some View {
        if syncManager.isSyncing {
            ProgressView("正在同步iCloud数据...")
                .progressViewStyle(CircularProgressViewStyle())
                .frame(maxWidth: .infinity, maxHeight: .infinity)
        } else {
            // 显示你的主界面
            HomeView()
        }
    }
}

关键改进点

  1. 跟踪活跃事件:用Set<UUID>记录正在进行的同步事件ID,只有所有事件都结束,才会把isSyncing设为false
  2. 监听事件状态:当事件state为.started时立刻开启进度条,.ended或.failed时移除对应事件
  3. 覆盖首次同步:通过initialCloudKitSyncCompletionHandler处理启动阶段的初始数据下载,避免漏掉这部分同步状态
  4. 初始状态检查:初始化时主动查询当前正在进行的事件,防止刚启动就错过已经开始的同步

内容的提问来源于stack exchange,提问作者Petr Smejkal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 10:07:26