Ваша апликација каже прекинуто, трака менија каже повезано, а саобраћај очигледно тече. Та разлика није трка стања коју поправљате поновним покушајем: апликација и тунел су одвојени процеси, а објекат који ваш код чита је кеширани приказ конфигурације коју систем чува. Apple-ов Personal VPN интерфејс је контролни API, а не путања података, и већина његовог необичног понашања произлази из те једне чињенице. Ево шта се заиста дешава при сваком позиву.

Објекат менаџера је кеширани приказ, а не тунел

NETunnelProviderManager се попуњава из складишта поставки које поседује систем, па његова својства ништа не значе док loadFromPreferences не заврши. Ако прочитате connection.status пре тога, добијате подразумевану вредност, и зато прво освежавање статуса после поновног покретања пријављује прекинуто док је тунел активан. Прво учитајте, па прочитајте статус и конфигурацију протокола из истог руковаоца завршетка. Касније промене пратите кроз NEVPNStatusDidChange уместо периодичне провере и имајте на уму да се обавештење јавља и за везе које покреће On Demand, где не постоји никакав позив апликације који би нешто вратио. Исто важи и за поставке протокола: читају се из учитане конфигурације, а не из онога што је апликација послала.

Сам тунел живи у процесу провајдера Network Extension, па апликација може изгубити сопствено стање без губитка везе. Све што интерфејс приказује мора се поново изградити из менаџера, а не памтити из претходне сесије.

Редослед покретања: сачувај, учитај, па покрени

Веза заснована на провајдеру захтева три корака по реду. Запишите конфигурацију провајдера и позовите saveToPreferences, учитајте менаџера да сачувана конфигурација буде она коју држите, па покрените кроз NETunnelProviderSession.startVPNTunnel(). Ако прескочите чување, покретање нема шта да покрене; ако учитате пре чувања, менаџер и даље одражава стару конфигурацију.

Булова вредност коју враћа startVPNTunnel само каже да је захтев прихваћен. Грешке на нивоу провајдера долазе касније као промена статуса у прекинуто, што пратите кроз обавештење NEVPNStatusDidChange, а руковалац обавештења мора поново прочитати connection.status након loadFromPreferences јер кеширана вредност може још бити стара. Грешке на нивоу захтева враћају се одмах из облика позива који баца изузетак или кроз излазни параметар грешке од startVPNTunnelAndReturnError:. Код који враћену вредност третира као исход пријављује везу која никада није успостављена. Други тихи квар је идентификатор пакета провајдера који се не поклапа са инсталираним проширењем: захтев се одбија и ништа у поруци не упућује на идентификатор. Ако је идентификатор погрешан, захтев обично одмах пропада, пре него што се појави иједан ред у дневнику провајдера.

Поновно учитавање поставки након покретања је оно што чини екране статуса исправним. Менаџер вам не шаље нову конфигурацију када је систем промени.

Материјал поверења долази путем профила, никада из кода апликације

Апликација нема подржан начин да дода коренски сертификат у iOS складиште поверења, па Personal VPN конфигурација може референцирати само материјал коме уређај већ верује: ланац који је успоставио инсталирани профил, управљање уређајем или CA коме систем верује из других разлога. Апликација која при првом покретању створи сопствени CA и очекује да га систем прихвати пропашће на валидацији унутар провајдера, где кориснички интерфејс апликације не може видети разлог.

Третирајте ту границу као дизајнерско ограничење какво јесте и испоручите сидро поверења уз профил за упис. Држите VPN аутентификацију одвојено од одлуке о поверењу, јер оне пропадају на различитим местима и отклањају их различити људи.

Бројачи долазе са интерфејса, не из VPN API-ја

Менаџер излаже статус и конфигурацију и ништа што броји бајтове по тунелу. Ако су вам потребни бројеви потрошње, позовите getifaddrs() и прочитајте if_data за одговарајући интерфејс: en0 за Wi-Fi, интерфејсе pdp_ip за мобилну мрежу и utun уређај за сам тунел. Бројачи utun описују унутрашњи саобраћај и укључују трошак протокола.

Ови бројеви су процене, а не обрачун по апликацији, и ресетују се када се интерфејс поново створи. Означите их тако у интерфејсу, иначе ће их неко искористити да наплати купцу. Ако су вам потребни бројеви по апликацији, потребан вам је потпуно другачији механизам, а не боље читање ових поља.

Чишћење припада stopVPNTunnel, а не терминацији

applicationWillTerminate вам даје неколико секунди пре него што систем може убити процес, што није поуздано место за рушење везе. Пошаљите заустављање на stopVPNTunnel() или га обрадите у повратном позиву за заустављање провајдера, и задржите идентификатор менаџера или конфигурације са којим сте почели, да заустављање циља ту везу уместо сваке конфигурације на уређају.

Сачувајте тај идентификатор уз профил коме припада, јер објекат менаџера не бележи коју је конфигурацију корисник изабрао. И запамтите да веза коју подигне On Demand нема никакав позив апликације иза себе, па екран статуса и даље мора учитати поставке пре него што ишта нацрта.

Повезани чланци