Din app siger afbrudt, menulinjen siger forbundet, og trafikken flyder tydeligt. Det gab er ikke en race condition, du kan rette med et nyt forsøg: appen og tunnelen er separate processer, og det objekt, din kode læser, er en cachelagret visning af en konfiguration, som systemet gemmer. Apples Personal VPN-flade er en kontrol-API, ikke en datasti, og det meste af dens mærkelige adfærd følger af netop det. Her er, hvad der faktisk sker ved hvert kald.

Managerobjektet er en cachelagret visning, ikke tunnelen

NETunnelProviderManager udfyldes fra et præferencelager, som systemet ejer, så dens egenskaber betyder intet, før loadFromPreferences er færdig. Læs connection.status før det, og du får en standardværdi, og derfor rapporterer den første statusopdatering efter en genstart afbrudt, mens tunnelen kører. Indlæs først, og læs derefter status og protokolkonfiguration fra den samme completion-handler. Overvåg senere ændringer via NEVPNStatusDidChange i stedet for at polle, og bemærk, at notifikationen også udløses for forbindelser, der startes af On Demand, hvor der slet ikke findes et appkald, der kan returnere noget. Det samme gælder protokolindstillinger: de læses fra den indlæste konfiguration, ikke fra det, appen sendte ind.

Selve tunnelen lever i en Network Extension-udbyderproces, så appen kan miste sin egen tilstand uden at miste forbindelsen. Alt, hvad UI’et viser, skal genopbygges fra manageren, ikke huskes fra den sidste session.

Startrækkefølge: gem, indlæs, start derefter

En udbyderbaseret forbindelse kræver tre trin i rækkefølge. Skriv udbyderkonfigurationen, og kald saveToPreferences, indlæs manageren, så den gemte konfiguration er den, du holder, og start derefter via NETunnelProviderSession.startVPNTunnel(). Springer du gemningen over, har starten intet at starte; indlæser du før gemningen, afspejler manageren stadig den gamle konfiguration.

Den boolske værdi, som startVPNTunnel returnerer, siger kun, at anmodningen blev accepteret. Fejl på udbyderniveau kommer senere som en statusændring til afbrudt, som du ser via notifikationen NEVPNStatusDidChange, og notifikationshandleren skal læse connection.status igen efter loadFromPreferences, fordi den cachelagrede værdi stadig kan være den gamle. Fejl på anmodningsniveau kommer straks tilbage fra den kastende form af kaldet eller via fejlud-parameteren i startVPNTunnelAndReturnError:. Kode, der behandler returværdien som resultatet, rapporterer en forbindelse, der aldrig blev etableret. Den anden tavse fejl er en udbyder-bundle-identifikator, der ikke matcher den installerede udvidelse: anmodningen afvises, og intet i meddelelsen peger på identifikatoren. Er identifikatoren forkert, fejler anmodningen normalt med det samme, før nogen loglinje fra udbyderen vises.

Genindlæsning af præferencer efter en start er det, der gør statusskærme korrekte. Manageren skubber ikke en ny konfiguration til dig, når systemet ændrer den.

Tillidsmateriale kommer via profil, aldrig fra appkode

En app har ingen understøttet måde at føje et rodcertifikat til iOS’ tillidslager, så en Personal VPN-konfiguration kan kun referere materiale, som enheden allerede stoler på: en kæde etableret af en installeret profil, af enhedsstyring eller af en CA, som systemet stoler på af andre grunde. En app, der danner sin egen CA ved første opstart og forventer, at systemet anerkender den, vil fejle valideringen inde i udbyderen, hvor appens UI ikke kan se årsagen.

Behandl den grænse som den designbegrænsning, den er, og lever tillidsankeret sammen med tilmeldingsprofilen. Hold VPN-godkendelse adskilt fra tillidsbeslutningen, fordi de fejler på forskellige steder og fejlfindes af forskellige folk.

Tællere kommer fra interfacet, ikke fra VPN-API’et

Manageren eksponerer status og konfiguration og intet, der tæller bytes pr. tunnel. Hvis du har brug for forbrugstal, så kald getifaddrs() og læs if_data for det relevante interface: en0 til Wi-Fi, pdp_ip-interfaces til mobilnet og en utun-enhed til selve tunnelen. utun-tællerne beskriver indre trafik og inkluderer protokoloverhead.

De tal er skøn, ikke regnskab pr. app, og de nulstilles, når interfacet genskabes. Mærk dem sådan i UI’et, ellers bruger nogen dem til at fakturere en kunde. Har du brug for tal pr. app, har du brug for en helt anden mekanisme, ikke en bedre aflæsning af disse felter.

Oprydning hører i stopVPNTunnel, ikke i afslutning

applicationWillTerminate giver dig et par sekunder, før systemet kan dræbe processen, og det er ikke et pålideligt sted at rive en forbindelse ned. Send stop til stopVPNTunnel(), eller håndtér det i udbyderens stop-callback, og behold den manager- eller konfigurationsidentifikator, du startede med, så stoppet rammer den forbindelse i stedet for hver konfiguration på enheden.

Gem den identifikator sammen med den profil, den hører til, da et managerobjekt ikke registrerer, hvilken konfiguration brugeren valgte. Og husk, at en forbindelse, der rejses af On Demand, slet ikke har noget appkald bag sig, så en statusskærm stadig skal indlæse præferencer, før den tegner noget.

Relaterede artikler