A túa app di desconectado, a barra de menús di conectado e o tráfico claramente flúe. Esa fenda non é unha condición de carreira que arranxes cun reintento: a app e o túnel son procesos separados, e o obxecto que le o teu código é unha vista na caché dunha configuración que garda o sistema. A superficie Personal VPN de Apple é unha API de control, non unha ruta de datos, e a maior parte do seu comportamento estraño derivase dese único feito. Velaquí o que ocorre realmente en cada chamada.

O obxecto xestor é unha vista na caché, non o túnel

NETunnelProviderManager énchese desde un almacén de preferencias que posúe o sistema, así que as súas propiedades non significan nada ata que loadFromPreferences remata. Le connection.status antes diso e obterás un valor predeterminado, e por iso a primeira actualización de estado tras reiniciar informa desconectado mentres o túnel está activo. Carga primeiro e despois le o estado e a configuración do protocolo desde o mesmo manexador de finalización. Observa os cambios posteriores mediante NEVPNStatusDidChange en lugar de sondar, e ten en conta que a notificación tamén se dispara para conexións iniciadas por On Demand, onde non existe ningunha chamada da app que poida devolver algo. O mesmo vale para os axustes do protocolo: lense da configuración cargada, non do que a app pasou.

O túnel vive nun proceso provedor de Network Extension, así que a app pode perder o seu propio estado sen perder a conexión. Todo o que mostra a interface hai que reconstruílo desde o xestor, non recordalo da sesión anterior.

Orde de inicio: garda, carga e despois inicia

Unha conexión baseada en provedor necesita tres pasos en orde. Escribe a configuración do provedor e chama a saveToPreferences, carga o xestor para que a configuración gardada sexa a que tes na man e despois inicia mediante NETunnelProviderSession.startVPNTunnel(). Se saltas o gardado, o inicio non ten nada que lanzar; se cargas antes de gardar, o xestor segue reflectindo a configuración antiga.

O booleano que devolve startVPNTunnel só di que a solicitude foi aceptada. Os fallos a nivel de provedor chegan despois como un cambio de estado a desconectado, que observas mediante a notificación NEVPNStatusDidChange, e o manexador da notificación ten que reler connection.status despois de loadFromPreferences porque o valor na caché pode seguir sendo o antigo. Os fallos a nivel de solicitude volven de inmediato desde a forma que lanza excepcións da chamada, ou mediante o parámetro de saída de erro de startVPNTunnelAndReturnError:. O código que trata o valor de retorno como o resultado informa dunha conexión que nunca se estableceu. O outro fallo silencioso é un identificador de paquete do provedor que non coincide coa extensión instalada: a solicitude rexéitase e nada da mensaxe apunta ao identificador. Se o identificador é incorrecto, a solicitude adoita fallar de inmediato, antes de que apareza ningunha liña de rexistro do provedor.

Recargar as preferencias despois dun inicio é o que fai correctas as pantallas de estado. O xestor non che empurra unha configuración nova cando o sistema a cambia.

O material de confianza chega por perfil, nunca desde o código da app

Unha app non ten forma soportada de engadir un certificado raíz ao almacén de confianza de iOS, así que unha configuración de Personal VPN só pode referenciar material no que o dispositivo xa confía: unha cadea establecida por un perfil instalado, pola xestión do dispositivo ou por unha CA na que o sistema confía por outros motivos. Unha app que xera a súa propia CA no primeiro arranque e espera que o sistema a acepte fallará a validación dentro do provedor, onde a interface da app non pode ver o motivo.

Trata ese límite como a restrición de deseño que é e entrega a áncora de confianza xunto co perfil de inscrición. Mantén a autenticación VPN separada da decisión de confianza, porque fallan en sitios distintos e depúranas persoas distintas.

Os contadores veñen da interface, non da API de VPN

O xestor expón estado e configuración, e nada que conte bytes por túnel. Se necesitas cifras de uso, chama a getifaddrs() e le if_data da interface correspondente: en0 para Wi-Fi, as interfaces pdp_ip para móbil e un dispositivo utun para o propio túnel. Os contadores de utun describen o tráfico interno e inclúen a sobrecarga do protocolo.

Esas cifras son estimacións, non contabilidade por app, e reinícianse cando se recrea a interface. Etiquétaas así na interface, ou alguén as usará para facturar a un cliente. Se necesitas cifras por app, necesitas un mecanismo completamente distinto, non unha mellor lectura destes campos.

A limpeza vai en stopVPNTunnel, non na terminación

applicationWillTerminate dache uns segundos antes de que o sistema poida matar o proceso, o que non é un sitio fiable para desmontar unha conexión. Envía a parada a stopVPNTunnel() ou xestiónaa no callback de parada do provedor, e conserva o identificador do xestor ou da configuración co que iniciaste para que a parada apunte a esa conexión e non a todas as configuracións do dispositivo.

Garda ese identificador xunto ao perfil ao que pertence, xa que un obxecto xestor non rexistra que configuración seleccionou o usuario. E lembra que unha conexión levantada por On Demand non ten ningunha chamada de app detrás, así que unha pantalla de estado segue tendo que cargar as preferencias antes de debuxar nada.

Artigos relacionados