为什么我的 swiftUI 视图在从对等方接收数据时不更新

问题描述

我正在研究 swiftUI 中的 Peer 连接,我创建了一个简单的 Todo 列表,使用 MultipeerConnectivity 将数据发送到关闭的设备。

我设法连接了 2 个设备并接收了数据(当 didReceive 数据触发时我在调试控制台上看到它),但我找不到解决我的 SwiftUI 视图没有更新的解决方案,我必须终止应用程序并重新启动以查看收到的项目。

我有一个带有@Published 数组存储的 DataManager 类:TodoModel,其中保存了所有待办事项。

使用 func newTodo 我将待办事项添加到数组并发送到另一个设备,视图在设备发送器中正确更新,但在设备接收器上没有。

class DataManager: ObservableObject {
    let objectwillChange = PassthroughSubject<Void,Never>()
    static let shared = DataManager()
    
    typealias Storage = [TodoModel]
    
    init() {
        loadData()
    }
    @Published var isRefreshing : Bool = false { // debugging
        willSet {
            objectwillChange.send()
        }
    }
    @Published var storage : [TodoModel] = []{
        willSet {
            objectwillChange.send()
        }
    }
    var resource : String = ""
    
    func loadData() {
     
        if let data = UserDefaults.standard.data(forKey: "salva") {
            
            do {
                let decoder = PropertyListDecoder()
                storage = try decoder.decode(Storage.self,from: data)
            } catch {
                debugPrint(error.localizedDescription)
            }
        }
    }
    
    func save() {
        dispatchQueue.main.async {
            self.objectwillChange.send()
            let encoder = PropertyListEncoder()
            encoder.outputFormat = .xml
            
            do {
                let data = try encoder.encode(self.storage)
                UserDefaults.standard.set(data,forKey: "salva")
            } catch {
                debugPrint(error.localizedDescription)
            }
        }
    }
    
    
    func newTodo(name: String,received: Bool = false,closure: ()->()) {
        let new = TodoModel(nome: name)
        self.objectwillChange.send()
        storage.insert(new,at: 0)
        save()
        closure()
        
        if received { return}
       // send to peer if receive is false
        if PeerToPeerManager.shared.mcSession.connectedPeers.count > 0 {
            PeerToPeerManager.shared.sendTodo(new)
            debugPrint("send todo with Peer")
        }
    }
    
    
    func cartellaDocuments() -> String {
        let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory,.userDomainMask,true)
        //print(paths[0])
        return paths[0]
    }
}


在我的 PeerToPeerManager 类中,我处理响应“didReceive data”并将接收到的 todo 放入数组存储中。 存储是一个@Published 数组应该更新视图,但它不起作用..我找不到原因??

class PeerToPeerManager: NSObject,ObservableObject,MCAdvertiserAssistantDelegate {
    
    
    
    override init() {
        super.init()
        
       startPTPM()
    }
    
    
    static let shared = PeerToPeerManager()
        
    var peerID: MCPeerID!
    var mcSession: MCSession!
    var serviceAdvertiser : MCNearbyServiceAdvertiser!
    @Published var statusconnession = ""
    @Published var showReqActionSheet : Bool = false // attiva la finestra richiesta accetta o no
    @Published var requestPeer : MCPeerID!
    var acceptClosure : ((Bool,MCSession?) -> ())!
    
    @Published var debugSessionState = false
    @Published var debugStartHosting = false
    
    func startPTPM() {
        peerID = MCPeerID(displayName: UIDevice.current.name)
        mcSession = MCSession(peer: peerID,securityIdentity: nil,encryptionPreference: .required)
        debugPrint("SESSIONE INIZIATA \(mcSession.myPeerID)")
        debugSessionState.toggle()
        mcSession.delegate = self
    }
    
    func startHosting() {
        self.serviceAdvertiser = MCNearbyServiceAdvertiser(peer: peerID,discoveryInfo: nil,serviceType: "swiftu-simptod")
        self.serviceAdvertiser.delegate = self
        self.serviceAdvertiser.startAdvertisingPeer()
        debugStartHosting.toggle()
    }

    func sendTodo(_ todo:TodoModel) {
        
        if mcSession.connectedPeers.count > 0 {
            
            let encoder = PropertyListEncoder()
            encoder.outputFormat = .xml
            
            do {
                let data = try encoder.encode(todo)
                try mcSession.send(data,toPeers: mcSession.connectedPeers,with: .reliable)
            } catch {
                debugPrint(error.localizedDescription)
            }
        }
    }
    
    func advertiserAssistantwillPresentInvitation(_ advertiserAssistant: MCAdvertiserAssistant) {
        debugPrint("Presenta l'invito?")
    }

}


extension PeerToPeerManager: MCSessionDelegate {

    func session(_ session: MCSession,peer peerID: MCPeerID,didChange state: MCSessionState) {
        switch state {
            case MCSessionState.connected:
                print("Connesso: \(peerID.displayName)")
              
                
                    
                    dispatchQueue.main.async {
                        self.statusconnession = "CONnesSO a \(peerID)"
                    }
              
               
            case MCSessionState.connecting:
                print("Sto connettendo: \(peerID.displayName)")
                dispatchQueue.main.async {
                    self.statusconnession = "CONNECTING"
                }
                
            case MCSessionState.notConnected:
                print("Non connesso: \(peerID.displayName)")
                dispatchQueue.main.async {
                    self.statusconnession = "NON CONnesSO"
                }
              
            @unkNown default: break
        }
    }
    
