خصائص Swift في تطوير التطبيقات المحمولة: ما هي، الميزات الرئيسية وكيف تعمل

المؤلف: IT Sectr نُشر: 2026-06-25 وقت القراءة: 17 دق

Swift هي لغة برمجة من Apple تجمع بين قوة Objective-C وأمان اللغات الحديثة. على عكس العديد من اللغات الأخرى، تقدم Swift تراكيب فريدة: أنواع القيم مع النسخ التلقائي، و Actors مع عزل على مستوى المترجم، و Property Wrappers لإعادة استخدام منطق الخصائص، والبرمجة الموجهة بالبروتوكولات كبديل للوراثة التقليدية. وفقًا لوثائق Apple Swift، هذه الميزات تجعل الكود أكثر أمانًا وأداءً.

النقاط الرئيسية

  • Struct — نوع قيمة، يُنسخ عند التعيين، لا يدعم الوراثة. Class — نوع مرجعي، يُمرر بالمرجع، يدعم الوراثة و deinit.
  • Actor — نوع مرجعي آمن للخيوط مع عزل على مستوى المترجم. يمنع المترجم الوصول المتزامن إلى الحالة القابلة للتغيير من مهام مختلفة.
  • Property Wrappers — @Published و @State و @Binding تغلف منطق الخصائص في SwiftUI و Combine، مما يلغي الكود المتكرر.
  • Generics تسمح بكتابة دوال وهياكل آمنة الأنواع دون الارتباط بنوع معين. تستخدم البرمجة الموجهة بالبروتوكولات extension + protocol بدلاً من التسلسل الهرمي للفئات.
  • Enum مع القيم المرتبطة — أداة قوية لنمذجة الحالات. يمكن لكل حالة تخزين مجموعة عشوائية من البيانات من أنواع مختلفة.

Struct vs Class في Swift: أنواع القيم وأنواع المراجع

في Swift، struct هو نوع قيمة، بينما class هو نوع مرجعي. هذا الاختلاف الأساسي يحدد السلوك أثناء التعيين والتمرير للدوال وإدارة الذاكرة. يتم نسخ struct بالكامل عند كل تعيين، بينما يُمرر class بالمرجع — يتم نسخ المؤشر فقط إلى الكائن في heap.

دلالات القيمة لـ Struct

يطبق struct تلقائيًا دلالات القيمة — كل مثيل مستقل. تغيير خاصية struct ممكن فقط من خلال var، ويجب وضع علامة mutating على الدوال التي تعدل الخصائص. لا يدعم struct الوراثة ولكنه يمكنه الامتثال للبروتوكولات. تستخدم المكتبة القياسية لـ Swift struct لـ String و Array و Dictionary و Int و Bool — جميع الأنواع الأساسية هي أنواع قيمة.

swift
struct User {
    let id: Int
    var name: String

    mutating func updateName(_ newName: String) {
        name = newName
    }
}

var user1 = User(id: 1, name: "Alice")
var user2 = user1
user2.updateName("Bob")
// user1.name === "Alice", user2.name === "Bob"
// نسخة كاملة — مثيلات مستقلة

دلالات المرجع لـ Class

يُخزن class في heap ويدعم المساواة المرجعية. يمكن لمتغيرات متعددة الإشارة إلى نفس الكائن، التغييرات عبر مرجع واحد تكون مرئية عبر جميع المراجع الأخرى. يدعم class الوراثة، ويسمح بتجاوز الدوال (override)، وله deinit لتنظيف الموارد. في Swift، يُستخدم class بشكل أقل من struct — توصي Apple بالبدء باستخدام struct والتحول إلى class فقط عند الحاجة إلى الوراثة أو دلالات المرجع.

swift
class Car {
    let model: String
    var speed: Int

    init(model: String, speed: Int) {
        self.model = model
        self.speed = speed
    }

    func accelerate(_ amount: Int) {
        speed += amount
    }
}

let car1 = Car(model: "Tesla", speed: 0)
let car2 = car1
car2.accelerate(50)
// car1.speed === 50, car2.speed === 50
// نفس المرجع — التغييرات مرئية في كل مكان

يعتمد الاختيار بين struct و class على دلالات البيانات. لـ الإعدادات ونماذج البيانات والحالات استخدم struct — النسخ أكثر أمانًا وقابلية للتنبؤ. للكائنات ذات الهوية (جلسة المستخدم، اتصال قاعدة البيانات) وعند الحاجة إلى الوراثة، استخدم class.

Actor — أمان الخيوط في Swift

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

عزل Actor

جميع خصائص وطرق actor معزولة افتراضيًا. استدعاء طريقة actor من الخارج يتطلب await — يقوم Swift بتبديل التنفيذ إلى actor والعودة. يقوم actor بمزامنة الوصول بشكل تسلسلي: يتم وضع طلبات من مهام مختلفة في قائمة انتظار. هذا يضمن حالة متسقة داخل actor. يمكن استدعاء الطرق غير المعزولة (nonisolated) بدون await ولكن لا يمكنها الوصول إلى الخصائص القابلة للتغيير.

swift
actor BankAccount {
    private var balance: Double

    init(initialBalance: Double) {
        balance = initialBalance
    }

    func deposit(amount: Double) {
        balance += amount
    }

    func withdraw(amount: Double) throws {
        guard balance >= amount else {
            throw BankError.insufficientFunds
        }
        balance -= amount
    }

    nonisolated func accountInfo() -> String {
        return "حساب بنكي"
    }
}

let account = BankAccount(initialBalance: 1000)
await account.deposit(amount: 500)
// balance = 1500 — ذري، بدون حالة سباق

Actor مفيد لـ مديري الحالة والتخزين المؤقت وخدمات الشبكة حيث تقرأ وتكتب مهام متعددة البيانات بشكل متزامن. على عكس المزامنة اليدوية عبر NSLock أو DispatchQueue، يتم التحقق من actor في وقت التجميع — من المستحيل ارتكاب خطأ. تظهر أخطاء العزل في وقت التجميع وليس في وقت التشغيل.

Property Wrappers: @Published, @State, @Binding

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

@Published و @State

@Published يُستخدم في الفئات التي تمتثل لـ ObservableObject لنشر التغييرات تلقائيًا. يتلقى مشتركو Combine إشعارات عند كل تحديث. @State هو حالة عرض SwiftUI المحلية. يعيد SwiftUI رسم العرض تلقائيًا عند تغيير @State. @Binding ينشئ اتصالًا ثنائي الاتجاه بين العرض الأب والتابع دون امتلاك البيانات.

swift
import SwiftUI
import Combine

@main
struct CounterApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

class CounterViewModel: ObservableObject {
    @Published var count: Int = 0

    func increment() {
        count += 1
    }
}

struct ContentView: View {
    @State private var greeting: String = "مرحبًا"
    @StateObject private var viewModel = CounterViewModel()

    var body: some View {
        VStack {
            CounterDisplay(count: $viewModel.count)
            Text(greeting)
            Button("زيادة", action: viewModel.increment)
        }
    }
}

struct CounterDisplay: View {
    @Binding var count: Int

    var body: some View {
        Text("العدد: \(count)")
    }
}

يتم إنشاء Property Wrappers المخصصة باستخدام @propertyWrapper وهيكل مع حقل wrappedValue. هذا يسمح بإعادة استخدام المنطق: التحقق، التطبيع، تخزين UserDefaults. تقلل Property Wrappers بشكل كبير من الكود المتكرر وتجعل نية المطور واضحة من خلال التعليق التوضيحي.

swift
@propertyWrapper
struct Clamped<T: Comparable> {
    private var value: T
    let range: ClosedRange<T>

    init(wrappedValue: T, range: ClosedRange<T>) {
        self.range = range
        self.value = min(max(wrappedValue, range.lowerBound), range.upperBound)
    }

    var wrappedValue: T {
        get { value }
        set { value = min(max(newValue, range.lowerBound), range.upperBound) }
    }
}

struct Settings {
    @Clamped(range: 0...100) var volume: Int = 50
}

var s = Settings()
s.volume = 150
// s.volume === 100 — القيمة محدودة تلقائيًا

Generics والبرمجة الموجهة بالبروتوكولات

Generics هي آلية قوية في Swift لكتابة كود مرن دون فقدان أمان الأنواع. تعمل الدوال والأنواع العامة مع أي نوع يفي بالقيود. البرمجة الموجهة بالبروتوكولات (POP) هي نموذج Swift حيث يحل protocol + extension محل الوراثة التقليدية للفئات، مما يوفر تكوين السلوك.

قيود Generics والأنواع المرتبطة

يسمح Swift بتقييد المعلمات العامة بالبروتوكولات (جمل where). بروتوكول مع نوع مرتبط هو المكافئ العام للبروتوكولات: يتم تحديد النوع الملموس في وقت الامتثال. هذا هو الأساس للمجموعات والخوارزميات الآمنة الأنواع. تستخدم المكتبة القياسية لـ Swift generics بنشاط: Array و Dictionary و Optional — كلها أنواع عامة.

swift
protocol Cacheable {
    associatedtype Key: Hashable
    associatedtype Value

