Combine: چیست، مفاهیم کلیدی و Publisher با Subscriber

نویسنده: IT Sectr منتشر شده: 2026-03-17 زمان مطالعه: 8 دقیقه

Combine یک فریم‌ورک برنامه‌نویسی واکنشی از Apple است که در iOS 13 معرفی شد. به گفته Apple Documentation, 2025, Combine یک API اعلامی واحد برای پردازش رویدادهای ناهمگام ارائه می‌دهد. Publisher منبع داده را مشخص می‌کند و Subscriber برای دریافت آن‌ها مشترک می‌شود.

نکات اصلی

  • Combine — فریم‌ورک داخلی Apple برای برنامه‌نویسی واکنشی، قابل دسترس از iOS 13
  • Publisher — پروتکلی که منبع مقادیر Output را با امکان خطای Failure مشخص می‌کند
  • Subscriber — پروتکلی که مقادیر را از Publisher با demand مدیریت‌شده دریافت می‌کند
  • Subject — Publisher که امکان درج دستی رویدادها را فراهم می‌کند (PassthroughSubject، CurrentValueSubject)
  • Operators — map, filter, combineLatest, flatMap برای تبدیل و ترکیب جریان‌ها

Combine چیست؟

Combine یک فریم‌ورک اعلامی Swift برای پردازش رویدادهای ناهمگام در طول زمان است. برخلاف رویکردهای مبتنی بر callback، Combine امکان توصیف خطوط لوله پردازش داده را از طریق زنجیره‌ای از عملگرها فراهم می‌کند. این فریم‌ورک با Foundation (URLSession، Timer، NotificationCenter) و SwiftUI (ObservableObject، @Published) یکپارچه شده است.

پروتکل Publisher

هر نوعی که پروتکل Publisher را پیاده‌سازی می‌کند، باید Output (نوع مقادیر) و Failure (نوع خطا) را مشخص کند. Publisher تا زمان اشتراک فعال نیست — فقط پس از فراخوانی subscribe شروع به انتشار رویدادها می‌کند. این معنای سرد است که مشخصه جریان‌های واکنشی است و کارایی را افزایش می‌دهد.

پروتکل Subscriber

Subscriber رویدادها را از طریق سه روش دریافت می‌کند: receive(subscription:) — تأیید اشتراک با مدیریت demand; receive(_:) — دریافت مقدار جدید; receive(completion:) — پایان جریان با موفقیت یا خطا. Demand تعداد مقادیری را که Subscriber آماده دریافت است مشخص می‌کند — این مکانیزم backpressure است.

Publisherهای داخلی

Apple Publisherهای داخلی متعددی ارائه می‌دهد: Just یک مقدار منتشر می‌کند و پایان می‌یابد، Future — نتیجه ناهمگام با بسته، Deferred — ایجاد تأخیری Publisher تا لحظه اشتراک. URLSession.dataTaskPublisher درخواست شبکه را به جریان واکنشی تبدیل می‌کند. Timer.publish یک تایمر دوره‌ای ایجاد می‌کند. NotificationCenter.default.publisher اعلان‌ها را به Publisher تبدیل می‌کند. این یکپارچگی‌ها امکان اشتراک در رویدادها را بدون پل دستی فراهم می‌کنند.

Publisher و Subscriber در Combine

در Combine Publisher و Subscriber توسط پروتکل Subscription به هم متصل می‌شوند. Subscription یک شیء پیونددهنده است که جریان داده را کنترل می‌کند. توسعه‌دهنده نیازی به ایجاد انواع Publisher خود ندارد — Apple Publisherهای داخلی Just, Future, Deferred, Fail, Empty و همچنین Publisher برای URLSession و Timer را فراهم کرده است.

Subject در Combine

Subject یک Publisher است که می‌توان به صورت دستی مقادیر را به آن ارسال کرد. PassthroughSubject وضعیت را ذخیره نمی‌کند: مشترک فقط رویدادهایی را دریافت می‌کند که پس از اشتراک رخ داده‌اند. CurrentValueSubject مشابه BehaviorRelay در RxSwift، مقدار فعلی را ذخیره می‌کند و بلافاصله آن را به مشترک جدید منتقل می‌کند.

Backpressure

