0Pricing
Swift Academy · درس

الربط بين الاستدعاءات القديمة وCombine وasync/await

غلّف معالجات الإكمال باستخدام withCheckedContinuation / withCheckedThrowingContinuation ، واربط ناشري Combine بـ AsyncSequence لحلقات for await البسيطة.

الربط بين الاستدعاءات القديمة وCombine وasync/await درس مجاني في Swift Academy على CoddyKit. هذا هو الدرس 2 من أصل 3. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Swift Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Swift Academy 3 دروس في المجموع.

لماذا نستخدم الجسور؟

الهدف: استخدم continuations لتغليف دوال الاستدعاء، وتعامل مع تدفقات Combine بصفتها AsyncSequence.

  • Continuations (غير قابلة لإطلاق الأخطاء / قابلة لإطلاق الأخطاء)
  • مثال على الربط مع URLSession
  • Combine → الاستهلاك باستخدام for await

Continuation غير قابلة لإطلاق الأخطاء

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

// Old-style API
func fetchName(completion: @escaping (String) -> Void) {
    // simulate async
    Task { try? await Task.sleep(nanoseconds: 80_000_000); completion("Ana") }
}

// Async wrapper using continuation (non-throwing)
func fetchName() async -> String {
    await withCheckedContinuation { (cont: CheckedContinuation<String, Never>) in
        fetchName { value in cont.resume(returning: value) }
    }
}

Task {
    let name = await fetchName()
    print("name:", name) // "Ana"
}

Continuation قابلة لإطلاق الأخطاء

استخدم withCheckedThrowingContinuation لإظهار أخطاء الواجهة القديمة عبر throw. وتأكد من وجود مسار استئناف واحد فقط.

enum NetError: Error { case offline; case badStatus }

func loadNumber(completion: @escaping (Result<Int, Error>) -> Void) {
    Task {
        try? await Task.sleep(nanoseconds: 80_000_000)
        completion(.failure(NetError.offline))
    }
}

func loadNumber() async throws -> Int {
    try await withCheckedThrowingContinuation { (cont: CheckedContinuation<Int, Error>) in
        loadNumber { result in
            switch result {
            case .success(let v): cont.resume(returning: v)
            case .failure(let e): cont.resume(throwing: e)
            }
        }
    }
}

Task {
    do {
        let n = try await loadNumber()
        print(n)
    } catch {
        print("error:", error) // offline
    }
}

الربط مع URLSession

غلّف واجهات برمجة التطبيقات ذات معالجات الإكمال (مثل URLSession) للحصول على دالة نظيفة من نوع async throws. تذكّر استئناف التنفيذ مرة واحدة بالضبط.

import Foundation

func getText(from url: URL) async throws -> String {
    try await withCheckedThrowingContinuation { (cont: CheckedContinuation<String, Error>) in
        let task = URLSession.shared.dataTask(with: url) { data, resp, err in
            if let err = err { cont.resume(throwing: err); return }
            guard let data = data, let text = String(data: data, encoding: .utf8) else {
                cont.resume(throwing: URLError(.badServerResponse)); return
            }
            cont.resume(returning: text)
        }
        task.resume()
    }
}

// Demo (works in environments with network access)
let demoURL = URL(string: "https://example.com")!
Task {
    do { let body = try await getText(from: demoURL); print(body.prefix(15)) }
    catch { print("network error:", error) }
}

Combine بوصفه AsyncSequence

يتيح كثير من ناشري Combine الخاصية .values، مما يسمح لك باستخدام for await على القيم المنبعثة دون إنشاء جسور مخصصة.

import Combine
import Foundation

let subject = PassthroughSubject<Int, Never>()

// Consume Combine as AsyncSequence:
Task {
    var produced: [Int] = []
    for await value in subject.values {          // Publisher.values -> AsyncSequence
        produced.append(value)
        if produced.count == 3 { break }         // stop after 3 values
    }
    print("collected:", produced)                // e.g., [1,2,3]
}

// Publish a few values
var cancellables = Set<AnyCancellable>()
Timer.publish(every: 0.05, on: .main, in: .common)
    .autoconnect()
    .prefix(3)
    .scan(0) { acc, _ in acc + 1 }
    .sink { subject.send($0) }
    .store(in: &cancellables)

Publisher → AsyncStream

إذا لم تتوفر .values، فأنشئ جسرًا صغيرًا باستخدام AsyncStream. ولا تنسَ إلغاء الاشتراك عند الانتهاء.

import Combine

// Bridge ANY Publisher to AsyncSequence using AsyncStream (simple version)
extension Publisher {
    func asAsyncStream() -> AsyncStream<Output> {
        AsyncStream { continuation in
            let cancel = self.sink(
                receiveCompletion: { _ in continuation.finish() },
                receiveValue: { value in continuation.yield(value) }
            )
            continuation.onTermination = { _ in cancel.cancel() }
        }
    }
}

// Demo:
let pub = [10, 20, 30].publisher
Task {
    var sum = 0
    for await v in pub.asAsyncStream() { sum += v }
    print("sum:", sum) // 60
}

استخدم continuation قابلة لإطلاق الأخطاء

تحقق سريع: ما أفضل طريقة لربط دالة استدعاء قد تفشل بالأسلوب غير المتزامن؟

مراجعة

مراجعة: غلّف دوال الاستدعاء القديمة باستخدام continuations (قابلة لإطلاق الأخطاء أو غير قابلة لذلك)، واستهلك Combine باستخدام .values أو جسر صغير من AsyncStream. اجعل عمليات الاستئناف أحادية، وألغِ الاشتراكات عند الانتهاء.

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

هل درس «الربط بين الاستدعاءات القديمة وCombine وasync/await» مجاني؟

نعم — نص درس «الربط بين الاستدعاءات القديمة وCombine وasync/await» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Swift Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Swift Academy 3 دروس في المجموع.

ماذا ستتعلم في «الربط بين الاستدعاءات القديمة وCombine وasync/await»؟

غلّف معالجات الإكمال باستخدام withCheckedContinuation / withCheckedThrowingContinuation ، واربط ناشري Combine بـ AsyncSequence لحلقات for await البسيطة. تتمرن على Swift Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ Swift Academy؟

لا تُشترط خبرة سابقة. Swift Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 2 من أصل 3.

كم من الوقت يستغرق درس «الربط بين الاستدعاءات القديمة وCombine وasync/await»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس Swift Academy هذا؟

نعم. كل درس في Swift Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. المنتِج والمستهلك وخطوط المعالجة والمهل الزمنية
  2. الربط بين الاستدعاءات القديمة وCombine وasync/await
  3. اختبار الشيفرة غير المتزامنة (XCTest)
← العودة إلى Swift Academy