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

ชนิดใด ๆ ที่ implement โปรโตคอล Publisher ต้องกำหนด Output (ชนิดของค่า) และ Failure (ชนิดของข้อผิดพลาด) Publisher จะไม่ทำงานจนกว่าจะมีการสมัครรับ — มันจะเริ่มส่งเหตุการณ์หลังจากเรียก subscribe เท่านั้น นี่คือความหมายแบบ cold ซึ่งเป็นลักษณะของสตรีมรีแอกทีฟและเพิ่มประสิทธิภาพ

โปรโตคอล Subscriber

Subscriber รับเหตุการณ์ผ่านสามเมธอด: receive(subscription:) — การยืนยันการสมัครรับพร้อมการจัดการ demand; receive(_:) — การรับค่าใหม่; receive(completion:) — การสิ้นสุดสตรีมด้วยความสำเร็จหรือข้อผิดพลาด Demand กำหนดจำนวนค่าที่ Subscriber พร้อมรับ — นี่คือกลไก backpressure

Publisher ในตัว

Apple มี Publisher ในตัวมากมาย: Just ส่งค่าเดียวแล้วเสร็จสิ้น, Future — ผลลัพธ์แบบอะซิงโครนัสพร้อม closure, Deferred — การสร้าง Publisher ที่เลื่อนออกไปจนกว่าจะมีการสมัครรับ URLSession.dataTaskPublisher แปลงคำขอเครือข่ายเป็นสตรีมรีแอกทีฟ Timer.publish สร้างตัวจับเวลาแบบระยะ periódico NotificationCenter.default.publisher แปลงการแจ้งเตือนเป็น Publisher การผสานรวมเหล่านี้ช่วยให้สมัครรับเหตุการณ์โดยไม่ต้องใช้สะพานเชื่อมด้วยตนเอง

Publisher และ Subscriber ใน Combine

ใน Combine Publisher และ Subscriber เชื่อมต่อกันผ่านโปรโตคอล Subscription Subscription คือออบเจกต์เชื่อมต่อที่ควบคุมการไหลของข้อมูล นักพัฒนาไม่จำเป็นต้องสร้าง Publisher ชนิดของตนเอง — Apple มีในตัวให้: Just, Future, Deferred, Fail, Empty และ Publisher สำหรับ URLSession และ Timer

Subject ใน Combine

Subject — คือ Publisher ที่สามารถส่งค่าด้วยตนเองได้ PassthroughSubject ไม่เก็บสถานะ: ผู้สมัครรับจะได้รับเฉพาะเหตุการณ์ที่เกิดขึ้นหลังจากการสมัครรับ CurrentValueSubject คล้ายกับ BehaviorRelay ใน RxSwift เก็บค่าปัจจุบันและส่งต่อไปยังผู้สมัครรับใหม่ทันที

Backpressure

กลไก backpressure ใน Combine ควบคุมความเร็วในการส่งข้อมูล Subscriber แจ้ง Publisher ผ่าน subscription.request(.unlimited) หรือ subscription.request(.max(N)) ว่าพร้อมประมวลผลกี่ค่า ซึ่งป้องกันบัฟเฟอร์ล้นเมื่อความเร็วของ producer และ consumer ไม่เท่ากัน

ตัวดำเนินการของ Combine

Combine มี มากกว่า 100 ตัวดำเนินการ แบ่งตามหมวดหมู่: การแปลง (map, tryMap, flatMap), การกรอง (filter, compactMap, removeDuplicates), การรวม (combineLatest, merge, zip), การจัดการเวลา (debounce, throttle, delay) และการจัดการข้อผิดพลาด (catch, replaceError, retry)

ตัวดำเนินการ combineLatest รวมค่าล่าสุดของ Publisher สองตัว — ทุกครั้งที่มีตัวใดตัวหนึ่งส่งค่าใหม่ closure จะถูกเรียกด้วยค่าที่อัปเดตทั้งสอง ตัวดำเนินการนี้จำเป็นสำหรับการตรวจสอบความถูกต้องของฟอร์ม ซึ่งต้องติดตามสถานะของช่องป้อนข้อมูลหลายช่องพร้อมกัน

ตัวดำเนินการ debounce ชะลอการเผยแพร่ค่าจนกว่าจะไม่มีเหตุการณ์ใหม่เกิดขึ้นในช่วงเวลาที่กำหนด นี่เป็นสิ่งสำคัญสำหรับแถบค้นหา: คำขอไปยังเซิร์ฟเวอร์จะถูกส่งหลังจากผู้ใช้หยุดพิมพ์ 300-500 ms ซึ่งลดภาระลง 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 ที่รวมอยู่ในระบบ: ไม่ต้องติดตั้ง dependencies, รวมเข้ากับ SwiftUI และ Foundation RxSwift — ไลบรารีของบุคคลที่สามที่รองรับ iOS 9+ และมีชุดตัวดำเนินการที่กว้างกว่า

