البرمجة المتزامنة السهلة في Swift 6.2: دليلك العملي الشامل

دليل عملي شامل لميزات التزامن الجديدة في Swift 6.2 — يغطّي العزل الافتراضي على Main Actor وسمة @concurrent وTask.immediate وisolated deinit مع أمثلة تطبيقية ونصائح تهجير عملية

مقدمة: لماذا تحتاج البرمجة المتزامنة إلى نهج جديد؟

إذا كنت مطوّر Swift، فعلى الأرجح مررت بهذا الموقف: تفتح مشروعك البسيط — ربما تطبيق قائمة مهام أو عارض صور — وتجد عشرات التحذيرات البرتقالية تملأ الشاشة، كلها تتعلّق بـ Sendable وdata-race safety وأشياء لم تكن تحتاج أصلًا للتفكير فيها.

هذا بالضبط ما حدث مع كثير من المطوّرين منذ أن قدّمت Apple نظام البرمجة المتزامنة في Swift 5.5. النظام كان قويًا نظريًا، لكنه عمليًا كان يُغرقك بتعقيدات لست بحاجة لها في معظم الحالات.

الخبر الجيد؟ مع Swift 6.2 (الذي صدر في سبتمبر 2025)، قدّم فريق Swift حلًا أنيقًا تحت مسمى البرمجة المتزامنة السهلة (Approachable Concurrency). والفكرة الجوهرية بسيطة بصراحة: Swift يجب أن يطلب منك فهم التزامن فقط بالقدر الذي تستخدمه فعلًا. يعني إذا كودك تسلسلي بسيط، ليش المترجم يُجبرك على التعامل مع تعقيدات التنفيذ المتوازي؟

في هذا الدليل، سنستكشف كل التغييرات الجديدة مع أمثلة عملية تقدر تطبّقها مباشرة في مشاريعك. فلنبدأ.

فلسفة الكشف التدريجي: ثلاث مراحل للتزامن

بنى فريق Swift النموذج الجديد على فلسفة الكشف التدريجي (Progressive Disclosure). الفكرة إنك تتعلّم التزامن على مراحل — كل مرحلة تضيف تعقيدًا فقط عندما تحتاجه فعلًا.

المرحلة الأولى: الكود التسلسلي البسيط

في هذه المرحلة، تكتب كودًا عاديًا بدون أي تفكير في التوازي. كل شيء يعمل على الـ Main Thread تمامًا كما اعتدت. لا تحتاج لأي annotations خاصة بالتزامن — وهذا هو الجمال في الموضوع.

class UserManager {
    var users: [User] = []
    
    func addUser(_ user: User) {
        users.append(user)
    }
    
    func removeUser(at index: Int) {
        users.remove(at: index)
    }
    
    func findUser(byName name: String) -> User? {
        users.first { $0.name == name }
    }
}

في Swift 6.2 مع تفعيل الإعداد الجديد، هذا الكود يعمل بدون أي تحذيرات. النظام يفترض تلقائيًا أن الكود معزول على الـ Main Actor. بسيط وواضح.

المرحلة الثانية: استخدام async/await بدون صداع

عندما تحتاج لاستدعاء واجهات غير متزامنة (طلبات شبكة، قراءة ملفات، وما شابه)، تستخدم async/await بشكل طبيعي:

class DataController {
    func loadUsers() async throws -> [User] {
        let url = URL(string: "https://api.example.com/users")!
        let (data, _) = try await URLSession.shared.data(from: url)
        return try JSONDecoder().decode([User].self, from: data)
    }
    
    func saveUser(_ user: User) async throws {
        let url = URL(string: "https://api.example.com/users")!
        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.httpBody = try JSONEncoder().encode(user)
        let (_, response) = try await URLSession.shared.data(for: request)
        
        guard let httpResponse = response as? HTTPURLResponse,
              httpResponse.statusCode == 200 else {
            throw NetworkError.saveFailed
        }
    }
}

الشيء المهم هنا — وهذا تغيير كبير صراحةً — إن الدوال الـ async تعمل الآن في سياق المُستدعي بشكل افتراضي. يعني لو استدعيت loadUsers() من الـ Main Actor، الدالة بتشتغل على الـ Main Actor أيضًا. لا يوجد انتقال مفاجئ بين الخيوط كما كان يحدث سابقًا (وكان يسبّب أخطاء خفية مزعجة).

المرحلة الثالثة: التوازي المتعمّد

فقط عندما تحتاج فعلًا للتنفيذ المتوازي — مثل معالجة مجموعة صور في نفس الوقت — تبدأ بالتفكير في Actors وSendability:

@concurrent
func processImages(_ images: [UIImage]) async -> [UIImage] {
    await withTaskGroup(of: UIImage.self) { group in
        for image in images {
            group.addTask {
                await image.applyFilter(.sepia)
            }
        }
        var results: [UIImage] = []
        for await result in group {
            results.append(result)
        }
        return results
    }
}

لاحظ استخدام @concurrent — هذه سمة جديدة تُخبر المترجم صراحةً أن هذه الدالة يجب أن تعمل على executor مستقل بعيدًا عن الـ Actor الحالي. سنتحدث عنها بتفصيل أكبر بعد قليل.

العزل الافتراضي على الـ Main Actor — SE-0466

صراحةً، هذا هو أهم تغيير في Swift 6.2.

مقترح SE-0466 يُتيح لك جعل كل الكود في الوحدة البرمجية معزولًا على الـ Main Actor بشكل افتراضي. يعني ببساطة: لم تعد بحاجة لكتابة @MainActor فوق كل class أو struct — النظام يتولّى هذا تلقائيًا.

كيفية التفعيل

في Xcode، اذهب إلى Build Settings وابحث عن Default Actor Isolation، ثم اختر MainActor. أو عبر Package.swift:

// In Package.swift
.executableTarget(
    name: "MyApp",
    swiftSettings: [
        .defaultIsolation(MainActor.self)
    ]
)

أو باستخدام علم المترجم مباشرة:

swiftc -default-isolation MainActor MyFile.swift

ماذا يحدث بعد التفعيل؟

بمجرد التفعيل، يتصرّف كل الكود كما لو كان مُعلّمًا بـ @MainActor:

// قبل التفعيل — كنت تحتاج لكتابة هذا:
@MainActor
class SettingsManager {
    var theme: Theme = .light
    var fontSize: Int = 14
    
    func updateTheme(_ newTheme: Theme) {
        theme = newTheme
        NotificationCenter.default.post(name: .themeChanged, object: nil)
    }
}

// بعد التفعيل — يكفي هذا:
class SettingsManager {
    var theme: Theme = .light
    var fontSize: Int = 14
    
    func updateTheme(_ newTheme: Theme) {
        theme = newTheme
        NotificationCenter.default.post(name: .themeChanged, object: nil)
    }
}

الفائدة واضحة: كود أنظف مع نفس مستوى الأمان. المترجم يعرف أن كل شيء على الـ Main Thread، فلا يحتاج لتحذيرك.

متى لا يُطبّق العزل الافتراضي؟

هناك استثناءات مهمة يجب أن تعرفها:

  • الأنواع التي تحمل بالفعل تعليقًا صريحًا لعزل Actor مختلف
  • البروتوكولات العامة (public protocols) — لأنها قد تُستخدم في وحدات برمجية أخرى بإعدادات مختلفة
  • الامتدادات على أنواع من وحدات برمجية خارجية
  • الدوال المُعلّمة صراحةً بـ nonisolated

سمة @concurrent: التوازي الصريح — SE-0461

هذا من التغييرات التي كنت أنتظرها شخصيًا. مقترح SE-0461 يُعالج مشكلة حقيقية كانت تُؤرّق المطوّرين: سابقًا، الدوال الـ nonisolated async كانت تنتقل تلقائيًا لخيط خلفي عند تنفيذها. وهذا كان مصدر أخطاء خفية كثيرة.

السلوك القديم مقابل الجديد

// السلوك القديم في Swift 6.1 وما قبله:
nonisolated func fetchData() async -> Data {
    // ⚠️ تعمل على خيط خلفي حتى لو استُدعيت من Main Actor
    let data = await networkCall()
    return data
}

// السلوك الجديد في Swift 6.2:
nonisolated func fetchData() async -> Data {
    // ✅ تعمل على نفس الـ Actor الذي استدعاها
    let data = await networkCall()
    return data
}

متى تستخدم @concurrent؟

القاعدة بسيطة: استخدمها عندما تريد صراحةً أن تعمل الدالة خارج أي Actor — أي على خيط خلفي مستقل:

@concurrent
func heavyComputation(data: [Double]) async -> Double {
    // عملية حسابية ثقيلة — لا نريدها على Main Thread
    var result: Double = 0
    for value in data {
        result += sin(value) * cos(value)
    }
    return result
}

@MainActor
func updateUI() async {
    let result = await heavyComputation(data: largeDataSet)
    // العودة تلقائيًا إلى Main Actor هنا
    resultLabel.text = "النتيجة: \(result)"
}

نصيحة عملية: إذا الدالة تقوم بعمل حسابي ثقيل أو عمليات I/O مكثّفة، استخدم @concurrent. أما إذا كانت مجرد دالة تنسيقية تستدعي APIs أخرى، اتركها بدون — خلّها تشتغل في سياق المُستدعي.

@concurrent مع البروتوكولات

ويمكنك أيضًا استخدامها في تعريفات البروتوكولات:

protocol DataProcessor {
    @concurrent func process(_ data: Data) async throws -> ProcessedResult
}

struct ImageProcessor: DataProcessor {
    @concurrent func process(_ data: Data) async throws -> ProcessedResult {
        let image = try decodeImage(from: data)
        let filtered = await applyFilters(to: image)
        return ProcessedResult(image: filtered)
    }
}

nonisolated(nonsending): الجسر بين العالمين

مع هذه التغييرات ظهر مفهوم جديد: nonisolated(nonsending). الاسم قد يبدو معقّدًا (وأنا أتفق أنه ليس أجمل اسم)، لكن الفكرة بسيطة.

الدالة المُعلّمة بهذه السمة تعمل في سياق المُستدعي بدون إرسال العمل لمُنفّذ آخر. وهذا هو السلوك الافتراضي الجديد:

// هذان التعريفان متكافئان في Swift 6.2:
func doSomething() async { }
nonisolated(nonsending) func doSomething() async { }

// بينما هذا يفرض التنفيذ على خيط مستقل:
@concurrent func doSomething() async { }

ببساطة، nonisolated(nonsending) تعني: "هذه الدالة ليست معزولة على Actor محدد، لكنها ستبقى حيث هي". هذا يمنع تلك الانتقالات المفاجئة بين الخيوط التي كانت سبب كثير من المشاكل.

المهام الفورية: Task.immediate — SE-0472

هذه الميزة ستُغيّر طريقة كتابتك لكود SwiftUI — أضمن لك.

المشكلة القديمة: عند إنشاء Task جديد، التنفيذ كان يتأجّل دائمًا للدورة التالية من الـ run loop حتى لو كان سيعمل على نفس الـ Actor. أحيانًا هذا التأخير البسيط يُسبّب وميض في الواجهة أو سلوك غير متوقع.

// السلوك القديم
@MainActor
func oldWay() {
    Task {
        // لا يُنفّذ فورًا — ينتظر الدورة التالية
        updateUI()
    }
    // هذا يُنفّذ أولًا
}

// السلوك الجديد
@MainActor
func newWay() {
    Task.immediate {
        // يبدأ فورًا لأننا بالفعل على Main Actor
        updateUI()
    }
}

مثال عملي في SwiftUI:

struct ContentView: View {
    @State private var items: [Item] = []
    
    var body: some View {
        List(items) { item in
            Text(item.name)
        }
        .onAppear {
            Task.immediate {
                items = await loadItems()
            }
        }
    }
}

تسمية المهام: Task Naming — SE-0469

ميزة صغيرة لكن — صدّقني — ستشكرها عندما تجلس تُصحّح خطأ في تطبيق فيه عشرات المهام المتوازية.

let downloadTask = Task(name: "DownloadUserProfile") {
    let profile = try await api.fetchProfile(userId: currentUser.id)
    await MainActor.run {
        self.profile = profile
    }
}