    func session(_ session: MCSession,didReceive data: Data,fromPeer peerID: MCPeerID) {
        debugPrint("Ricevuta todo")
        
        do {
            let decoder = PropertyListDecoder()
            let todo = try decoder.decode(TodoModel.self,from: data)
            DataManager.shared.newTodo(name: todo.nome) {
                debugPrint("Ricevuta todo")
            }

        } catch {
            debugPrint(error.localizedDescription)
        }
    }
    
    func session(_ session: MCSession,didStartReceivingResourceWithName resourceName: String,fromPeer peerID: MCPeerID,with progress: Progress) {
        DataManager.shared.resource = resourceName
        DataManager.shared.isRefreshing = true
    }
    
    func session(_ session: MCSession,didFinishReceivingResourceWithName resourceName: String,at localURL: URL?,withError error: Error?) {
        DataManager.shared.isRefreshing = false
        DataManager.shared.resource = ""
    }
    
    func session(_ session: MCSession,didReceive stream: InputStream,withName streamName: String,fromPeer peerID: MCPeerID) {
        
    }
}

extension PeerToPeerManager: MCNearbyServiceAdvertiserDelegate {
    
    func advertiser(_ advertiser: MCNearbyServiceAdvertiser,didReceiveInvitationFromPeer peerID: MCPeerID,withContext context: Data?,invitationHandler: @escaping (Bool,MCSession?) -> Void) {
        
        acceptClosure = invitationHandler
        requestPeer = peerID
        showReqActionSheet = true
        
    }
    
    func accept() {
        self.acceptClosure(true,mcSession)
    }
    
    func rifiuta() {
        self.acceptClosure(false,nil)
    }
    
    func advertiser(_ advertiser: MCNearbyServiceAdvertiser,didNotStartAdvertisingPeer error: Error) {
        debugPrint("%@","didNotStartAdvertisingPeer: \(error)")
    }
    
}

这里是我的 ContentView

struct ContentView: View {
    @Observedobject var dm : DataManager // why not working??
    @Observedobject var ptpm : PeerToPeerManager  // why not working??
    
    @State var addItem = false
    @State var item: String = ""
    
    //peerstuff
    @State var isActionSheetShown = false //
    @State var isPeerChooserShown = false
    
    var body: some View {
        NavigationView{
            vstack(alignment:.leading) {
                HStack{
                    Text("Status sessionState")
                    Text("\(String(ptpm.debugSessionState))")
                }.padding()
                HStack{
                    Text("StartHosting")
                    Text("\(String(ptpm.debugStartHosting))")
                }.padding()
                HStack{
                    Text("Connession status")
                    Text("\(String(ptpm.statusconnession))")
                    
                }.padding()
                List{ // ------- WHY NOT UPDATING...??
                    ForEach(dm.storage) { item in
                        Text(item.nome)
                    }
                }.listStyle(PlainListStyle())
                if addItem {

                    HStack(alignment: .top) {
                        TextField("add here",text: $item)
                            .padding(.bottom,30)
                            .padding().textFieldStyle(RoundedBorderTextFieldStyle())
                        Button("Add") {
                            dm.newTodo(name: item) {
                                
                            }
                            
                        }.padding()
                    }
                    .background(Color.green.frame(height: 100,alignment: .center))


                }
            }
            .edgesIgnoringSafeArea(.bottom)
            .navigationBarTitle(Text("Todo"),displayMode: .inline)
            .navigationBarItems(leading: buttonPTP,trailing: buttonAdd )
            
            .actionSheet(isPresented: $ptpm.showReqActionSheet,content: {
                actionSheetConfirm
            })
            .sheet(isPresented: self.$isPeerChooserShown) {
                MultipeerbrowserView(dismissFlag: self.$isPeerChooserShown)
            }
        }
        
    }
    var buttonAdd: some View {
        Button(action: { withAnimation { self.addItem.toggle() } }) {
            Image(systemName: !addItem ? "plus.circle.fill" : "minus.circle.fill").imageScale(.large)
        }
    }
    
    // richiesta
    
    
    var buttonPTP: some View {
        Button(action: { self.isActionSheetShown = true }) {
            Image(systemName: "person.crop.circle.fill.badge.plus").imageScale(.large)
        }.actionSheet(isPresented: $isActionSheetShown) { actionSheet }
    }
    
    
    var actionSheet: ActionSheet {
        ActionSheet(title: Text("Connessione P2P"),message: Text("diventa host o connettiti ad un host"),buttons: [
                        /// # primo pulsante
                        .default(Text("diventa Host")) {
                            self.ptpm.startHosting()
                        },/// # secondo pulsante
                        .default(Text("Connettiti")) {
                            self.isPeerChooserShown = true // fa partire il browser di apple per selezionare utenti
                        },/// # pulsante Annulla
                        .cancel { debugPrint("Toccato: Annulla") }
            ]
        )
    }
    
    var actionSheetConfirm: ActionSheet {
        ActionSheet(title: Text("Richiesta di accesso da \(ptpm.requestPeer.displayName)"),message: Text("per lo scambio di todo tra device"),buttons: [
                        /// # primo pulsante
                        .default(Text("Accetta")) {
                            self.ptpm.accept()
                        },/// # pulsante Annulla
                        .cancel(Text("Annulla")) {
                            self.ptpm.rifiuta()
                        }
            ]
        )
    }
}

import SwiftUI

@main
struct createPeeerApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView(dm: DataManager(),ptpm: PeerToPeerManager())
                
        }
    }
}

问题: 我不明白为什么我的 swift 列表没有更新,因为 storage 是 @Published array ,如果我是发件人并将 todo 添加到 array 它会自动更新,但是当我从 didReceive data func 将数据附加到存储时它不会更新清单。

感谢帮助

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)