ประสิทธิภาพของ Combine สูงกว่าในสถานการณ์ทั่วไปเนื่องจากการ implement เนทีฟในภาษา C RxSwift มีเครื่องมือสำหรับการดีบักมากกว่า (RxSwift.Resources.total, Debug) แต่สถาปัตยกรรมเชิงวัตถุอาจมีประสิทธิภาพน้อยกว่าสำหรับสตรีมข้อมูลขนาดใหญ่ การเลือกระหว่างทั้งสองเป็นเรื่องของการประนีประนอมระหว่างความสามารถและการผสานรวม

ความแตกต่างที่สำคัญ — โมเดล backpressure ใน Combine Subscriber จัดการ demand ผ่าน Subscription ซึ่งให้การควบคุมความเร็วของสตรีมอย่างชัดเจน ใน RxSwift backpressure จะไม่ถูกควบคุมโดยค่าเริ่มต้น — Observable ส่งค่าทั้งหมด และ Subscriber ประมวลผลเมื่อได้รับ สำหรับสตรีมข้อมูลขนาดใหญ่ Combine คาดเดาได้ง่ายกว่าและปลอดภัยกว่าในด้านหน่วยความจำ

Combine ถูกรวมเข้ากับ SwiftUI อย่างลึกซึ้งในระดับภาษา @Published สร้าง Publisher, ObservableObject มี objectWillChange Publisher SwiftUI View สมัครรับ Publisher เหล่านี้โดยอัตโนมัติและวาดใหม่เมื่อมีการเปลี่ยนแปลง สำหรับ UIKit มี wrapper ของบุคคลที่สามเช่น CombineCocoa แต่ Combine ถูกออกแบบมาสำหรับ SwiftUI ดังนั้นการผสานรวมใน UIKit จึงต้องใช้โค้ดเพิ่มเติม ในทางกลับกัน RxSwift มีระบบนิเวศที่สมบูรณ์สำหรับ UIKit: RxCocoa มี wrapper เชิงรีแอกทีฟสำหรับองค์ประกอบ UI ทั้งหมด

Combine รองรับการผสานรวมกับ Core Data ผ่าน NSFetchedResultsController Publisher Publisher ส่งข้อมูลใหม่ทุกครั้งที่มีการเปลี่ยนแปลงใน Core Data stack: การเพิ่ม ลบ อัปเดตเรกคอร์ด ช่วยให้สร้างรายการเชิงรีแอกทีฟโดยไม่ต้องเรียก reloadData ด้วยตนเอง การรวม Combine กับ NotificationCenter Publisher สำหรับ UIApplication.willResignActiveNotification ทำให้นักพัฒนาสร้างเลเยอร์รีแอกทีฟสำหรับวงจรชีวิตทั้งหมดของแอปพลิเคชันโดยไม่ต้องมี delegate และ callback สำหรับคลาส ObservableObject, SwiftUI จะอัปเดต View โดยอัตโนมัติเมื่อ @Published properties เปลี่ยนแปลง — 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 ให้การควบคุมที่ละเอียดกว่า: สามารถระบุ closures สำหรับแต่ละขั้นตอนของวงจรชีวิต Publisher สำหรับการทดสอบ มี CombineExpectations — ไลบรารีที่ช่วยตรวจสอบว่า Publisher ส่งค่าที่คาดหวังในการทดสอบด้วยเวลาเสมือนจริงและการควบคุม demand

Combine ในทางปฏิบัติ

Combine ถูกใช้อย่างแข็งขันใน SwiftUI สำหรับการจัดการฟอร์ม: แต่ละ @Published property สร้าง 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 ใช้ objectWillChange Publisher SwiftUI View สมัครรับ @ObservedObject และ @StateObject โดยอัตโนมัติผ่านไปป์ไลน์ของ Combine และวาด View ใหม่เมื่อ properties เปลี่ยนแปลง

Backpressure ใน Combine คืออะไร?

Backpressure — คือกลไกการควบคุมความเร็วในการส่งข้อมูล Subscriber แจ้ง Subscription ว่าพร้อมประมวลผลกี่ค่า (demand) Publisher ไม่สามารถส่งได้มากกว่าที่ร้องขอ สิ่งนี้สำคัญเมื่อทำงานกับสตรีมข้อมูลขนาดใหญ่จากซ็อกเก็ตเครือข่าย

จะจัดการข้อผิดพลาดใน Combine อย่างไร?

ตัวดำเนินการ catch ดักจับข้อผิดพลาดและแทนที่ Publisher ด้วยตัวสำรอง 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
  • เลือก Combine สำหรับโปรเจกต์ใหม่ตั้งแต่ iOS 13, RxSwift สำหรับรองรับเวอร์ชันเก่า

เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร

IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ

ปรึกษาโครงการ

อ่านเพิ่มเติม