الترحيل إلى Swift من Objective-C: دليل شامل 2026

الترحيل إلى Swift من Objective-C لا يتطلب إعادة كتابة تطبيق iOS بالكامل. يمكن تشغيل اللغتين داخل المشروع نفسه، ونقل وحدات محددة مع الحفاظ على الإصدارات المعتادة. البداية الأفضل هي تدقيق التبعيات، وتثبيت السلوك باختبارات، ثم تجربة تحويل محدودة قبل توسيع النطاق.

يوضح هذا الدليل كيفية اتخاذ قرار الترحيل في 2026، وإعداد التوافق بين اللغتين في Xcode، والتعامل مع الأنواع والذاكرة والتزامن، وقياس نجاح كل مرحلة. المقصود هنا لغة البرمجة Swift، وليس شبكة SWIFT المصرفية.

هل الترحيل إلى Swift ضروري لكل تطبيق Objective-C؟

لا. وجود Objective-C في التطبيق ليس وحده سبباً لإعادة كتابته، ولا يعني أن التطبيق فقد التوافق مع iOS. توثّق Apple آليات استخدام Objective-C وSwift معاً، ويمكن إبقاء أجزاء مستقرة من الكود القديم مع تطوير أجزاء جديدة بلغة Swift. القرار الصحيح يرتبط بمشكلات الصيانة وخطة المنتج، لا بعمر اللغة وحده.

تزداد جدوى الترحيل عندما تتكرر التعديلات على وحدات يصعب اختبارها، أو يحتاج الفريق إلى استخدام واجهات وأدوات تعتمد على Swift، أو تصبح حدود الكود القديم عائقاً أمام تطوير الميزات. في المقابل، قد تكون صيانة وحدة مستقرة قليلة التغيير أقل تكلفة ومخاطرة من تحويلها.

ما المكاسب الواقعية من Swift؟

توفّر Swift نظام أنواع يساعد على كشف أخطاء أثناء الترجمة، وOptionals لتمثيل غياب القيم صراحة، وأنواع قيم مثل structs، وأدوات للتزامن مثل async/await وactors. الاستفادة من هذه الميزات تحتاج إلى تصميم مناسب؛ فالتحويل الحرفي للكود لا يضمن تحسين البنية.

كذلك لا تمنع Swift جميع الانهيارات. استخدام force unwrap مع قيمة nil، أو الوصول إلى فهرس خارج حدود المصفوفة، قد يؤدي إلى توقف التنفيذ. وتظل دورات الاحتفاظ بالمراجع ممكنة تحت ARC، كما تتطلب المؤشرات غير الآمنة والتعامل مع واجهات C عناية خاصة.

الأداء ليس نتيجة مضمونة للترحيل. قد تتحسن عمليات معينة بفعل تحسينات المترجم، بينما لا يتغير أداء أجزاء تعتمد أساساً على الشبكة أو واجهات النظام. قارن زمن التشغيل واستهلاك الذاكرة في إعدادات Release وعلى أجهزة مستهدفة بدلاً من الاعتماد على أرقام تفوق عامة.

متى يكون تأجيل الترحيل أفضل؟

  • عندما تعمل الوحدة باستقرار ولا تعوق متطلبات المنتج الحالية.
  • عندما يفتقر المشروع إلى اختبارات للمسارات الحساسة؛ ابدأ ببناء شبكة الأمان.
  • عندما تعتمد الوحدة بكثافة على runtime الخاص بـ Objective-C، ويحتاج استبدال سلوكها الديناميكي إلى دراسة منفصلة.
  • عندما تكون الأولوية إصلاح عطل إنتاجي أو مشكلة أمنية لا تتطلب تغيير اللغة.

قد يكون المشروع المختلط حلاً طويل الأجل مقبولاً. المطلوب هو حدود واضحة ومسؤولية صيانة محددة، وليس الوصول إلى نسبة Swift كاملة بأي ثمن.

اختيار استراتيجية الترحيل إلى Swift

الترحيل التدريجي: تحويل وحدات قابلة للعزل

في هذا النهج، تُحوّل خدمة أو شاشة أو مكوّن محدد، ثم تختبره وتطلقه قبل الانتقال إلى الجزء التالي. يتيح ذلك استمرار تطوير المنتج، ويسهّل تحديد مصدر الانحدارات. وتشرح Apple الأساس التقني لهذا التشغيل المختلط في دليل استيراد Objective-C إلى Swift.

التكلفة المقابلة هي إدارة واجهات بين لغتين، ومراجعة طريقة انتقال الأنواع والأخطاء والمراجع عبرها. لذلك ينجح النهج التدريجي عندما تكون دفعاته صغيرة وحدوده واضحة، لا عندما تتحول كل دالة إلى نقطة عبور بين اللغتين.

إعادة الكتابة الكاملة: قرار يحتاج مبرراً مستقلاً

قد تناسب إعادة الكتابة تطبيقاً محدود الوظائف يمكن توصيف سلوكه واختباره بالكامل، أو منتجاً تتغير متطلباته جذرياً. لكنها قد تعيد إدخال أخطاء سبق إصلاحها، وتتطلب استعادة تفاصيل غير موثقة مثل حالات انقطاع الشبكة وسلوك ترقية البيانات.

لا توجد عتبة موثوقة لعدد الأسطر تجعل إعادة الكتابة آمنة تلقائياً. تعقيد التبعيات، وحساسية البيانات، وسرعة تغير المتطلبات عوامل أكثر دلالة من الحجم وحده.

المعيارالترحيل التدريجيإعادة الكتابة الكاملة
الإصدارات أثناء العمليمكن استمرارها مع دفعات صغيرةتحتاج خطة لإدارة المنتج الحالي والبديل
التحقق من السلوكعلى مستوى الوحدة ثم التكاملعلى مستوى التطبيق بأكمله
التراجع عن التغييرأسهل إذا ظلت العقود والبيانات متوافقةأصعب عند تغيير البنية وتنسيقات البيانات
التكلفة الإضافيةواجهات توافق وصيانة مشروع مختلطإعادة اكتشاف السلوك وتكافؤ الميزات

تدقيق التطبيق قبل بدء التحويل

هدف التدقيق هو معرفة ما يجب الحفاظ عليه وما يمكن تغييره بأمان. لا تبدأ بتحويل الملفات وفق ترتيبها في المشروع؛ ارسم أولاً خريطة الوحدات ومن يستدعيها وما تعتمد عليه.

جرد التبعيات والسلوك الديناميكي

  • المكتبات الخارجية: افحص الصيانة والترخيص والتوافق مع إصدار Xcode المستهدف. لا تستبدل مكتبة مستقرة لمجرد أنها مكتوبة بـ Objective-C.
  • التخزين: وثّق Core Data، والأرشفة، وUserDefaults، وأسماء الأنواع المخزنة إن وجدت.
  • السلوك الديناميكي: احصر selectors وKVC وKVO وmethod swizzling وإنشاء الفئات باستخدام أسمائها النصية.
  • واجهات المستخدم: راجع Storyboards وXIBs وIBOutlet وIBAction والروابط بأسماء الفئات.
  • البناء: سجّل targets وframeworks وإعدادات CI والحد الأدنى لنظام التشغيل.

تغيير مدير الحزم من CocoaPods إلى Swift Package Manager ليس شرطاً للترحيل. افصل هذا التغيير عن تحويل اللغة ما لم توجد حاجة واضحة، حتى لا تتداخل أسباب فشل البناء.

اختيار أول وحدة: العزل أهم من نوع الملف

اختر مكوّناً له مدخلات ومخرجات واضحة، وتبعيات محدودة، واختبارات يمكن تشغيلها سريعاً. قد يكون أداة تنسيق أو جزءاً من منطق الأعمال أو ميزة جديدة خلف واجهة مستقرة.

لا تفترض أن نماذج البيانات أقل مخاطرة دائماً. نموذج تستخدمه معظم طبقات التطبيق قد يكون أصعب في الترحيل من شاشة معزولة، خصوصاً إذا كان مشاركاً في الأرشفة أو التخزين. رتّب الوحدات بحسب أثر التغيير، لا بحسب قاعدة ثابتة من الأسفل إلى الأعلى.

تحديد خط أساس للقياس

قبل التعديل، سجّل نتائج الاختبارات، ومؤشرات الأعطال، وزمن تشغيل المسارات المهمة، واستهلاك الذاكرة، وزمن البناء إذا كان يمثل مشكلة للفريق. استخدم الأجهزة وسيناريوهات القياس نفسها بعد الترحيل لتصبح المقارنة مفيدة.

إعداد التوافق بين Objective-C وSwift في Xcode

التوافق يعمل في اتجاهين، ولكل اتجاه آلية مختلفة. الأمثلة التالية تخص app target مختلطاً؛ أما frameworks فلها متطلبات إضافية تتعلق بالوحدات وإتاحة الواجهات العامة.

استدعاء Objective-C من Swift باستخدام Bridging Header

عند إضافة ملف Swift إلى target يحتوي على Objective-C، قد يعرض Xcode إنشاء Bridging Header. إن أنشأته يدوياً، اضبط مساره في إعداد Objective-C Bridging Header الخاص بالـ target.

ضع فيه استيرادات الرؤوس التي يحتاجها Swift فقط:

// App-Bridging-Header.h
#import "LegacyPriceFormatter.h"

بعد ذلك تصبح الواجهات المستوردة متاحة لملفات Swift في الـ target نفسه. إبقاء هذا الملف صغيراً يقلل الارتباط غير الضروري ويساعد على فهم حدود الترحيل. في framework، تُتاح واجهات Objective-C عادة عبر الرؤوس العامة وumbrella header للوحدة، بدلاً من افتراض تطبيق إعداد التطبيق نفسه.

استدعاء Swift من Objective-C باستخدام الرأس المولّد

يولّد Xcode رأساً لواجهات Swift القابلة للتمثيل في Objective-C. اسمه الافتراضي مشتق من اسم وحدة المنتج بالشكل ProductModuleName-Swift.h، ويمكن أن يتأثر بإعدادات البناء. لا تنشئ هذا الملف أو تعدّله يدوياً.

مثال لفئة Swift بسيطة مكشوفة إلى Objective-C:

import Foundation

@objc final class PriceFormatter: NSObject {
    @objc func label(for amount: NSNumber) -> String {
        return amount.stringValue
    }
}

وفي ملف تنفيذ Objective-C، بافتراض أن اسم الوحدة هو ShopApp:

#import "ShopApp-Swift.h"

PriceFormatter *formatter = [[PriceFormatter alloc] init];
NSString *label = [formatter labelFor:@(42)];

توضح الوراثة من NSObject ووسوم @objc هنا الواجهة المطلوبة صراحة. الوراثة وحدها لا تعني ظهور كل عضو في الرأس المولّد، ووسم @objc لا يجعل أي نوع Swift قابلاً للتصدير. راجع التفاصيل في دليل Apple لاستيراد Swift إلى Objective-C، خاصة عند العمل داخل framework حيث تؤثر مستويات الوصول أيضاً.

تجنّب دورات الاستيراد

استورد الرأس المولّد في ملفات .m عندما يكون ذلك ممكناً، واستخدم التصريح المسبق @class في رؤوس Objective-C التي تحتاج إلى الإشارة إلى فئة Swift دون تفاصيل تنفيذها. لا تضف الرأس المولّد إلى Bridging Header؛ فهذا قد يؤدي إلى اعتماد دائري.

تحديات الأنواع والذاكرة عند الترحيل

Nullability وOptionals

راجع دلالات nullable وnonnull في رؤوس Objective-C قبل تحويل المستهلكين إلى Swift. غياب هذه المعلومات قد يجعل التعامل مع القيم المستوردة أقل وضوحاً، ويشجع على استخدام force unwrap لإسكات أخطاء المترجم بدلاً من معالجة الحالات الفعلية.

NS_ASSUME_NONNULL_BEGIN

@interface LegacyUser : NSObject
@property (nonatomic, copy) NSString *identifier;
@property (nonatomic, copy, nullable) NSString *displayName;
@end

NS_ASSUME_NONNULL_END

يعبّر هذا العقد عن معرّف مطلوب واسم عرض قد يغيب. لكن التعليقات النوعية لا تصلح التنفيذ تلقائياً: تحقّق من أن الكود القديم لا يعيد nil حيث يعد بقيمة غير فارغة. وفي Swift، استخدم if let أو guard let أو قيمة بديلة مناسبة للمعنى، لا قيمة افتراضية تخفي خللاً.

