使用获取结果核心数据中的观察对象更新视图

问题描述 投票:0回答:1

我有一个父视图,它正在读取核心数据并创建子节点信息列表。我希望在值更新后立即更新每一行。

我的第一次尝试,我使用 fetchedresults 来创建一个列表,并将 @Observedobject 传递给另一个视图来渲染该行。它有点有效,但仅在您来回切换到不同的 UI 选项卡时更新视图,而不是当场更新。

我的第二次尝试,我在List和NodeListItem上使用了NotificationCenter.default.publisher和@State变量refreshID = UUID。这有助于重新渲染视图,但是当外设接收被动数据时,每个行项目每分钟都会调用 .onReceive 函数约 8-9 次,并且每当外设发送数据时,它还会忽略用于更新用户行的工作表视图短信通知

节点列表

  struct NodeList: View {
    @Environment(\.managedObjectContext) var context
    @EnvironmentObject var bleManager: BLEManager
    @FetchRequest( sortDescriptors: [NSSortDescriptor(key: "user.vip", ascending: false), NSSortDescriptor(key: "lastHeard", ascending: false)], animation: .default)

    var nodes: FetchedResults<RadioInfoEntity>

    private var didSave =  NotificationCenter.default.publisher(for: .NSManagedObjectContextDidSave)
    @State private var refreshID = UUID()

     var body: some View {
       NavigationStack (path: $path) {
        List(nodes, id: \.self) { node in
         NodeListItem(node: node,
                     connected: bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral?.num ?? -1 == node.num)
        .onReceive(self.didSave) { _ in   //the listener
                        self.refreshID = UUID()
                        print("generated a new UUID")
                        }
        }.id(refreshID)
        .onAppear {
          if self.bleManager.context == nil {
          self.bleManager.context = context
            }
        }
      }
     }
   }

节点列表项

struct NodeListItem: View {

  @Environment(\.managedObjectContext) var context
  @EnvironmentObject var bleManager: BLEManager
  @ObservedObject var node: RadioInfoEntity
  @State private var name: String = ""

  var body: some View { 
   LazyVStack(alignment: .leading) {
     Text(name)
     let status = getStatus(status: name)
     Text(status)
     Button("Update") {
        showingSheetView.toggle()
              } .sheet(isPresented: $showingSheetView) {
                SheetView(user: node)
            }
     }.onAppear(perform: {
        if let tempName = node.user?.name{
          self.name = tempName
       }
      })
     }
   }


struct SheetView: View {
  @Environment(\.dismiss) var SheetViewDismiss
  @ObservedObject var user: RadioInfoEntity
  @State private var name: String = ""
  var body: some View {
     HStack{
      TextField("name", text: $customName)
     }
     .onAppear(perform: {
         self.longName = user.user?.name ?? ""
       })
      Button("Update") {
        self.user.objectWillChange.send()
        var u = User()
        //this is a longer function in reality
        u.name = getUpdatedName(name: name)
        bleManager.saveUser(config: u, fromUser: connectedUser, toUser: user.user!)
        SheetViewDismiss()
        }
     }
   }
 }

BLEmanager 的工作原理有点像这样

 class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {

   // MARK: Data Read / Update Characteristic Event
   func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {

    switch characteristic.uuid {
    case FROMRADIO_UUID:
     switch decodedInfo.packet.decoded.portnum {
      case .nodeinfoApp:
      upsertNodeInfoPacket(packet: decodedInfo.packet, context: context!)
      }
     }
  }
 }

更新核心数据

  func upsertNodeInfoPacket (packet: MeshPacket, context: NSManagedObjectContext) {
   let fetchNodeInfoAppRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "RadioInfoEntity")
        fetchNodeInfoAppRequest.predicate = NSPredicate(format: "num == %lld", Int64(packet.from))
   do {
    let fetchedRadio = try context.fetch(fetchNodeInfoAppRequest) as? [RadioInfoEntity] ?? []
     fetchedRadio[0].id = Int64(packet.from)
     fetchedRadio[0].num = Int64(packet.from)
     fetchedRadio[0].lastHeard = Date()
     if let nodeInfoMessage = try? NodeInfo(serializedData: packet.decoded.payload) {
      if nodeInfoMessage.hasUser {
       fetchedRadio[0].user!.userId = nodeInfoMessage.user.id
       fetchedRadio[0].user!.num = Int64(nodeInfoMessage.num)
       fetchedRadio[0].user!.name = nodeInfoMessage.user.name
      }
     }
     do {
           //this was never needed or seen to do anything so commented out
      //fetchedRadio[0].objectWillChange.send()
       try context.save()
       print("💾 Updated NodeInfo from Node Info App Packet For: \(fetchedRadio[0].num)")
       } catch {
            context.rollback()
            let nsError = error as NSError
           print("💥 Error Saving RadioInfoEntity from NODEINFO_APP \(nsError)")
        }
        } catch {
          print("💥 Error Fetching RadioInfoEntity for NODEINFO_APP")
        }
}
     
     

如有任何建议,我们将不胜感激

swift swiftui core-data nsfetchedresultscontroller observedobject
1个回答
0
投票

您需要删除

@State
并使用
@ObservedObject
,如下所示:

@ObservedObject var node: RadioInfoEntity
...
Text(node.name)
© www.soinside.com 2019 - 2024. All rights reserved.