简体   繁体   中英

How to send a last message and close the socket when a SwiftUI view is dismissed?

I have a SwiftUI sheet view with an ObservedObject that handles tcp communication, when this sheet gets dismissed I need it to send a last tcp message and then close the socket. The onDisappear event never seems to get triggered ( Edit: found out the culprit its because I'm presenting the sheet using a UIHostingController , still need a solution ) I've tried putting it I the form, navigation view, tried creating a new stack for it, nothing worked. So I've tried using my ObservedObject deinit but this gives me a bad access error if I try to reopen the view fast after closing it.

deinit {
    let msg = getUpdatedTimersString()
    self.connection.sendMsg(msg, success: connection.close)
}

from my connection class that uses Network Framework

func sendMsg(_ message: String, success: @escaping () -> Void = { }, error: @escaping () -> Void = { }) {
        let msg = message + "\r\n"
        let data: Data? = msg.data(using: .utf8)
        debugPrint("Sending: \(msg)")
        connection.send(content: data, completion: .contentProcessed { (sendError) in
            if let sendError = sendError {
                self.debug("\(sendError)")
                error()
            } else {
                success()
            }
        })
}

func close() {
        connection.cancel()
}

Edit: adding the view code below

struct ScheduleView: View {
    @ObservedObject var scheduleManager = ScheduleManager() // This handles the tcp communication, the deinit you see above is from this

    var body: some View {
        NavigationView {
            Form {
                ForEach(scheduleManager.timers) { timer in
                    ScheduleForm(scheduleManager: self.scheduleManager, timer: timer).onDisappear { debugPrint("schedule form row disappeared") } // This is just a view that adds a section header and a DatePicker to the form for each timer
                }
            }.onDisappear { debugPrint("form disappeared") }

            .navigationBarTitle(Text("Schedule"), displayMode: .inline)
        }.onDisappear() { debugPrint("nav disappeared") }
    }
}

None of these onDisappear work for me, the one in the ScheduleForm rows is the only one that even triggers for me, but it triggers when the sheet is created and every time I scroll a row out of sight, but not when I dismiss the sheet.

Solution:

final class ScheduleController: UIHostingController<ScheduleView> {
    required init?(coder: NSCoder) {
        super.init(coder: coder, rootView: ScheduleView())
    }

    init() {
        super.init(rootView: ScheduleView())
    }
    override func viewWillDisappear(_ animated: Bool) {
        rootView.scheduleManager.updateTimers() // this sends the last message
    }

    override func viewDidDisappear(_ animated: Bool) {
        rootView.scheduleManager.connection.close // this closes the connection
    }
}

The first thing I did was change your function signature to use a single closure for success and error completion.

func sendMsg(_ message: String, completion: ((Bool) ->())? = nil)

Then we can use that inside the onDisappear modifier. On completion close the connection.

struct ContentView: View {

  var body: some View {

    Text("Hello World").onDisappear {
      sendMsg("last msg", completion: { completed in
        close()
      })
    }

  }
}

what I often do is something like this:

struct ScheduleView: View {

@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>

@ObservedObject var scheduleManager = ScheduleManager() 

var body: some View {
    NavigationView {
        Form {
            ForEach(scheduleManager.timers) { timer in
                ScheduleForm(scheduleManager: self.scheduleManager, timer: timer)
            }
        }
    }.onDisappear(perform: doExit)
     .navigationBarTitle(Text("Schedule"), displayMode: .inline)
}

func doExit() {

  // do your closing etc ... here

  // then to go back to the previous view
  self.presentationMode.wrappedValue.dismiss()
}

}

I have this very crude test working. A sheet is presented and when you swipe, onDisappear is triggered and perform the doExit function. Don't know anything about UIHostingController.

struct ContentView: View {
@State var testArray = ["one", "two", "three"]
@State var showMe = false

var body: some View {
    Button(action: { self.showMe = true}) {
        Text("Press button")
    }.sheet(isPresented: $showMe, onDismiss: {self.showMe = false}) { TestView(scheduleManager: self.testArray) }
}
}

struct TestView: View {
@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
@State var scheduleManager: [String]

var body: some View {
    NavigationView {
        Form {
            ForEach(scheduleManager, id: \.self) { timer in
                Text(timer)
            }
        }
    }.onDisappear(perform: doExit)
}

func doExit() {
    print("-----> TestView doExit")
    // then to go back to the previous view
    self.presentationMode.wrappedValue.dismiss()
}
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM