NEVPNManager не сохраняет? Порядок: загрузить, включить, сохранить, подключиться
Коды 4 и 5 обычно означают, что профиль изменили до загрузки или сохранили, пока он был отключён. Исправьте порядок вызовов, затем проверьте право.
Два кода ошибки объясняют большинство первых попыток с Network Extension VPN на iOS: NEVPNErrorDomain Code=4 и Code=5. Ни один не является жалобой сервера. Один появляется, когда приложение записывает настройки в объект, который ни разу не читался с диска, а второй — когда сама запись не удаётся, чаще всего потому, что профиль сохраняется, пока он ещё отключён. Оба исправляются изменением порядка четырёх вызовов, а не правкой параметров IKEv2.
Сначала загрузите, иначе сохранению не с чем сравнивать
Менеджер хранит копию сохранённого профиля, и объект, который вы редактируете, не является этой копией, пока вы её не прочитаете. Сначала вызовите loadFromPreferences, редактируйте внутри его обработчика завершения, затем сохраните. Если пропустить чтение, фреймворк увидит расхождение между тем, что у него есть, и тем, что вы пишете. В Swift форма одинакова на каждом шаге, и каждый шаг принадлежит своему замыканию:
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
}
}
Запуск туннеля изнутри замыкания сохранения — та же ошибка в другом месте. Он выполняется до повторного чтения профиля, поэтому вызов запуска видит состояние, которое вы построили, а не состояние, которое было сохранено. Замыкание сохранения завершается вторым loadFromPreferences, и только этот обработчик завершения вызывает startVPNTunnel.
Флаг включения — часть сохранения
Сохранённый профиль бездействует, пока его не включат. Задайте isEnabled = true в той же записи, иначе профиль попадёт на диск отключённым, и startVPNTunnel откажет, даже если Настройки показывают выглядящую полной конфигурацию.
Внешние изменения — вторая половина. Если пользователь что-то меняет в Настройках или устройство переходит между сетями, перечитайте, прежде чем доверять значениям, которые вы всё ещё держите в памяти. Объект не отслеживает эти изменения за вас. Устаревшая копия — это способ, которым приложение записывает профиль, молча откатывающий правку пользователя. Два соседних поля создают тихую проблему в один и тот же момент: правила on-demand должны быть массивом объектов правил, а не пустой заглушкой, а disconnectOnSleep должен отражать выбранное вами поведение, а не скопированное значение по умолчанию.
Сторонним приложениям доступны только два протокола
Network Extension даёт приложениям IPsec и IKEv2. L2TP и PPTP остаются внутри системы, поэтому приложение, которому они нужны, не может их получить, и реалистичные ответы — шлюз с поддержкой IKEv2 или управляемый профиль конфигурации.
Учётные данные следуют той же схеме. Положите пароль в Keychain и передайте постоянную ссылку вместо строки. Нулевая ссылка при сохранении обычно означает, что поиск прошёл до записи элемента, а не что менеджер сломан.
И то и другое — решения проектирования, а не обходные пути. Приложение, которое держит пароль в собственных настройках, выбирает путь, который фреймворк не одобряет, и его сбой читается как ошибка сохранения.
Исключите право, прежде чем переписывать код
Когда loadFromPreferences вообще не вызывает обратный вызов, вероятная причина — отсутствующая возможность Network Extension или профиль подготовки, который её не содержит. Этот сбой зависает вместо выдачи ошибки, и поэтому работающий код переписывают часами. Проверьте право в подписанной сборке, которую вы собираетесь выпускать, потому что запуск в симуляторе и на устройстве может отличаться, а важная цель — устройство. Откройте настройки подписи цели и подтвердите, что возможность Network Extension присутствует именно для этой сборки.
Профиль подготовки без права сбоит так же на оборудовании, тогда как та же сборка может нормально загружаться в симуляторе. Проверьте подписанный архив на реальном устройстве, прежде чем переписывать порядок вызовов.
Следите за изменениями состояния, а не за предположениями
Наблюдайте за NEVPNStatusDidChangeNotification и записывайте manager.connection.status после каждого шага. Состояние, которое никогда не покидает invalid, означает, что профиль не сохранён или права нет. Соединение, которое доходит до connecting и никогда не подключается, указывает на предложение или учётные данные, и это другое расследование с другими доказательствами.
Подтвердите, что профиль появляется в разделе Настройки, Основные, VPN, и запишите ошибку из каждого замыкания. Эти два факта без догадок отделяют ошибку порядка от ошибки прав.