// في أي مكان داخل المهمة:
print("المهمة الحالية: \(Task.name ?? "بدون اسم")")
// المخرج: المهمة الحالية: DownloadUserProfile

تخيّل أنك في Instruments وتشوف 30 Task — بدون أسماء كلها تبدو متشابهة. الآن كل مهمة لها اسم واضح. تكلفة الأداء شبه معدومة والفائدة كبيرة في الـ debugging.

الـ deinit المعزول: Isolated Deinit — SE-0371

أخيرًا! هذه مشكلة أعرف أنها أزعجت كثير من المطوّرين.

المشكلة: لو عندك class معزول على Actor وتريد تنظّف الموارد في الـ deinit، ما كنت تقدر توصل لخصائص الفئة لأن الـ deinit كان دائمًا nonisolated. كان لازم تلجأ لـ workarounds معقّدة.

Swift 6.2 يحل هذا بكلمتين: isolated deinit.

@MainActor
class SessionManager {
    var activeSessions: [Session] = []
    var observer: NSObjectProtocol?
    
    init() {
        observer = NotificationCenter.default.addObserver(
            forName: .sessionExpired,
            object: nil,
            queue: .main
        ) { [weak self] _ in
            self?.handleSessionExpired()
        }
    }
    
    // الآن نقدر نوصل لخصائص الفئة بأمان
    isolated deinit {
        // ✅ آمن — نحن على Main Actor
        for session in activeSessions {
            session.invalidate()
        }
        if let observer = observer {
            NotificationCenter.default.removeObserver(observer)
        }
        activeSessions.removeAll()
    }
    
    func handleSessionExpired() {
        activeSessions.removeAll { $0.isExpired }
    }
}

InlineArray: المصفوفات الثابتة الحجم — SE-0453

هذه ليست ميزة تزامن بحتة، لكنها من أهم الإضافات في Swift 6.2 وتتقاطع مع أداء التطبيقات المتزامنة. InlineArray هي مصفوفة ذات حجم ثابت تُخزّن بياناتها inline بدلًا من تخصيص ذاكرة على الـ Heap.

var colors: InlineArray<4, String> = ["أحمر", "أخضر", "أزرق", "أصفر"]

colors[0] = "برتقالي"
print(colors[1]) // أخضر

// لا يمكن تغيير حجمها
// colors.append("وردي") ← خطأ في الترجمة!

متى تستخدمها؟

  • البيانات ثابتة الحجم: ألوان RGBA (4 قيم)، إحداثيات 3D (3 قيم)، أيام الأسبوع
  • الأداء الحرج: لتجنّب تخصيص الذاكرة الديناميكي في hot loops
  • التوافق مع C: عند التعامل مع هياكل بيانات C ذات المصفوفات الثابتة
// مثال: مصفوفة تحويل 4x4
struct Transform {
    var matrix: InlineArray<16, Float>
    
    static var identity: Transform {
        var t = Transform(matrix: .init(repeating: 0))
        t.matrix[0] = 1
        t.matrix[5] = 1
        t.matrix[10] = 1
        t.matrix[15] = 1
        return t
    }
}

نوع Span: الوصول الآمن للذاكرة — SE-0447

نوع Span الجديد يوفّر طريقة آمنة للوصول إلى ذاكرة متجاورة بدون نسخها. فكّر فيه كـ "نافذة" على بيانات موجودة في مكان آخر — تقرأ منها بدون أن تملكها.

func processBuffer(_ span: Span<UInt8>) {
    for byte in span {
        // معالجة كل بايت بدون نسخ البيانات
    }
}

let data: [UInt8] = [0x48, 0x65, 0x6C, 0x6C, 0x6F]
processBuffer(Span(data))

Span يمنع الأخطاء الشائعة مع UnsafeBufferPointer مع الحفاظ على الأداء. مفيد جدًا إذا كنت تعمل على تطبيقات معالجة صوت أو فيديو أو ألعاب.

القيم الافتراضية في الاستيفاء النصي — SE-0477

