Due codici di errore spiegano la maggior parte dei primi tentativi con una VPN Network Extension su iOS: NEVPNErrorDomain Code=4 e Code=5. Nessuno dei due è una lamentela del server. Uno compare quando l’app scrive impostazioni in un oggetto che non è mai stato letto dal disco, l’altro quando la scrittura stessa fallisce, il più delle volte perché il profilo viene salvato mentre è ancora disattivato. Entrambi si correggono cambiando l’ordine di quattro chiamate, non toccando i parametri IKEv2.

Carica prima, altrimenti il salvataggio non ha con cosa confrontarsi

Il gestore conserva una copia del profilo salvato, e l’oggetto che modifichi non è quella copia finché non la leggi. Chiama prima loadFromPreferences, modifica dentro il suo handler di completamento, poi salva. Se salti la lettura, il framework vede una discrepanza tra ciò che ha e ciò che stai scrivendo. In Swift la forma è la stessa a ogni passo, e ogni passo appartiene al proprio closure:

let mgr = NEVPNManager.shared()
mgr.loadFromPreferences { loadErr in
    guard loadErr == nil else { return }
    let proto = NEVPNProtocolIKEv2()
    proto.serverAddress = "vpn.example.net"
    proto.username = "alice"
    proto.useExtendedAuthentication = true
    mgr.protocolConfiguration = proto
    mgr.isEnabled = true
    mgr.saveToPreferences { saveErr in
        // do not start here; reload, then connect
    }
}

Avviare il tunnel dall’interno del closure di salvataggio è lo stesso errore altrove. Viene eseguito prima che il profilo sia stato riletto, quindi la chiamata di avvio vede lo stato che hai costruito e non lo stato che è stato salvato. Il closure di salvataggio termina con un secondo loadFromPreferences, e solo quell’handler di completamento chiama startVPNTunnel.

Il flag di attivazione fa parte del salvataggio

Un profilo salvato è inerte finché non viene acceso. Imposta isEnabled = true nella stessa scrittura, altrimenti il profilo arriva su disco disattivato e startVPNTunnel rifiuta anche se Impostazioni mostra una configurazione che sembra completa.

Le modifiche esterne sono l’altra metà. Se l’utente cambia qualcosa in Impostazioni, o il dispositivo si sposta tra reti, rileggi prima di fidarti dei valori che tieni ancora in memoria. L’oggetto non osserva quei cambiamenti per te. Una copia obsoleta è il modo in cui un’app scrive un profilo che annulla silenziosamente la modifica dell’utente. Due campi adiacenti creano problemi silenziosi nello stesso momento: le regole on-demand devono essere un array di oggetti regola e non un segnaposto vuoto, e disconnectOnSleep deve riflettere il comportamento che hai scelto e non un valore predefinito copiato.

Solo due protocolli sono disponibili per le app di terze parti

Network Extension dà alle app IPsec e IKEv2. L2TP e PPTP restano dentro il sistema, quindi un’app che ne ha bisogno non può averli, e le risposte realistiche sono un gateway compatibile con IKEv2 o un profilo di configurazione gestito.

Le credenziali seguono lo stesso schema. Metti la password nel Keychain e passa un riferimento persistente invece della stringa. Un riferimento nullo al salvataggio di solito significa che la ricerca è avvenuta prima che l’elemento fosse scritto, non che il gestore sia rotto.

Entrambe sono decisioni di progetto e non soluzioni improvvisate. Un’app che tiene la password nelle proprie preferenze sceglie un percorso che il framework sconsiglia, e il guasto che produce si legge come un errore di salvataggio.

Escludi l’entitlement prima di riscrivere il codice

Quando loadFromPreferences non richiama mai, la causa probabile è una capacità Network Extension assente o un profilo di provisioning che non la porta. Quel guasto si blocca invece di dare errore, ed è per questo che codice funzionante viene riscritto per ore. Controlla l’entitlement sulla build firmata che intendi distribuire, perché un’esecuzione nel simulatore e una sul dispositivo possono differire e il target che conta è il dispositivo. Apri le impostazioni di firma del target e conferma che la capacità Network Extension sia presente per quella build esatta.

Un profilo di provisioning senza l’entitlement fallisce allo stesso modo sull’hardware, mentre la stessa build può caricarsi normalmente nel simulatore. Prova l’archivio firmato su un dispositivo reale prima di riscrivere l’ordine delle chiamate.

Osserva i cambi di stato, non le supposizioni

Osserva NEVPNStatusDidChangeNotification e registra manager.connection.status dopo ogni passo. Uno stato che non esce mai da invalid significa che il profilo non è stato salvato o che l’entitlement manca. Una connessione che arriva a connecting e non si connette mai punta alla proposta o alle credenziali, che è un’altra indagine con altre prove.

Conferma che il profilo compaia in Impostazioni, Generali, VPN, e registra l’errore di ogni closure. Questi due fatti separano un bug di ordine da un bug di permessi senza indovinare.

Articoli correlati