هل يفشل حفظ NEVPNManager؟ الترتيب هو التحميل فالتمكين فالحفظ فالاتصال
الرمز 4 والرمز 5 يعنيان عادةً أن الملف الشخصي عُدِّل قبل تحميله أو حُفظ وهو معطّل. أصلح ترتيب الاستدعاء، ثم تحقق من الاستحقاق.
يُفسّر رمزا خطأ معظم المحاولات الأولى لإعداد VPN من نوع Network Extension على 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، وسجّل الخطأ من كل إغلاق. هاتان الحقيقتان تفصلان خطأ الترتيب عن خطأ الأذونات دون تخمين.