ميزة صغيرة وأنيقة — من اللمسات اللطيفة التي تُحسّن تجربة الكتابة اليومية:

struct UserProfile {
    var name: String
    var email: String?
    var age: Int?
    var bio: String?
}

let user = UserProfile(name: "أحمد", email: nil, age: 28, bio: nil)

// الطريقة القديمة
print("البريد: \(user.email ?? "غير محدد")")

// الطريقة الجديدة
print("البريد: \(user.email, default: "غير محدد")")
print("العمر: \(user.age, default: "غير محدد")")
print("النبذة: \(user.bio, default: "لا توجد نبذة")")

الفرق يبدو بسيطًا، لكن default: أوضح في النيّة من ?? داخل الاستيفاء النصي، خاصةً لما تكون السلسلة طويلة ومعقدة.

المُعرّفات الخام: Raw Identifiers — SE-0451

الآن تقدر تستخدم أحرف خاصة في أسماء المُعرّفات. وأحلى استخدام لها (من وجهة نظري) هو في الاختبارات:

enum HTTPStatus: Int {
    case `200` = 200
    case `301` = 301
    case `404` = 404
    case `500` = 500
}

@Test func `المستخدم يمكنه تسجيل الدخول بنجاح`() {
    let result = login(username: "test", password: "pass")
    #expect(result == .success)
}

تخيّل تقرأ نتائج الاختبارات وكل اختبار اسمه جملة واضحة بالعربي. أجمل بكثير من testUserCanLoginSuccessfully.

weak let: المراجع الضعيفة الثابتة — SE-0481

إضافة طال انتظارها فعلًا. الآن تقدر تعرّف مراجع ضعيفة بـ let بدل var:

class ViewController: UIViewController {
    // قبل — لازم var
    weak var delegate: ViewControllerDelegate?
    
    // الآن — يمكن let إذا ما بيتغيّر
    weak let coordinator: AppCoordinator?
    
    init(coordinator: AppCoordinator) {
        self.coordinator = coordinator
        super.init(nibName: nil, bundle: nil)
    }
}

weak let تُعبّر بوضوح عن نيّتك: هذا المرجع يُعيَّن مرة واحدة ولا يتغيّر (طبعًا ممكن يصبح nil لما الكائن يتحرّر من الذاكرة).

مسارات المفاتيح للدوال: Method Key Paths — SE-0479

Key Paths تدعم الآن الدوال وليس فقط الخصائص:

let names = ["أحمد", "فاطمة", "محمد", "عائشة"]

// الطريقة القديمة
let uppercased = names.map { $0.uppercased() }

// الطريقة الجديدة
let uppercased = names.map(\.uppercased())

// مع sorted
let sorted = names.sorted(by: \.localizedCompare)

كود أكثر إيجازًا وقابلية للقراءة. لو تكتب سلاسل تحويلات كثيرة (وهذا شائع في SwiftUI)، هذه الميزة ستوفّر عليك أسطر كثيرة.

التهجير العملي: كيف تنقل مشروعك إلى Swift 6.2

حسنًا، فهمنا الميزات الجديدة. السؤال المهم الآن: كيف أبدأ عمليًا؟

الخطوة 1: تحديث Xcode

تأكّد من استخدام Xcode 16.3 أو أحدث.

الخطوة 2: فعّل الميزات تدريجيًا

لا تُفعّل كل شيء دفعة واحدة — هذه النصيحة من تجربة. ابدأ بميزة واحدة وتأكّد من استقرار المشروع:

// في Package.swift
.target(
    name: "MyApp",
    swiftSettings: [
        // ابدأ بهذه
        .enableUpcomingFeature("InferSendableFromCaptures"),
        
        // ثم أضف هذه
        // .enableUpcomingFeature("GlobalActorIsolatedTypesUsability"),
        
        // وأخيرًا هذه — الأكثر تأثيرًا
        // .enableUpcomingFeature("NonisolatedNonsendingByDefault"),
    ]
)

الخطوة 3: فعّل العزل الافتراضي

.executableTarget(
    name: "MyApp",
    swiftSettings: [
        .defaultIsolation(MainActor.self)
    ]
)

الخطوة 4: أضف @concurrent حيث يلزم