الأنواع العامة وstructs وenums

يمكن تحسين استيراد مجموعات Objective-C باستخدام lightweight generics، مثل تحديد نوع عناصر NSArray. في الاتجاه المقابل، لا يمكن كشف structs أو enums ذات القيم المرتبطة ومعظم الواجهات العامة الخاصة بـ Swift مباشرة إلى Objective-C.

الحل العملي هو واجهة توافق صغيرة تستخدم أنواعاً قابلة للجسر، بينما يبقى التنفيذ الداخلي حراً في استخدام خصائص Swift. لا تحوّل كل نموذج إلى NSObject لإرضاء مستهلك واحد، ولا تنقل كل نماذج المشروع إلى structs قبل التأكد من أن المستهلكين لا يعتمدون على هوية الكائن وتعديله بالمشاركة.

ARC وclosures ودورات الاحتفاظ

تستخدم Swift وObjective-C إدارة المراجع التلقائية ARC، لذلك لا تختفي تسريبات الذاكرة بمجرد تغيير اللغة. راجع closures المخزنة، والمؤقتات، والمراقبين، وdelegates، وأي علاقة يحتفظ فيها كائنان ببعضهما.

استخدم weak عندما تسمح دلالة العلاقة بزوال الكائن، ولا تستخدم unowned إلا إذا كان العمر المتوقع مضموناً؛ فالوصول إلى مرجع unowned بعد زوال الكائن قد يؤدي إلى انهيار. قِس النتائج باستخدام Memory Graph وInstruments بدلاً من افتراض أن جميع المراجع تحتاج إلى weak.

الأخطاء وحدود الاستدعاء

بعض أنماط NSError في Objective-C تُستورد إلى Swift كدوال throws. راجع التوقيع الذي يراه Swift فعلياً، وحافظ على معنى الأخطاء ومعلوماتها التي يعتمد عليها المستهلكون. وإذا أبقيت واجهة completion handler، وثّق الخيط أو queue الذي تُستدعى عليه، وما إذا كانت تُستدعى مرة واحدة في كل مسار.

Swift 6 والتزامن: مسار منفصل عن تحويل اللغة

إضافة Swift إلى تطبيق Objective-C لا تعني ضرورة تحويل جميع العمليات غير المتزامنة إلى async/await فوراً. كذلك يختلف إصدار المترجم المستخدم عن وضع اللغة المحدد للـ target؛ استخدام مترجم حديث لا يعني تلقائياً تشغيل كل target بوضع Swift 6.

تضيف فحوص التزامن الصارمة متطلبات حول العزل ومشاركة القيم، وقد تكشف مشكلات عند التعامل مع كائنات Objective-C قابلة للتغيير. راجع دليل الانتقال الرسمي إلى Swift 6 لتخطيط هذا العمل وفق إعدادات المشروع.

  • حدّد ملكية الحالة المشتركة قبل نقلها بين tasks أو actors.
  • اجعل الوصول إلى الواجهة متوافقاً مع عزل MainActor حيث يلزم.
  • راجع دلالات Sendable، ولا تستخدم @unchecked Sendable لمجرد إسكات التشخيصات.
  • عند تغليف callbacks في واجهة async، اختبر الإكمال والإلغاء واحتمال استدعاء callback أكثر من مرة.

تقسيم العمل إلى ترحيل اللغة، ثم تحسين واجهات التزامن، ثم تفعيل قيود إضافية عند الحاجة يجعل مراجعة كل تغيير أسهل. وقد تجمع بعض الوحدات هذه الخطوات عندما تكون صغيرة ومغطاة باختبارات كافية.

خطة تنفيذ واختبار لكل دفعة

1. تثبيت السلوك الحالي

اكتب اختبارات توصيف السلوك قبل إعادة التنفيذ. استخدم XCTest للتحقق من النتائج الحالية، بما فيها الحالات الطرفية، ثم شغّل الاختبارات نفسها على النسخة الجديدة قدر الإمكان. إذا اكتشفت سلوكاً خاطئاً، وثّق قرار إصلاحه في تغيير مستقل حتى لا يختلط الإصلاح بالترحيل.

2. تحويل وحدة مع الحفاظ على عقدها

