Can a Network Extension display any UI or launch it's container app?

We're looking to create a packet tunnel provider network extension. Inside this extension we will have some custom client code that runs to check a few things on the device prior to allowing the VPN to connect per enterprise policy. This code will be inside of the startTunnelWithOptions:completionHandler: block and execute prior to fully completing the connection, if something is wrong the connection will not be completed. The initial VPN profile and such will be installed during the first execution of the container app.

My question is around how we can relay to the user why the VPN connection was not allowed. This is an enterprise application where specific artifacts must be present prior to the VPN connection completing (OS version, WiFi vs. Celluar, etc). Our code inside startTunnelWithOptions:completionHandler: determines this. The VPN will be initiated on-demand or per-app, (it could be started from inside the container app but unlikely), therefore our container app may not be running at all when the VPN comes up but the extension will clearly be called when appropriate. This rule's out the use of handleAppMessage:completionHandler: to send some kind of message back to the user if the container app is closed (I thought this was the intention of the handleAppMessage API but maybe not). If the container app is not running this API will not work, correct? Even if it did work, it won't flip the user back to the container app, correct?

Our issues is that the container app provides a dashboard and we need to tell the user why the VPN connection was disallowed. The way I see it we could do this in two ways:

1) If it's allowed for an extension to show a UI elemetn (e.g., deprecated alertView, etc) initiated by the code inside startTunnelWithOptions, but it's unclear to me if we are able to do that as an extension. Idealy we would display a UIAlert window stating that the connection was disallowed and the user needs to open our app to see why.

2) We could call back to our container app via openURL, it would be launched if not running, and we could then have it display the dashboard along with the reason why the VPN connection was disallowed (sent in the openURL payload). The problem with this is that only Today extensions can do this per documentation "Each extension point determines whether to support this method, or under which conditions to support this method." "A Today widget (and no other app extension type) can ask the system to open its containing app by calling the

openURL:completionHandler:
method of the
NSExtensionContext
class. ". Obviously, there may be work arounds to launch the app still but its clearly not supported to do so as a Network Extension.

Any idea's how a network extension can display a simple message to the user in response to the VPN not starting? This would need to be displayed in any scenario that the VPN starts, via general->settings->VPN, ondemand, etc.

Thanks

Replies

There's a new displayMessage:completionHandler: method in NEProvider that might be useful.

mike.ly wrote:

There's a new

-displayMessage:completionHandler:
method in NEProvider that might be useful.

Indeed. I’d completely forgotten about that.

sdfijasdlfkjasd’s wrote:

1) If it's allowed for an extension to show a UI elemetn (e.g., deprecated alertView, etc) initiated by the code inside startTunnelWithOptions, but it's unclear to me if we are able to do that as an extension.

Network Extension provider extensions are definitely not allowed to display UI directly.

2) We could call back to our container app via openURL …

Yeah, that’s definitely not going to work. Code running in the background (like Network Extension provider extension) is not allowed to open a URL because it would switch the user’s foreground app out from underneath them.

I’m still researching whether it’s possible to use iOS 10’s new User Notification framework to post real notifications from an Network Extension provider extension. The good news is that, even if that doesn’t work out,

-displayMessage:completionHandler:
provides a minimum level of functionality for you.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Excellent, I had no idea this was in iOS 10. It should work as we're simply telling the user to open our app (to troubleshoot) if the connection was not completed.

Thanks for the help on this. I understand both points. displayMessage should work for what we're tyring to do! In the event we wanted to use notifications, are you thinking that normal notifications would not work and iOS 10's new mechanism would? I was under the impression that having our VPN client code schedule a notification to be sent to the device would be a last result to send them a message, but it sounds like that may not be possible and only possible under iOS 10's new mechanism?

In the event we wanted to use notifications, are you thinking that normal notifications would not work and iOS 10's new mechanism would?

The current UILocalNotification mechanism requires access to

+[UIApplication sharedApplication]
, which is not available to extensions (it’s tagged with
NS_EXTENSION_UNAVAILABLE_IOS
). In contrast, the entry point to the User Notification framework is
+[UNUserNotificationCenter currentNotificationCenter]
, which is available to extensions. So, I can’t see any reason why it wouldn’t work. OTOH, I don’t think anyone has tested it in the context of an Network Extension provider extension.

If you have time to test it yourself, please give it a whirl and let us know what you see.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

If you have time to test it yourself, please give it a whirl and let us know what you see.

After discussing this with the User Notification framework engineering team, the consensus is that this should work. I still haven’t had chance to sit down and test it myself though.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Hello, eskimo,


I tried NSUserNotification on mac, and it doesn't work.