    func get(_ key: Key) -> Value?
    mutating func set(_ value: Value, for key: Key)
}

struct MemoryCache<K: Hashable, V>: Cacheable {
    private var storage: [K: V] = [:]

    typealias Key = K
    typealias Value = V

    func get(_ key: K) -> V? {
        return storage[key]
    }

    mutating func set(_ value: V, for key: K) {
        storage[key] = value
    }
}

func firstMatch<T: Equatable>(in array: [T], target: T) -> Int? {
    for (index, item) in array.enumerated() {
        if item == target {
            return index
        }
    }
    return nil
}

let numbers = [10, 20, 30, 40]
let index = firstMatch(in: numbers, target: 30)
// index === 2 — طريقة عامة واحدة لجميع أنواع Equatable

Extension البروتوكول والتنفيذات الافتراضية

Extension البروتوكول هي آلية رئيسية لـ POP. في extension يمكن توفير تنفيذ افتراضي لطرق البروتوكول. تحصل الأنواع الممتثلة للبروتوكول على هذا التنفيذ مجانًا ويمكنها تجاوزه عند الحاجة. هذا يسمح ببناء تسلسلات هرمية للسلوك دون وراثة الفئات — بديل للوراثة المتعددة، غير الموجودة في Swift.

swift
protocol Drivable {
    var speed: Double { get set }
    func drive()
}

extension Drivable {
    func drive() {
        if speed > 0 {
            print("القيادة بسرعة \(speed) كم/س")
        }
    }
}

struct Bicycle: Drivable {
    var speed: Double = 15
}

struct Plane: Drivable {
    var speed: Double = 900

    func drive() {
        print("الطيران بسرعة \(speed) كم/س")
    }
}

let bike = Bicycle()
bike.drive() // "القيادة بسرعة 15.0 كم/س" — افتراضي

let plane = Plane()
plane.drive() // "الطيران بسرعة 900.0 كم/س" — تجاوز

POP مع extension يتيح التكوين من خلال بروتوكولات متعددة — يمكن لنوع أن يممتثل لعدة بروتوكولات، ويحصل على تطبيقاتها. هذا أكثر مرونة من وراثة الفئة الواحدة حيث يكون التسلسل الهرمي بأكمله ثابتًا. المكتبة القياسية لـ Swift مبنية على POP: CustomStringConvertible و Equatable و Hashable و Codable — كلها بروتوكولات مع extensions.

Enum مع القيم المرتبطة و Extension

Enum في Swift هو تركيب أكثر قوة بشكل ملحوظ من اللغات الأخرى. يمكن أن تحتوي كل حالة على قيم مرتبطة — مجموعة عشوائية من البيانات من أي نوع. بالاقتران مع extension و pattern matching، يصبح enum الأساس لنمذجة الحالات ونتائج العمليات وآلات الحالة المحدودة.

القيم المرتبطة و Pattern Matching

القيم المرتبطة تسمح لكل حالة بتخزين بيانات فريدة. على سبيل المثال، حالة loading لا تحتوي على بيانات، success تحتوي على النتيجة، failure تحتوي على خطأ. Pattern Matching عبر switch يستخرج القيم المرتبطة ويعالج كل حالة. يتحقق المترجم من التغطية الكاملة (exhaustive switch) — مما يلغي الحالات المنسية.

swift
enum NetworkResult<T> {
    case loading
    case success(T)
    case failure(Error)
}

enum MediaFile {
    case image(url: URL, width: Int, height: Int)
    case video(url: URL, duration: Double)
    case audio(url: URL, bitrate: Int)
}

func handle(_ result: NetworkResult<String>) {
    switch result {
    case .loading:
        print("جارٍ التحميل...")
    case .success(let data):
        print("تم الاستلام: \(data)")
    case .failure(let error):
        print("خطأ: \(error.localizedDescription)")
    }
}

let file = MediaFile.video(url: URL(string: "https://example.com/video.mp4")!, duration: 120)

if case .video(let url, let duration) = file {
    print("فيديو \(url) مدته \(duration) ثانية")
}

Extension لـ Enum

يسمح Swift بإضافة الدوال والخصائص المحسوبة والامتثال للبروتوكولات عبر extension لـ enum. هذا لا يتطلب تغيير التعريف الأصلي — يبقى الكود نظيفًا ومعياريًا. يمكن للدوال في extension العمل مع القيم المرتبطة وحساب البيانات المشتقة وتنفيذ منطق الأعمال.

swift
extension MediaFile {
    var fileName: String {
        switch self {
        case .image(let url, _, _),
             .video(let url, _),
             .audio(let url, _):
            return url.lastPathComponent
        }
    }