حافظ أولاً على المدخلات والمخرجات ودلالات الأخطاء والسلوك الملحوظ لدى المستهلكين. حدّث واجهة التوافق بالقدر الضروري، ثم أجرِ تحسينات داخلية صغيرة يمكن مراجعتها. تجنّب جمع إعادة تسمية واسعة وتغيير تخزين وتبديل مكتبات مع تحويل اللغة في دفعة واحدة.

3. اختبار التكامل لا التنفيذ المعزول فقط

  • اختبر استدعاء Swift من Objective-C والعكس.
  • تحقّق من nil والقيم الفارغة وبيانات الخادم غير المتوقعة.
  • اختبر انقطاع الشبكة والمهل وإعادة المحاولة والإلغاء.
  • اختبر قراءة بيانات محفوظة بإصدار التطبيق السابق.
  • تحقّق من Storyboards وXIBs وselectors والمراقبين المتأثرين.
  • شغّل اختبارات الواجهة للمسارات الحرجة مثل تسجيل الدخول والشراء وحفظ البيانات.

ادمج الاختبارات مع CI باستخدام البيئة المناسبة للفريق، مثل Xcode Cloud أو GitHub Actions على macOS. نسبة التغطية وحدها لا تثبت سلامة الترحيل؛ الأهم هو تغطية السلوك الذي يترتب على تغييره أثر للمستخدم.

4. اعتماد معايير قبول واضحة

المجالما يجب التحقق منه
السلوكنجاح اختبارات الوحدة والتكامل والمسارات الحرجة
البياناتإمكان قراءة البيانات القديمة وعدم فقدها أثناء التحديث
الأداءعدم تجاوز حدود الانحدار التي يحددها الفريق مسبقاً
الذاكرةمراجعة التسريبات ودورات الاحتفاظ في السيناريوهات المعدلة
البناء والتوزيعنجاح بناء Release والأرشفة والاختبار على الأجهزة المستهدفة

5. الإطلاق والمراقبة والتراجع

اختبر البناء عبر TestFlight قبل الإطلاق، وراقب الأعطال والأخطاء الوظيفية بعده. إذا احتفظت مؤقتاً بتنفيذ قديم وآخر جديد، فقد يساعد feature flag على اختيار التنفيذ، بشرط أن يظل كلاهما متوافقاً مع البيانات والعقود الحالية.

لا تعتمد على فكرة الرجوع الفوري إلى نسخة متجر سابقة؛ إصلاح تطبيق منشور قد يتطلب إصداراً جديداً. وخطة التراجع لا تكتمل بإرجاع الكود إذا كانت الدفعة غيّرت تنسيق البيانات بصورة غير قابلة للعكس.

هل يلزم الانتقال إلى SwiftUI أيضاً؟

لا؛ ترحيل اللغة مستقل عن إطار الواجهة. يمكن كتابة UIViewController وواجهات UIKit بلغة Swift دون إعادة تصميم الشاشات. وهذا يتيح الاحتفاظ بسلوك الواجهة أثناء تحديث التنفيذ.

يمكن كذلك إدخال SwiftUI تدريجياً باستخدام UIHostingController داخل UIKit، أو استخدام UIViewRepresentable وUIViewControllerRepresentable لدمج مكونات UIKit داخل SwiftUI. الاختيار يعتمد على متطلبات الشاشة وخبرة الفريق وإصدارات النظام المستهدفة.

يتوفر أساس SwiftUI منذ iOS 13، لكن واجهاته الأحدث قد تتطلب إصدارات أحدث. لذلك راجع توفر كل API، ولا تعتبر الحد الأدنى للإطار ضماناً لتوفر جميع خصائصه.

تقدير الوقت والتكلفة واختيار فريق التنفيذ

لا يمكن استنتاج مدة موثوقة من عدد أسطر Objective-C وحده. التقدير الجيد يشمل تدقيق الوحدة، وكتابة الاختبارات الناقصة، وإعادة التنفيذ، وتصميم الجسر، وحل مشاكل البناء، واختبار البيانات، والمراقبة بعد الإصدار.