مکانیزم backpressure در Combine سرعت انتقال داده را تنظیم می‌کند. Subscriber از طریق subscription.request(.unlimited) یا subscription.request(.max(N)) به Publisher اطلاع می‌دهد که چند مقدار آماده پردازش است. این کار از سرریز بافر در سرعت نابرابر تولیدکننده و مصرف‌کننده جلوگیری می‌کند.

عملگرهای Combine

Combine شامل بیش از 100 عملگر تقسیم‌بندی شده به دسته‌ها: تبدیل (map, tryMap, flatMap)، فیلتر (filter, compactMap, removeDuplicates)، ترکیب (combineLatest, merge, zip)، مدیریت زمان (debounce, throttle, delay) و مدیریت خطا (catch, replaceError, retry) است.

عملگر combineLatest آخرین مقادیر دو Publisher را ترکیب می‌کند — هر بار که هر یک از آن‌ها مقدار جدیدی منتشر می‌کند، بسته با هر دو مقدار به‌روز فراخوانی می‌شود. این عملگر در اعتبارسنجی فرم‌ها که نیاز به ردیابی وضعیت چندین فیلد ورودی همزمان است ضروری می‌باشد.

عملگر debounce انتشار مقادیر را تا زمانی که فاصله مشخصی بدون رویداد جدید بگذرد به تأخیر می‌اندازد. این برای نوارهای جستجو حیاتی است: درخواست به سرور فقط پس از توقف تایپ کاربر به مدت 300-500 میلی‌ثانیه ارسال می‌شود که بار را 5-10 برابر کاهش می‌دهد.

مدیریت خطا

Combine چندین استراتژی مدیریت خطا در سطح عملگرها ارائه می‌دهد. catch Failure را گرفته و Publisher را با یک جایگزین تعویض می‌کند — جریان بدون کرش ادامه می‌یابد. replaceError(with:) یک مقدار پیش‌فرض را به جای خطا قرار می‌دهد. retry(_:) اشتراک را در صورت خطا به تعداد مشخصی تکرار می‌کند. مهم: نوع خطا Never در امضای Publisher تضمین می‌کند که جریان هرگز با خطا خاتمه نمی‌یابد — این امکان استفاده از Publisher در SwiftUI بدون مدیریت Failure را فراهم می‌کند.

نمونه کدهای Combine

اولین نمونه کار پایه با Just — Publisher که یک مقدار منتشر می‌کند و پایان می‌یابد:

swift
let publisher = Just("Hello Combine")

publisher
    .sink(receiveCompletion: { completion in
        print("تکمیل شد: \(completion)")
    }, receiveValue: { value in
        print("دریافت شد: \(value)")
    })

دومین نمونه یکپارچگی Combine با URLSession برای درخواست شبکه با پردازش نتیجه در نخ اصلی را نشان می‌دهد:

swift
let url = URL(string: "https://api.example.com")!

URLSession.shared
    .dataTaskPublisher(for: url)
    .map { $0.data }
    .receive(on: DispatchQueue.main)
    .sink(receiveCompletion: { completion in
        if case .failure(let error) = completion {
            print("خطا: \(error)")
        }
    }, receiveValue: { data in
        print("داده دریافت شد: \(data.count)")
    })

سومین نمونه — استفاده از CurrentValueSubject برای مدیریت وضعیت:

swift
let counter = CurrentValueSubject<Int, Never>(0)

counter
    .sink { value in
        print("شمارنده: \(value)")
    }

counter.send(1) // نمایش می‌دهد: شمارنده: 1
counter.send(2) // نمایش می‌دهد: شمارنده: 2
print(counter.value) // نمایش می‌دهد: 2

Combine در مقابل RxSwift

Combine و RxSwift یک مسئله را حل می‌کنند — برنامه‌نویسی واکنشی، اما با رویکردهای متفاوت. Combine یک فریم‌ورک بومی Apple است که در سیستم تعبیه شده: نیاز به نصب وابستگی‌ها ندارد، با SwiftUI و Foundation یکپارچه است. RxSwift یک کتابخانه شخص ثالث با پشتیبانی از iOS 9+ و مجموعه گسترده‌تری از عملگرها است.

عملکرد Combine در سناریوهای معمولی به دلیل پیاده‌سازی بومی در C بالاتر است. RxSwift ابزارهای بیشتری برای اشکال‌زدایی فراهم می‌کند (RxSwift.Resources.total, Debug)، اما معماری شی‌گرای آن ممکن است در جریان‌های داده بزرگ کارایی کمتری داشته باشد. انتخاب بین آن‌ها سازشی بین قابلیت‌ها و یکپارچگی است.

تفاوت مهم — مدل backpressure است. در Combine Subscriber demand را از طریق Subscription مدیریت می‌کند که کنترل صریح بر سرعت جریان می‌دهد. در RxSwift backpressure به طور پیش‌فرض کنترل نمی‌شود — Observable همه مقادیر را منتشر می‌کند و Subscriber آن‌ها را هنگام دریافت پردازش می‌کند. برای جریان‌های داده بزرگ Combine قابل‌پیش‌بینی‌تر و از نظر حافظه ایمن‌تر است.

Combine در سطح زبان با SwiftUI یکپارچه شده است. @Published یک Publisher تولید می‌کند، ObservableObject Publisher objectWillChange را فراهم می‌کند. SwiftUI View به طور خودکار در این Publisherها مشترک می‌شود و در هنگام تغییرات دوباره ترسیم می‌کند. برای UIKit پوشش‌های شخص ثالث مانند CombineCocoa وجود دارد، اما Combine در اصل برای SwiftUI طراحی شده است، بنابراین یکپارچگی آن در UIKit به کد اضافی نیاز دارد. RxSwift در مقابل، اکوسیستم غنی برای UIKit دارد: RxCocoa پوشش‌های واکنشی برای تمام عناصر UI فراهم می‌کند.

Combine از یکپارچگی با Core Data از طریق NSFetchedResultsController Publisher پشتیبانی می‌کند. Publisher با هر تغییری در پشته Core Data — افزودن، حذف، به‌روزرسانی رکوردها — داده‌های جدید منتشر می‌کند. این امکان ساخت لیست‌های واکنشی بدون فراخوانی دستی reloadData را فراهم می‌کند. با ترکیب Combine با NotificationCenter Publisher برای UIApplication.willResignActiveNotification، توسعه‌دهنده یک لایه واکنشی برای کل چرخه حیات برنامه بدون دلیگیت‌ و callback ایجاد می‌کند. برای کلاس‌های ObservableObject، SwiftUI به طور خودکار View را در هنگام تغییر خصوصیات @Published به‌روز می‌کند — Combine تمام اتصال را بدون یک خط کد دستی فراهم می‌کند. تست ماژولار کد Combine از طریق XCTestExpectation و Publisher.sink در موارد تست با جداسازی Scheduler از طریق ImmediateScheduler انجام می‌شود.

هنگام کار با Combine در پروژه‌های UIKit، توسعه‌دهندگان اغلب از اشتراک از طریق sink استفاده می‌کنند و AnyCancellable را در Set ذخیره می‌کنند. الگوی معمول: ViewController یک view ایجاد می‌کند، cancellables را ذخیره می‌کند، در Publisher از ViewModel مشترک می‌شود. در deinit همه اشتراک‌ها به طور خودکار از طریق مجموعه Cancellables لغو می‌شوند. برای کار صحیح با UIControl در Combine می‌توان از UIControl.Event Publisher از طریق توسعه‌دهنده publisher(for:) استفاده کرد.

اشکال‌زدایی Combine از طریق عملگر print() یا handleEvents() انجام می‌شود. print(String) تمام رویدادها را ثبت می‌کند: receive subscription, request demand, receive value, receive completion. handleEvents کنترل دقیق‌تری می‌دهد: می‌توان بسته‌هایی برای هر مرحله از چرخه حیات Publisher مشخص کرد. برای تست Combine، CombineExpectations وجود دارد — کتابخانه‌ای که امکان بررسی انتشار مقادیر مورد انتظار توسط Publisher در تست‌ها با زمان مجازی و کنترل demand را فراهم می‌کند.

Combine در عمل

Combine به طور فعال در SwiftUI برای مدیریت فرم‌ها استفاده می‌شود: هر خصوصیت @Published یک Publisher تولید می‌کند، SwiftUI از طریق View.body در آن مشترک می‌شود. با ترکیب چندین Publisher از طریق combineLatest، اعتبارسنجی فرم اعلامی می‌شود: هر Publisher یک فیلد را ردیابی می‌کند، combineLatest تمام مقادیر را جمع‌آوری می‌کند، map آن‌ها را بررسی کرده و وضعیت دکمه Submit را برمی‌گرداند. Publishers.Merge چندین Publisher از یک نوع را در یکی ترکیب می‌کند — برای جمع‌آوری رویدادها از کنترل‌های مختلف در یک جریان مفید است.

برای یکپارچگی Combine با UIKit از UIControl Publisher از طریق توسعه‌دهنده استفاده کنید: button.publisher(for: .touchUpInside) یک Publisher برمی‌گرداند که هنگام فشار رویداد منتشر می‌کند. CombineCocoa — یک کتابخانه اجتماعی است که Publisher برای تمام رویدادهای UIControl، UITextView.text، UIScrollView.contentOffset فراهم می‌کند. هنگام لغو اشتراک، cancellables Store در deinit پاک می‌شود و تضمین می‌کند که هیچ اشتراکی از مالک خود بیشتر زنده نمی‌ماند. برای UIControl با وضعیت‌های متعدد از Publishers.MergeMany برای ترکیب Publisherها در یک آرایه استفاده کنید.

سوالات متداول

Combine از چه نسخه iOS در دسترس است؟

Combine از iOS 13, macOS 10.15, tvOS 13 و watchOS 6 در دسترس است. این امر استفاده از آن را در پروژه‌هایی که نیاز به پشتیبانی از نسخه‌های قدیمی‌تر دارند محدود می‌کند. برای iOS 12 و پایین‌تر از RxSwift یا کتابخانه‌های شخص ثالث استفاده می‌شود.

تفاوت PassthroughSubject با CurrentValueSubject چیست؟

PassthroughSubject وضعیت را ذخیره نمی‌کند و آخرین مقدار را به مشترکان جدید تکرار نمی‌کند — آن‌ها فقط رویدادهای آینده را دریافت می‌کنند. CurrentValueSubject مقدار فعلی را ذخیره می‌کند و بلافاصله آن را به مشترک جدید منتقل می‌کند. CurrentValueSubject.value امکان خواندن و نوشتن مقدار فعلی به صورت همزمان را فراهم می‌کند.

چگونه Combine با SwiftUI یکپارچه می‌شود؟

Combine در پایه SwiftUI قرار دارد: @Published یک Publisher تولید می‌کند، ObservableObject از Publisher objectWillChange استفاده می‌کند. SwiftUI View به طور خودکار در @ObservedObject و @StateObject از طریق خط لوله Combine مشترک می‌شود و View را در هنگام تغییر خصوصیات دوباره ترسیم می‌کند.

backpressure در Combine چیست؟

Backpressure — مکانیزم مدیریت سرعت انتقال داده است. Subscriber به Subscription اطلاع می‌دهد که چند مقدار آماده پردازش است (demand). Publisher نمی‌تواند بیش از مقدار درخواست شده منتشر کند. این هنگام کار با جریان‌های داده بزرگ از سوکت‌های شبکه حیاتی است.

چگونه خطاها در Combine مدیریت می‌شوند؟

عملگر catch خطا را گرفته و Publisher را با fallback جایگزین می‌کند. replaceError خطا را با مقدار پیش‌فرض جایگزین می‌کند. retry اشتراک را تعداد مشخصی بار در صورت خطا تکرار می‌کند. تمام عملگرها باید قبل از subscribe قرار داده شوند تا از کرش در Failure جلوگیری شود.

خلاصه

  • Combine — فریم‌ورک داخلی Apple برای برنامه‌نویسی واکنشی از iOS 13
  • Publisher — پروتکل با Output و Failure، پس از اشتراک شروع به کار می‌کند
  • Subscriber — مقادیر را از طریق receive با پشتیبانی از demand دریافت می‌کند
  • Subject (PassthroughSubject, CurrentValueSubject) — Publisher با درج دستی رویدادها
  • عملگرها map, filter, combineLatest, debounce, catch — اساس خط لوله
  • Backpressure از طریق demand از سرریز بافر Subscriber جلوگیری می‌کند
  • برای پروژه‌های جدید از iOS 13 به بالا Combine را انتخاب کنید، برای پشتیبانی از نسخه‌های قدیمی RxSwift

ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد

IT Sectr از سال 2017 برنامه‌های iOS و Android را برای استارتاپ‌ها و کسب‌وکارها ایجاد می‌کند. ما به شما مشاوره می‌دهیم و بهترین راه‌حل را پیشنهاد خواهیم کرد.

بحث درباره پروژه

همچنین بخوانید