How can I use inter process communication?

Hi, I am currently building my own VPN application using NetworkExtension's PacketTunnelProvider.

I want to send information from the PacketTunnelProvider to the ViewController when a VPN connection fails and to tell the user why.

The code now is as shown below. When the startTunnel() being overwritten is executed, somehow NotificationCenter.default.post(name: NSNotification.Name.NEVPNStatusDidChange, object: nil) is executed and VPNStatusDidChange(_ notification: Notification?) in the ViewController is called and displays some message.

I tried to do the same thing by writing NotificationCenter.default.post(name: NSNotification.Name(rawValue: "testnotify"), object: nil) in the PacketTunnelProvider.swift , but it does not work. What is wrong?

Here is a part of current PacketTunnelProvider.swift

override func startTunnel(options: [String : NSObject]? = nil, completionHandler: @escaping (Error?) -> Void) {

  conf = (self.protocolConfiguration as! NETunnelProviderProtocol).providerConfiguration! as [String : AnyObject]
  self.setupWSSession()
   
  DispatchQueue.global().async {
    while (self.connectionPhase < 5) {
      Thread.sleep(forTimeInterval: 0.5)
    }
    self.tunToWS()
  }
  NotificationCenter.default.post(name: NSNotification.Name(rawValue: "testnotify"), object: nil)
}

And here is a part of ViewController.swift

override func viewDidLoad() {
    super.viewDidLoad()
    initVPNTunnelProviderManager()
    NotificationCenter.default.addObserver(self, selector: #selector(ViewController.VPNStatusDidChange(_:)), name: NSNotification.Name.NEVPNStatusDidChange, object: nil)
    
    NotificationCenter.default.addObserver(self, selector: #selector(ViewController.receieve(_:)), name: NSNotification.Name(rawValue: "testnotify"), object: nil)
   
}


@objc func VPNStatusDidChange(_ notification: Notification?) {
  print("VPN Status changed:")
  let status = self.vpnManager.connection.status
  switch status {
  case .connecting:
    print("Connecting...")
    connectButton.setTitle("Disconnect", for: .normal)
    break
  case .connected:
    print("Connected...")
    connectButton.setTitle("Disconnect", for: .normal)
    break
  case .disconnecting:
    print("Disconnecting...")
    break
  case .disconnected:
    print("Disconnected...")
    connectButton.setTitle("Connect", for: .normal)
    break
  case .invalid:
    print("Invliad")
    break
  case .reasserting:
    print("Reasserting...")
    break
  }
}


@objc func receive(_ notification: Notification?)  {
    print("receive Notification!")
}

Accepted Reply

I would like to know how to change the NETunnelProviderManager.connection.status available in the Container APP from the PacketTunnelProvider side.

That happens automatically based on the state of the provider that’s visible to the system, for example, whether it’s called your start-tunnel method and whether you’ve then called that method’s completion handler. If you want to provide more detailed information, you’ll need to build that on top of an IPC mechanism.

If possible, I would like to know how to communicate with PacketTunnelProvider without calling sendProviderMessage(_:responseHandler:) on the Container APP side.

In situations like this I usually have the container app always call sendProviderMessage(_:responseHandler:) with a get-latest-status request. The provider then holds on to its completion handler and, the next time the status changes, the provider completes the request with that status.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

  • Thank you so much eskimo. Thanks to your answers, I will be able to do what I want to do!

Add a Comment

Replies

I am currently building my own VPN application

Let’s start with some basics:

  • What platform are you targeting?

  • And if it’s macOS, are you building an app extension or a system extension?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

  • Thanks for your reply eskimo.

    My target platform is iOS. And I am using network extension (packet tunnel provider).

    I would like to know how startTunnel() and stopTunnel(), which exist in the NEPacketTunnelProvider, use NotificationCenter to send messages to the ViewController.

    Kind regards

Add a Comment

My target platform is iOS.

OK, that simplifies this considerably because your IPC options on iOS are very limited.

I would like to know how startTunnel() and stopTunnel(), which exist in the NEPacketTunnelProvider, use NotificationCenter to send messages to the ViewController.

You can’t use NotificationCenter for this because your packet tunnel provider and your view controller are running in separate processes.

What sort of notifications your trying to send? If this is simple start/stop notifications, code in your app can observe those using the connection property of your NEVPNManager instance (for a packet tunnel this is actually of type NETunnelProviderSession). This has a property status and you can observe the NEVPNStatusDidChangeNotification notification to hear about changes.

If you need something more complicated than that, build it on top of the provider message infrastructure, that is:

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Thank you very much eskimo. 

By calling sendProviderMessage(_:responseHandler:) on the Container APP side, I was able to communicate with PacketTunnelProvider's handleAppMessage(_:completionHandler:).

If possible, I would like to know how to communicate with PacketTunnelProvider without calling sendProviderMessage(_:responseHandler:) on the Container APP side. I want to tell the user what stage of the connection process they are currently in and the detailed reasons for any errors that occur.

If we use the connection property of the NEVPNManager instance for this purpose, I would like to know how to change the NETunnelProviderManager.connection.status available in the Container APP from the PacketTunnelProvider side. And I would like to know how to add my own connection status.

sincerely

I would like to know how to change the NETunnelProviderManager.connection.status available in the Container APP from the PacketTunnelProvider side.

That happens automatically based on the state of the provider that’s visible to the system, for example, whether it’s called your start-tunnel method and whether you’ve then called that method’s completion handler. If you want to provide more detailed information, you’ll need to build that on top of an IPC mechanism.

If possible, I would like to know how to communicate with PacketTunnelProvider without calling sendProviderMessage(_:responseHandler:) on the Container APP side.

In situations like this I usually have the container app always call sendProviderMessage(_:responseHandler:) with a get-latest-status request. The provider then holds on to its completion handler and, the next time the status changes, the provider completes the request with that status.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

  • Thank you so much eskimo. Thanks to your answers, I will be able to do what I want to do!

Add a Comment