【问题标题】:Too Many Notifications CoreData/CloudKit Sync太多通知 CoreData/CloudKit 同步
【发布时间】:2021-11-10 18:58:04
【问题描述】:

我有几个使用 CoreData / iCloud 同步的应用程序,它们在启动时都会收到大量更新/更改/插入/删除/等通知,有时在运行时不会对基础数据进行任何更改。添加或删除新项目时,我似乎再次收到有关所有内容的通知。甚至通知的数量也不一致。

我的问题是,我该如何避免这种情况?一旦我确定我的所有设备都是最新的,是否可以应用截止日期。

坚持

import Foundation
import UIKit
import CoreData

struct PersistenceController {
    let ns = NotificationStuff()
    static let shared = PersistenceController()

    static var preview: PersistenceController = {
        let result = PersistenceController(inMemory: true)
        let viewContext = result.container.viewContext
        for _ in 0..<10 {
            let newItem = Item(context: viewContext)
            newItem.stuff = "Stuff"
            newItem.timestamp = Date()
        }
        do {
            try viewContext.save()
        } catch {
            let nsError = error as NSError
            fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
        }
        return result
    }()

    let container: NSPersistentCloudKitContainer

    init(inMemory: Bool = false) {
        container = NSPersistentCloudKitContainer(name: "TestCoreDataSync")
        if inMemory {
            container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
        }
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        })
        
    }
}

class NotificationStuff
{
    var changeCtr = 0
    
    init()
    {
        NotificationCenter.default.addObserver(self, selector: #selector(self.processUpdate), name: Notification.Name.NSPersistentStoreRemoteChange, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(contextDidSave(_:)), name: Notification.Name.NSManagedObjectContextDidSave, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(contextObjectsDidChange(_:)), name: Notification.Name.NSManagedObjectContextObjectsDidChange, object: nil)
    }
    
    @objc func processUpdate(_ notification: Notification)
    {
        //print(notification)
        DispatchQueue.main.async
        { [self] in
            observerSelector(notification)
        }
    }
    
    @objc func contextObjectsDidChange(_ notification: Notification)
    {
       DispatchQueue.main.async
        { [self] in
            observerSelector(notification)
        }
    }
    
    @objc func contextDidSave(_ notification: Notification)
    {
        DispatchQueue.main.async
        {
            self.observerSelector(notification)
        }
    }
    
    func observerSelector(_ notification: Notification) {
        
        DispatchQueue.main.async
        { [self] in
            if let insertedObjects = notification.userInfo?[NSInsertedObjectsKey] as? Set<NSManagedObject>, !insertedObjects.isEmpty
            {
                print("Insert")
            }
            
            if let updatedObjects = notification.userInfo?[NSUpdatedObjectsKey] as? Set<NSManagedObject>, !updatedObjects.isEmpty
            {
                changeCtr = changeCtr + 1
                print("Change \(changeCtr)")
            }
            
            if let deletedObjects = notification.userInfo?[NSDeletedObjectsKey] as? Set<NSManagedObject>, !deletedObjects.isEmpty
            {
                print("Delete")
            }
            
            if let refreshedObjects = notification.userInfo?[NSRefreshedObjectsKey] as? Set<NSManagedObject>, !refreshedObjects.isEmpty
            {
                print("Refresh")
            }
            
            if let invalidatedObjects = notification.userInfo?[NSInvalidatedObjectsKey] as? Set<NSManagedObject>, !invalidatedObjects.isEmpty
            {
                print("Invalidate")
            }
            
            
            let mainManagedObjectContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
            guard let context = notification.object as? NSManagedObjectContext else { return }
            
            // Checks if the parent context is the main one
            if context.parent === mainManagedObjectContext
            {
                
                // Saves the main context
                mainManagedObjectContext.performAndWait
                {
                    do
                    {
                        try mainManagedObjectContext.save()
                    } catch
                    {
                        print(error.localizedDescription)
                    }
                }
            }
        }
    }
}

内容视图

import SwiftUI
import CoreData

struct ContentView: View {
    @State var stuff = ""
    @Environment(\.managedObjectContext) private var viewContext

    @FetchRequest(
        sortDescriptors: [NSSortDescriptor(keyPath: \Item.timestamp, ascending: true)],
        animation: .default)
    private var items: FetchedResults<Item>

    var body: some View {
        VStack
        {
            TextField("Type here", text: $stuff,onCommit: { addItem(stuff: stuff)
                stuff = ""
            })
            List {
                ForEach(items) { item in
                    Text(item.stuff ?? "??")
                }
                .onDelete(perform: deleteItems)
            }
        }.padding()
    }

   private func addItem(stuff: String) {
        withAnimation {
            let newItem = Item(context: viewContext)
            newItem.timestamp = Date()
            newItem.stuff = stuff

            do {
                try viewContext.save()
            } catch {
                let nsError = error as NSError
                fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
            }
        }
    }

    private func deleteItems(offsets: IndexSet) {
        withAnimation {
            offsets.map { items[$0] }.forEach(viewContext.delete)

            do {
                try viewContext.save()
            } catch {
                let nsError = error as NSError
                fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
            }
        }
    }
}

private let itemFormatter: DateFormatter = {
    let formatter = DateFormatter()
    formatter.dateStyle = .short
    formatter.timeStyle = .medium
    return formatter
}()

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
    }
}

数据库有一个 Item 实体,其中包含一个时间戳字段和一个名为 stuff 的字符串字段。

【问题讨论】:

    标签: core-data swiftui synchronization icloud cloudkit


    【解决方案1】:

    这取决于它是分别在系统的控制台还是 Xcode 的控制台中检查生产或调试版本。

    对于生产版本,我的理解是通过始终使用以下内容使我的消息更容易找到(而不是去强调/隐藏其他消息):

    let log = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "YourCategorisationOfMessagesGoingToThisHandle")

    然后在代码中我可能会有类似的东西

    log.debug("My debug message") log.warning("My warning etc")

    fwiw:我倾向于按文件所在的文件对内容进行分类,因为这是确定性的,可以帮助我找到文件,所以我的源文件往往以

    开头

    fileprivate let log = Logger(subsystem: Bundle.main.bundleIdentifier!, category: #file.components(separatedBy: "/").last ?? "")

    如果这样做,我可以轻松过滤系统的控制台消息以查找与我的应用相关的内容。

    还有更多关于如何使用它和控制台在系统控制台中通过here过滤应用程序的消息。

    对于调试版本和 Xcode 控制台,可以使用来自我的应用程序的相同一致的应用程序日志消息,例如我的应用程序的调试消息总是以“一些容易找到的字符串或其他”开头。我不相信有一种方法可以选择性地限制/切断响应。但是绝对有可能完全关闭来自许多嘈杂子系统的调试消息(一旦高兴它们可靠地工作)

    对于提到的 Core Data 和 CloudKit 案例,如果我使用 -com.apple.CoreData.Logging.stderr 0 和 -com.apple.CoreData.CloudKitDebug 0 启动参数运行 Debug 构建,那么这会使 Xcode 的控制台更安静:-)。关于如何在here上的 SO 答案中进行设置的很好的说明

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-06-11
      • 1970-01-01
      • 2021-05-23
      • 2021-05-27
      • 2020-11-06
      • 1970-01-01
      • 1970-01-01
      • 2020-12-12
      相关资源
      最近更新 更多