ابدأ بدفعة تجريبية تمثل نوعاً متكرراً من العمل، وسجّل الوقت الفعلي والعوائق المكتشفة. استخدم النتائج لتقدير الوحدات المشابهة، مع فصل الوحدات شديدة الارتباط أو الحساسة للبيانات. ضع احتياطياً للمخاطر بناءً على نتائج التدقيق، لا على نسبة ثابتة تُطبق على كل مشروع.

متى تحتاج إلى شريك خارجي؟

تكون الاستعانة بخبرة إضافية مفيدة إذا غابت خبرة التوافق بين اللغتين، أو ظهرت تحديات في runtime والتزامن والتخزين، أو لم تتوفر قدرة كافية لبناء الاختبارات مع استمرار تطوير المنتج. وقد تكفي مراجعة معمارية محدودة بدلاً من إسناد الترحيل بالكامل.

عند تقييم الشريك، اطلب خطة لدفعة تجريبية، ومعايير قبول، وآلية تراجع، وتوثيقاً لحدود التوافق، وتسليماً معرفياً للفريق الداخلي. الوعود بتحويل آلي كامل أو تسريع مضمون دون فحص التطبيق ليست أساساً مناسباً للقرار.

لمناقشة نطاق تطبيقك وخيارات التنفيذ، يمكنك التواصل معنا مع وصف الوحدات الأساسية والتبعيات والاختبارات المتاحة.

أسئلة شائعة عن الترحيل إلى Swift

هل يجب تحويل جميع ملفات Objective-C؟

لا. يمكن إبقاء وحدات مستقرة بلغة Objective-C وتطوير غيرها بلغة Swift. حدّد النجاح بتحسين قابلية الصيانة وتقليل المخاطر وتحقيق متطلبات المنتج، لا بنسبة الملفات المحوّلة فقط.

هل توجد أداة تحوّل التطبيق تلقائياً؟

قد تساعد أدوات التحويل أو مساعدات البرمجة على إنتاج مسودة، لكنها لا تتحقق تلقائياً من دلالات الملكية أو التزامن أو التوافق مع البيانات والسلوك الديناميكي. تعامل مع المخرجات ككود جديد يحتاج إلى مراجعة واختبار.

هل يمكن استخدام مكتبة Objective-C من Swift؟

نعم، إذا كانت واجهتها وإعدادات بنائها قابلة للاستيراد. وجود مكتبة Objective-C لا يفرض استبدالها؛ افحص صيانتها وتوافقها وجودة واجهتها قبل اتخاذ القرار.

هل يؤدي الترحيل إلى تحسين الأداء مباشرة؟

ليس بالضرورة. يعتمد الأثر على الخوارزميات وعمليات الجسر وتخصيص الذاكرة والعمل المنفذ. أثبت أي تحسن بقياسات قبل التغيير وبعده في ظروف متكافئة.

ما أول خطوة عملية؟

اختر وحدة محدودة الأثر، ووثّق عقدها، وأضف اختبارات لسلوكها الحالي، ثم حوّلها وأطلقها تحت المراقبة. نتائج هذه التجربة أساس أفضل للخطة من تقدير شامل قبل فحص التبعيات.

الخلاصة: ابدأ بما يحقق قيمة ويمكن التحقق منه

الترحيل إلى Swift مشروع للحفاظ على سلوك التطبيق مع تحسين قدرته على التطور، وليس مجرد تبديل للصياغة. ابدأ بتدقيق واضح، واضبط التوافق بين اللغتين، وافصل تغيير اللغة عن القرارات المعمارية الكبيرة، ولا توسّع النطاق قبل نجاح دفعة قابلة للقياس. وقد تكون النتيجة المناسبة تطبيقاً مختلطاً جيد التصميم، لا إعادة كتابة كاملة.

المصادر والمراجع التقنية

تختلف تفاصيل الإعداد والتوافق بحسب إصدار Xcode ونوع الـ target وإعدادات المشروع. استخدم التوثيق الرسمي مرجعاً، واختبر الأمثلة ضمن بيئة تطبيقك قبل اعتمادها.

الترحيل من Objective-C إلى Swift غالبًا ما يكون جزءًا من عملية أوسع تشمل إعادة تصميم تطبيقات iOS القديمة بالكامل، خاصةً عند تراكم مشكلات الكود القديم وتراجع الأداء مع أنظمة iOS الحديثة.