class PacketTunnelProvider: NEPacketTunnelProvider, NSUserNotificationCenterDelegate {

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

....

let notification = NSUserNotification.init();

notification.title = "mactunnel";

notification.informativeText = "do body"

notification.soundName = NSUserNotificationDefaultSoundName

NSUserNotificationCenter.default.delegate = self

NSUserNotificationCenter.default.deliver(notification)

}


delegate



func userNotificationCenter(_ center: NSUserNotificationCenter, shouldPresent notification: NSUserNotification) -> Bool {

NSLog("should present")

return true

}

func userNotificationCenter(_ center: NSUserNotificationCenter, didDeliver notification: NSUserNotification) {

NSLog("did deliver")

}

func userNotificationCenter(_ center: NSUserNotificationCenter, didActivate notification: NSUserNotification) {

NSLog("did active")

}


Only "did deliver" is printed, but the notification is not delivered to the system notification panel, surprisely, no any error messages are printed any where.


I am using sierra, 10.12.1. MacBook Pro Retina Mid2012.


Same code works in container app context.


Any idea why it doesn't work? My use case is I want to prompt logon dialog for username and password, in case the container app is not running, and user start vpn from network preference.


displayMessage does work for me, but I am hoping more advanced user action like click a link to launch the app etc can be provided.


thanks!!

-Adam

I tried NSUserNotification on mac, and it doesn't work.

That’s not hugely surprising. The previous discussion on this thread was related to iOS, which has the new User Notification framework, which was specifically designed to support app extensions.

-displayMessage:completionHandler:
does work for me, but I am hoping more advanced user action like click a link to launch the app etc can be provided.

I don’t have any other clever ideas here. I recommend you file an enhancement request for the features you need. Please post your bug number, just for the record.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Hello, Eskimo,

Is there any plan to have NSUserNotification behavior same as iOS?

Thanks

-Adam

Is there any plan to have NSUserNotification behavior same as iOS?

I can’t answer questions about The Future™.

My general advice is that, if there’s something that you’d like to supported in the future, you should file an enhancement request describing your requirements. Please post your bug number, just for the record.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

29315971 Failed to send notification to notification center from network extension


Was duplicated to 22033452 that is still open

Eskimo wrote:-

The good news is that, even if that doesn’t work out,

-displayMessage:completionHandler:
provides a minimum level of functionality for you.


Hi Eskimo, We have a similar requirement where we want to display a message to the end user from container app when something happens in the plugin. I have tried adding this call in startProxyWithOptions (just to try) and also in handleNewFlow but both the cases I do not get the message in the UI. In startProxyWithOptions case I even have the container app running in the foreground. Completionhandler returns false for success which means the message was never sent. I'm using iOS 10.3, xcode 8.3. Is there any setting that is preventing the popup that I can turn on? I mean if there is a API to display message in the UI, it should work, I assume, so I'm probably missing something here...

Any clue/Thoughts?

Is there a reason you’re using

-displayMessage:completionHandler:
rather than UserNotification framework? The latter is obviously a better choice on platforms that support it.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Eskimo wrote:

Is there a reason you’re using

-displayMessage:completionHandler:


-Becuase it is not working, so I wanted to try displayMessage. Read Adam.ma's comment above on Feb 23 2017.

Ref:

29315971 Failed to send notification to notification center from network extension

Was duplicated to 22033452 that is still open

What sort of Network Extension provider are you working on? Here’s how it breaks down:

  • UserNotification framework notifications definitely work from an app proxy provider (I’ve written that code myself)

  • I’m pretty sure they work from a packet tunnel provider (other developers I’ve talked to have reported success with this)

  • They definitely do not work from a filter data provider (the strict sandbox on a filter data provider prevents this)

  • They should work from a filter control provider, although I don’t have any direct experience with that

Read Adam.ma's comment above on Feb 23 2017.

Ah, um, that comment is about

NSUserNotification
on the Mac, not UserNotification framework on iOS. macOS does not support UserNotification framework, which I why I wrote that “[UserNotification framework] is obviously a better choice on platforms that support it.”

Assuming that you’re working on an iOS app proxy provider, I recommend taht you take another shot at using UserNotification framework. If you can’t get it working, post back with more details (or open a DTS tech support incident so I can help you out directly).

Finally, here’s the code I used to post a notification from my app proxy provider.

override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
    NSLog("QNEPacketTunnel.Provider: app message %@", messageData as NSData)

    let content = UNMutableNotificationContent()
    content.title = "MyTitle"
    content.body = "\(Date())"

    let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5.0, repeats: false)

    // Create the request object.
    let request = UNNotificationRequest(identifier: "Hello Cruel World!", content: content, trigger: trigger)
    let center = UNUserNotificationCenter.current()
    center.add(request) { (error) in
        if let _ = error {
            NSLog("QNEPacketTunnel.Provider: did not schedule")
        } else {
            NSLog("QNEPacketTunnel.Provider: did schedule")
        }
    }

    completionHandler?(messageData)
}

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"