some View هو تركيب نحوي رئيسي في Swift لا يمكن لـ SwiftUI العمل بدونه. وفقًا لـ Apple Swift Book, 2024، some View هو نوع غير شفاف (opaque type) يخفي نوع القيمة المعادة المحدد مع الحفاظ على الكتابة الصارمة في وقت الترجمة. يسمح هذا التركيب لبروتوكول View بأن يكون له توقيع body موحد دون الكشف عن تفاصيل التنفيذ.
الخلاصة
some View هو تركيب نوع غير شفاف تم تقديمه في Swift 5.1. يُستخدم كنوع إرجاع لخاصية body لبروتوكول View. الرمز some View يعني: "الدالة أو الخاصية تعيد نوعًا محددًا يمتثل لبروتوكول View، لكن الكود المستدعي لا يعرف ولا يجب أن يعرف أي نوع بالضبط".
مفهوم النوع غير الشفاف هو الجانب المعاكس للبرمجة العامة (generics). إذا كانت generics تسمح للكود المستدعي بتحديد النوع، فإن النوع غير الشفاف يسمح للتنفيذ بتحديد النوع، مخفيًا إياه عن المستدعي. هذا يعطي المطور حرية تغيير التنفيذ الداخلي دون تغيير العقد.
وفقًا لـ Swift Evolution SE-0244، تمت إضافة الأنواع غير الشفافة لدعم SwiftUI ونمط البروتوكولات ذات الأنواع المرتبطة (PAT)، والتي لا يمكن استخدامها كنوع إرجاع بدون هذا التركيب.
بدون some View، سيكون توقيع body مستحيلًا: بروتوكول View له نوع مرتبط Body يمتثل لـ View. إذا أعاد body ببساطة View (كبروتوكول)، فلن يتمكن Swift من العمل مع البروتوكولات ذات متطلبات Self في موضع الإرجاع. some View يحل هذه المشكلة بتوفير نوع محدد لكن مخفي.
النوع غير الشفاف هو نوع خاص يتصرف كمحدد للمترجم ولكن كمجرد للمطور. عندما يرى المترجم some View، يقوم بتحليل التنفيذ وتحديد النوع الدقيق المُعاد. يتم تثبيت هذا النوع واستخدامه لتوليد الكود دون إرسال ديناميكي.
struct SimpleView: View {
var body: some View {
Text("مرحبًا")
}
}
// يرى المترجم: body -> Text, not some View
مبدأ العمل: يستنتج مترجم Swift النوع المحدد من التنفيذ. في المثال أعلاه، يحتوي الجسم على Text فقط، لذلك يعرف المترجم أن body يعيد Text بالضبط، حتى لو كان التوقيع مكتوبًا كـ some View. هذا يوفر تحسينين: استدعاء مباشر بدون جدول طرق افتراضية وإمكانية التضمين (inlining).
إذا تغير تنفيذ body (على سبيل المثال، بدلاً من Text يتم إرجاع VStack من Text و Button)، يعيد المترجم تعريف النوع المحدد. لكن بالنسبة للكود المستدعي (SwiftUI)، يبقى التوقيع كما هو — some View. هذا هو الجانب المعاكس لـ generics: الكود المستدعي لا يعتمد على تغييرات التنفيذ.
من القواعد الرئيسية للأنواع غير الشفافة: دالة أو خاصية تعيد some View يجب أن تعيد دائمًا نفس النوع المحدد. لا يمكنك إرجاع Text في فرع if و Image في فرع آخر. هذه الحدود يتحقق منها المترجم وتعمل كضمان للكود المستدعي.
struct BadView: View {
var flag: Bool
var body: some View {
if flag {
Text("صحيح") // خطأ: Text vs VStack
} else {
VStack {
Text("خطأ")
Image(systemName: "xmark")
}
}
}
}
لحل هذه المشكلة يُستخدم @ViewBuilder، الذي يغلف الفروع المختلفة في حاوية شرطية ConditionalContent. التعليق التوضيحي @ViewBuilder فوق body هو ممارسة قياسية في SwiftUI، على الرغم من أنه يمكن أن يكون ضمنيًا إذا كان body يحتوي على تعبير واحد فقط.
AnyView هو نوع يمحو التنفيذ المحدد لـ View (type erasure). يغلف أي View في غلاف واحد، مما يسمح بتخزين Views من أنواع مختلفة في نفس الحاوية. على عكس some View، يعمل AnyView في وقت التنفيذ ويضيف حملًا إضافيًا للتغليف وفك التغليف.
| المعيار | some View | AnyView |
|---|---|---|
| وقت التحديد | الترجمة | التنفيذ |
| الأداء | استدعاء مباشر، بدون حمل | تغليف في existential container |
| مرونة الأنواع | نوع محدد واحد | أي أنواع View |
| التغيير الديناميكي | غير مدعوم | مدعوم في وقت التنفيذ |
| أولوية الاستخدام | دائمًا عندما يكون ممكنًا | فقط عندما يكون some View مستحيلًا |
| دعم بروتوكولات PAT | نعم | نعم |
متى تستخدم AnyView: فقط في الحالات التي يكون فيها some View مستحيلًا بسبب الحاجة إلى تغيير ديناميكي للنوع في وقت التنفيذ. على سبيل المثال، عند إرجاع View من قاموس أو في هيكل تكراري حيث يجب أن يتغير النوع المحدد في كل مستوى. يجب تقليل استخدام AnyView إلى أدنى حد، لأن كل تغليف يعطل تحسينات SwiftUI.
مفهوم خاطئ شائع: AnyView لا يحل مشكلة الأنواع المختلفة في body — @ViewBuilder هو من يحلها. AnyView يمحو النوع لكنه لا يساعد المترجم في استنتاج نوع واحد. استخدم @ViewBuilder للمنطق الشرطي و AnyView فقط للإرسال الديناميكي.
@ViewBuilder هو منشئ نتائج (result builder) مصمم خصيصًا للعمل مع some View. يسمح باستخدام المنطق الشرطي (if/else, switch) وتعبيرات متعددة في الجسم مع الحفاظ على نوع إرجاع واحد. يقوم ViewBuilder تلقائيًا بتغليف التعبيرات المتعددة في TupleView والفروع الشرطية في ConditionalContent.
struct ProfileView: View {
let user: User?
@ViewBuilder
var body: some View {
if let user {
UserCard(user: user)
Text("على الإنترنت")
.font(.caption)
} else {
ProgressView("Loading...")
}
}
}
كيف يعمل: يحلل @ViewBuilder كتلة الكود ويولد استدعاء buildBlock أو buildOptional أو buildEither المناسب. للمنطق الشرطي، يتم إنشاء ConditionalContent — نوع مشترك يخفي الأنواع المحددة داخل الفروع لكنه في حد ذاته نوع واحد للمترجم. هذا يحل مشكلة الأنواع المحددة المختلفة.
بدون @ViewBuilder، خاصية body التي تحتوي على تعبيرات متعددة أو منطق شرطي ستسبب خطأ في الترجمة. لهذا السبب يطبق SwiftUI @ViewBuilder على body ضمنيًا، وبالنسبة للخصائص والدوال المخصصة يجب إضافته صراحةً.
يمكن تداخل @ViewBuilder: ViewBuilder داخل آخر. يسمح هذا بإنشاء تسلسلات هرمية معقدة بشروط في مستويات مختلفة. ومع ذلك، فإن التداخل العميق يعقد القراءة، لذلك يُنصح باستخراج الشروط المتداخلة إلى مكونات View منفصلة.
مثال 1: إرجاع View مخصصة من خاصية محسوبة. يمكن للخاصية إرجاع some View، مخفية التركيب الداخلي. هذا يسمح بإعادة تنظيم الكود دون تغيير الواجهة العامة.
struct ArticleView: View {
var body: some View {
CardView {
HeaderView()
ContentView()
FooterView()
}
}
}
struct CardView<Content: View>: View {
let content: Content
var body: some View {
content
.padding(16)
.background(.white)
.cornerRadius(12)
.shadow(radius: 4)
}
}
مثال 2: تمرير View كإغلاق عبر @ViewBuilder. يستخدم هذا النمط في حاويات SwiftUI القياسية (VStack, HStack, List) ويمكن تنفيذه في المكونات المخصصة.
struct CustomContainer<Content: View>: View {
@ViewBuilder let content: () -> Content
var body: some View {
VStack(alignment: .leading) {
content()
}
.padding(20)
}
}
مثال 3: دالة مصنع تعيد some View. تسمح بإنشاء Views حسب المعاملات دون الكشف عن التنفيذ. هذا مفيد بشكل خاص للمكتبات والمكونات القابلة لإعادة الاستخدام.
func makeIcon(for status: Status) -> some View {
switch status {
case .success:
Image(systemName: "checkmark.circle.fill")
.foregroundColor(.green)
case .error:
Image(systemName: "xmark.circle.fill")
.foregroundColor(.red)
case .pending:
ProgressView()
}
}
الأسئلة الشائعة
some View هو نوع غير شفاف، يعني أنه يتم إرجاع نوع محدد يمتثل لبروتوكول View. النوع المحدد يثبته المترجم لكنه مخفي عن الكود المستدعي. هذا يوفر كتابة صارمة دون الكشف عن تفاصيل التنفيذ.
some View يُحدد في وقت الترجمة بدون أي حمل إضافي. AnyView يستخدم محو النوع (type erasure) في وقت التنفيذ مع تكاليف إضافية للتغليف في existential container. استخدم some View دائمًا عندما يكون ممكنًا، و AnyView فقط للتغيير الديناميكي للنوع.
النوع غير الشفاف يتطلب نوعًا محددًا واحدًا لجميع مسارات الإرجاع. if/else مع أنواع مختلفة ينتهك هذا المطلب. @ViewBuilder يحل المشكلة بتغليف الفروع في ConditionalContent — نوع واحد يخفي اختلافات التنفيذات المحددة.
some View لا يقلل الأداء — المترجم يعرف النوع الدقيق ويولد كودًا مباشرًا. على العكس، any View (كبروتوكول) سيتطلب إرسالًا ديناميكيًا. some View هو آلية تحسين مدمجة في تصميم SwiftUI.
نعم، some هو تركيب عام في Swift 5.1 غير مرتبط بـ SwiftUI. يمكن استخدامه مع أي بروتوكول: some Equatable، some Codable، some Collection. هذا مفيد لإخفاء الأنواع المتداخلة المعقدة مثل [String: [Int]].
الملخص
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.