بعد التفعيل، قد تلاحظ أن بعض العمليات الثقيلة أصبحت على الـ Main Thread. أضف @concurrent لها:

// قبل: كانت على خيط خلفي تلقائيًا
func processLargeDataSet() async -> [Result] {
    // عملية ثقيلة...
}

// بعد: @concurrent تضمن العمل على خيط خلفي
@concurrent
func processLargeDataSet() async -> [Result] {
    // عملية ثقيلة...
}

الخطوة 5: راجع الـ deinit

لو كنت تستخدم workarounds في الـ deinit لفئات معزولة، حان وقت استبدالها بـ isolated deinit.

خمس ميزات إضافية تستحق الاهتمام

1. أولوية المهام — SE-0462

let task = Task(priority: .high) {
    await performCriticalUpdate()
}

Task {
    let currentPriority = Task.currentPriority
    if currentPriority < .high {
        await Task(priority: .high) {
            await urgentWork()
        }.value
    }
}

2. المراقبة التحويلية — SE-0475

@Observable
class ShoppingCart {
    var items: [CartItem] = []
    var total: Decimal = 0
}

func observeCart(_ cart: ShoppingCart) async {
    for await change in cart.changes(for: \.items) {
        print("تغيّرت عناصر السلة: \(change.count) عنصر")
        await updateCartUI()
    }
}

3. enumerated() كـ Collection — SE-0459

struct ItemListView: View {
    let items: [Item]
    
    var body: some View {
        ForEach(items.enumerated(), id: \.offset) { index, item in
            HStack {
                Text("\(index + 1).")
                Text(item.name)
            }
        }
    }
}

4. أمان الذاكرة الصارم — SE-0458

@safe
func processInput(_ input: String) -> Int {
    return input.count
}

@unsafe
func directMemoryAccess(_ pointer: UnsafeMutableRawPointer, size: Int) {
    memset(pointer, 0, size)
}

5. اختبارات الخروج — ST-0008

import Testing

@Test func invalidInputCausesPreconditionFailure() async {
    await #expect(processExitsWith: .failure) {
        let array: [Int] = []
        _ = array[5]
    }
}

مثال عملي شامل: تطبيق إدارة مهام

لنجمع كل شيء في مثال واقعي — تطبيق إدارة مهام يستخدم ميزات Swift 6.2. هذا المثال يوضّح كيف تتكامل الميزات مع بعضها في مشروع حقيقي:

// Package.swift settings:
// .defaultIsolation(MainActor.self)
// .enableUpcomingFeature("NonisolatedNonsendingByDefault")

struct TodoItem: Identifiable, Codable, Sendable {
    let id: UUID
    var title: String
    var isCompleted: Bool
    var priority: Priority
    var createdAt: Date
    
    enum Priority: String, Codable, Sendable {
        case low, medium, high
    }
}

// معزول تلقائيًا على Main Actor — بدون كتابة @MainActor
class TodoStore {
    private(set) var items: [TodoItem] = []
    private let apiClient: APIClient
    
    init(apiClient: APIClient) {
        self.apiClient = apiClient
    }
    
    func loadItems() async throws {
        items = try await apiClient.fetchTodos()
    }
    
    func addItem(_ item: TodoItem) async throws {
        items.append(item)
        try await apiClient.saveTodo(item)
    }
    
    func toggleCompletion(for id: UUID) async throws {
        guard let index = items.firstIndex(where: { $0.id == id }) else {
            return
        }
        items[index].isCompleted.toggle()
        try await apiClient.updateTodo(items[index])
    }
}

class APIClient {
    private let baseURL: URL
    
    init(baseURL: URL) {
        self.baseURL = baseURL
    }
    
    func fetchTodos() async throws -> [TodoItem] {
        let url = baseURL.appending(path: "todos")
        let (data, _) = try await URLSession.shared.data(from: url)
        return try JSONDecoder().decode([TodoItem].self, from: data)
    }
    
    func saveTodo(_ item: TodoItem) async throws {
        var request = URLRequest(url: baseURL.appending(path: "todos"))
        request.httpMethod = "POST"
        request.httpBody = try JSONEncoder().encode(item)
        let (_, _) = try await URLSession.shared.data(for: request)
    }
    