    var description: String {
        switch self {
        case .image(_, let w, let h):
            return "صورة \(w)x\(h)"
        case .video(_, let d):
            return "فيديو \(d) ث"
        case .audio(_, let b):
            return "صوت \(b) كيلوبت/ث"
        }
    }
}

extension MediaFile: Equatable {
    static func == (lhs: MediaFile, rhs: MediaFile) -> Bool {
        return lhs.fileName == rhs.fileName
    }
}

let files: [MediaFile] = [file]
files.forEach { print($0.description) }
// "فيديو 120.0s"

Enum مع القيم المرتبطة + extension هو بديل للفئات المختومة من Kotlin أو الاتحادات المميزة من لغات أخرى. تستخدم Swift هذه التركيبة لـ Result (نوع قياسي في المكتبة القياسية)، و Optional (Optional هو enum مع .none و .some) ومعالجة الحالة في SwiftUI. يضمن Pattern Matching معالجة جميع الحالات — الأخطاء في وقت التشغيل مستحيلة.

الأسئلة الشائعة

كيف يختلف struct عن class في Swift؟

Struct — نوع قيمة، يُنسخ عند التعيين، لا يدعم الوراثة. Class — نوع مرجعي، يُمرر بالمرجع، يدعم الوراثة و deinit والتحقق من الهوية (===). توصي Apple بالبدء بـ struct والتحول إلى class فقط عند الحاجة إلى دلالات المرجع.

ما هو Actor في Swift ومتى يستخدم؟

Actor — نوع مرجعي آمن للخيوط. يمنع المترجم الوصول المتزامن إلى الحالة القابلة للتغيير من سياقات مختلفة. استخدم Actor لمديري الحالة والتخزين المؤقت والخدمات التي تصل إليها مهام متعددة. استدعاء طرق actor يتطلب await.

ما هي Property Wrappers المستخدمة في SwiftUI؟

الرئيسية: @State — حالة العرض المحلية؛ @Binding — اتصال بالبيانات دون امتلاك؛ @Published — نشر التغييرات في ObservableObject؛ @StateObject — امتلاك ObservableObject؛ @EnvironmentObject — تبعية من البيئة. يتم إنشاء المغلفات المخصصة عبر @propertyWrapper.

ما هي البرمجة الموجهة بالبروتوكولات؟

POP — نموذج حيث يحدد protocol + extension السلوك بدلاً من وراثة الفئة. يمتثل نوع لعدة بروتوكولات، ويحصل على تطبيقاتها مجانًا. هذا تكوين بدلاً من الوراثة — أكثر مرونة وأمانًا. المكتبة القياسية لـ Swift (Equatable, Codable) مبنية على POP.

كيف يختلف enum في Swift عن enum في اللغات الأخرى؟

Enum في Swift يدعم القيم المرتبطة — يمكن لكل حالة تخزين بيانات عشوائية من أنواع مختلفة. بالاقتران مع pattern matching (switch)، يوفر هذا نموذجًا قويًا للحالات والأخطاء والتصميم المدفوع بالمجال. Optional و Result هما enum قياسيان في Swift مع قيم مرتبطة.

الملخص

  • Struct vs Class — اختلاف أساسي: أنواع القيم تُنسخ، أنواع المراجع تُمرر بالمرجع. Struct لا يدعم الوراثة، class يدعمها. توصي Apple بـ struct افتراضيًا.
  • Actor — أمان الخيوط على مستوى المترجم. حالة قابلة للتغيير معزولة، وصول عبر await، حماية من سباقات البيانات دون أقفال يدوية.
  • Property Wrappers — @Published و @State و @Binding ومخصصة عبر @propertyWrapper. تقلل الكود المتكرر وتجعل الهدف واضحًا من خلال التعليق التوضيحي.
  • Generics — دوال وأنواع آمنة الأنواع مع قيود where. Protocol + نوع مرتبط للتجريدات. المكتبة القياسية لـ Swift مبنية بالكامل على generics.
  • البرمجة الموجهة بالبروتوكولات — تكوين السلوك عبر extension البروتوكول. التنفيذات الافتراضية تسمح بإعادة استخدام الكود دون وراثة الفئات.
  • Enum مع القيم المرتبطة — نموذج قوي للحالات. كل حالة تخزن بيانات فريدة. يضمن Pattern Matching معالجة جميع الحالات.
  • Extension — يضيف دوالًا وخصائص محسوبة وامتثالًا للبروتوكولات لأي نوع، بما في ذلك enum. يبقى الكود معياريًا ونظيفًا دون تغيير التعريف الأصلي.

سنقوم بتطوير تطبيق جوال جاهز

تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.

مناقشة المشروع