    func updateTodo(_ item: TodoItem) async throws {
        var request = URLRequest(
            url: baseURL.appending(path: "todos/\(item.id)")
        )
        request.httpMethod = "PUT"
        request.httpBody = try JSONEncoder().encode(item)
        let (_, _) = try await URLSession.shared.data(for: request)
    }
    
    @concurrent
    func exportTodos(_ items: [TodoItem]) async throws -> Data {
        var csv = "ID,العنوان,مكتمل,الأولوية,تاريخ الإنشاء\n"
        for item in items {
            let completed = item.isCompleted ? "نعم" : "لا"
            csv += "\(item.id),\(item.title),\(completed),"
            csv += "\(item.priority.rawValue),\(item.createdAt)\n"
        }
        return Data(csv.utf8)
    }
}

struct TodoListView: View {
    @State private var store: TodoStore
    @State private var showingAddSheet = false
    @State private var isLoading = true
    
    init(store: TodoStore) {
        _store = State(initialValue: store)
    }
    
    var body: some View {
        NavigationStack {
            Group {
                if isLoading {
                    ProgressView("جارٍ التحميل...")
                } else {
                    List {
                        ForEach(store.items) { item in
                            TodoRowView(item: item) {
                                Task.immediate {
                                    try? await store.toggleCompletion(
                                        for: item.id
                                    )
                                }
                            }
                        }
                    }
                }
            }
            .navigationTitle("مهامي")
            .toolbar {
                Button("إضافة", systemImage: "plus") {
                    showingAddSheet = true
                }
            }
            .task {
                do {
                    try await store.loadItems()
                    isLoading = false
                } catch {
                    print("خطأ: \(error)")
                    isLoading = false
                }
            }
        }
    }
}

لاحظ كيف الكود نظيف. ما كتبنا @MainActor ولا مرة (بفضل العزل الافتراضي)، واستخدمنا @concurrent فقط لعملية التصدير الثقيلة، وTask.immediate للاستجابة الفورية لأحداث المستخدم.

نصائح وأفضل الممارسات

  • ابدأ بالعزل الافتراضي: فعّل defaultIsolation(MainActor.self) في المشاريع الجديدة من البداية. يوفّر عليك annotations كثيرة.
  • استخدم @concurrent بحذر: لا تضعها على كل دالة async. فقط الدوال ذات العمل الحسابي الثقيل تحتاجها. معظم دوال الشبكة لا تحتاج لها لأن الـ await بداخلها يتكفّل بالأمر.
  • فعّل الميزات واحدة تلو الأخرى: في المشاريع القائمة، لا تُفعّل كل شيء دفعة واحدة.
  • استخدم Task.immediate في SwiftUI: لاستجابة فورية لأحداث المستخدم، هي أفضل من Task العادي.
  • سمِّ مهامك: Task(name:) في المهام المهمة يُسهّل الـ debugging بشكل كبير.
  • استخدم isolated deinit: إذا كنت تستخدم workarounds لتنظيف الموارد، استبدلها بـ isolated deinit.

الخلاصة

Swift 6.2 يُمثّل نقطة تحوّل حقيقية. بعد سنوات من الشكاوى حول صعوبة نظام التزامن، استمع فريق Swift وقدّم حلًا عمليًا. الفكرة الجوهرية — الكشف التدريجي — بسيطة لكنها فعّالة: لا تُعقّد حياة المطوّر بمفاهيم ما يحتاجها.

مع العزل الافتراضي، والسلوك الجديد للدوال غير المتزامنة، وسمة @concurrent للتوازي الصريح — كتابة كود متزامن وآمن في Swift أصبحت أسهل من أي وقت مضى.

ابدأ بتفعيل هذه الميزات في مشروعك التالي. ستلاحظ الفرق فورًا: كود أنظف، تحذيرات أقل، وتجربة تطوير أمتع. البرمجة المتزامنة في Swift لم تعد معركة مع المترجم — أصبحت حوار.

عن الكاتب Editorial Team

Our team of expert writers and editors.