@Published คือ property wrapper จากเฟรมเวิร์ก Combine ที่เผยแพร่การเปลี่ยนแปลงของคุณสมบัติของคลาสที่สอดคล้องกับโปรโตคอล ObservableObject โดยอัตโนมัติ เมื่อค่าของคุณสมบัติที่ถูกทำเครื่องหมายด้วย @Published เปลี่ยนแปลง SwiftUI จะรับสัญญาณผ่าน objectWillChange และวาดวิวทั้งหมดที่สมัครรับข้อมูลอ็อบเจ็กต์นั้นใหม่ ตามเอกสาร Apple Combine Framework (2025) @Published สร้าง Publisher ที่สามารถแปลงเพิ่มเติมได้ผ่านโอเปอเรเตอร์ Combine: map, filter, debounce และอื่น ๆ ทำให้ @Published เป็นสะพานสำคัญระหว่างข้อมูลและส่วนติดต่อผู้ใช้ในสถาปัตยกรรม MVVM
ประเด็นสำคัญ
objectWillChange จะถูกเรียก ซึ่งจะกระตุ้นการวาดวิวที่สมัครรับข้อมูลใหม่$property — สามารถสมัคร รวม และแปลงสตรีมได้@Published คือ property wrapper ที่กำหนดในโมดูล Combine ซึ่งเพิ่มความสามารถในการแจ้งผู้สมัครรับข้อมูลโดยอัตโนมัติเกี่ยวกับการเปลี่ยนแปลงของคุณสมบัติของคลาส สามารถใช้ได้เฉพาะภายในคลาสเท่านั้น (ไม่ใช้ในโครงสร้าง) และเฉพาะกับคุณสมบัติของคลาสที่สอดคล้องกับโปรโตคอล ObservableObject
เมื่อค่าคุณสมบัติ @Published เปลี่ยนแปลง Combine จะสร้างอีเวนต์ผ่าน publisher ในตัวที่เข้าถึงได้ผ่านคำนำหน้า dollar: $propertyName publisher นี้คือ ObservableObjectPublisher ซึ่งเป็นของ ObservableObject เอง SwiftUI จะสมัครรับข้อมูลโดยอัตโนมัติเมื่อวิวใช้ @ObservedObject หรือ @StateObject และวาดวิวใหม่เมื่อใดก็ตามที่คุณสมบัติ @Published ภายในอ็อบเจ็กต์เปลี่ยนแปลง
ตามหนังสือของ Matt Neuburg “IOS 18 Programming Fundamentals with Swift” (2025) @Published เป็น wrapper ที่สะดวกบนแพทเทิร์น willSet ซึ่งเรียก objectWillChange.send() โดยอัตโนมัติ จริง ๆ แล้ว คอมไพเลอร์ขยาย @Published เป็น computed property พร้อม willSet observer ซึ่งให้ค่าใช้จ่ายรันไทม์เป็นศูนย์เมื่อเทียบกับการใช้งานด้วยตนเอง
ใช้ @Published สำหรับคุณสมบัติ ObservableObject ทั้งหมดที่การเปลี่ยนแปลงควรสะท้อนในส่วนติดต่อ สำหรับคุณสมบัติที่ไม่ส่งผลต่อ UI การใช้ stored properties ปกติโดยไม่มี @Published จะลดการวาดใหม่ที่ไม่จำเป็น
@Published สร้างองค์ประกอบหลักสองอย่างในเวลาคอมไพล์ อย่างแรกคือ stored property พร้อม willSet observer ที่เรียก objectWillChange.send() ก่อนเขียนค่าใหม่ อย่างที่สองคือโปรเจคชัน $propertyName ซึ่งส่งคืน Published.Publisher ที่สามารถใช้ได้โดยตรงในไปป์ไลน์ Combine
พิจารณาคลาส Settings ที่มีสามคุณสมบัติ: สอง @Published และหนึ่งปกติ:
class Settings: ObservableObject {
@Published var username: String = "Guest"
@Published var isDarkMode = false
var lastLogin: Date = Date() // without @Published
}
เมื่อ username หรือ isDarkMode เปลี่ยน SwiftUI จะวาดวิวทั้งหมดที่สมัครรับข้อมูลอินสแตนซ์ Settings ใหม่ การเปลี่ยน lastLogin จะไม่กระตุ้นการวาดใหม่ หากคุณต้องการแจ้งผู้สมัครรับข้อมูลด้วยตนเองเกี่ยวกับการเปลี่ยนแปลงคุณสมบัติปกติ คุณสามารถเรียก objectWillChange.send() ใน willSet observer
รายละเอียดสำคัญ: @Published เผยแพร่การเปลี่ยนแปลงเฉพาะเมื่อมีการกำหนดค่าให้คุณสมบัติโดยตรง หากคุณสมบัติเป็นชนิดอ้างอิง (คลาส) และสถานะภายในเปลี่ยนแปลงโดยไม่แทนที่การอ้างอิง @Published จะไม่ตรวจพบ ในกรณีเช่นนี้ จำเป็นต้องส่งอีเวนต์ด้วยตนเองหรือเปลี่ยนเป็นชนิดค่า (โครงสร้าง)
@Published ถูกรวมเข้ากับ Combine อย่างแน่นหนา — แต่ละคุณสมบัติ @Published จะให้ publisher โดยอัตโนมัติที่เข้าถึงได้ผ่านโปรเจคชัน $propertyName ซึ่งอนุญาตให้ใช้โอเปอเรเตอร์ Combine สำหรับการกรอง การแปลง การรวม และการประมวลผลค่าที่เลื่อนออกไป
สถานการณ์ทั่วไปคือการค้นหาแบบ debounce ฟิลด์อินพุตถูกผูกกับคุณสมบัติ @Published searchText แต่คำขอไปยังเซิร์ฟเวอร์ควรส่งหลังจากหยุด 300 มิลลิวินาทีเท่านั้น Combine กับ $searchText.debounce แก้ปัญหานี้ในบรรทัดเดียว:
class SearchViewModel: ObservableObject {
@Published var searchText = ""
@Published var results: [String] = []
private var cancellables = Set<AnyCancellable>()
init() {
setupSearchSubscription()
}
private func setupSearchSubscription() {
$searchText
.debounce(for: .milliseconds(300), scheduler: RunLoop.main)
.removeDuplicates()
.sink { [weak self] text in
self?.performSearch(text)
}
.store(in: &cancellables)
}
private func performSearch(_ text: String) { }
}
ตามบทความของ John Sundell (Swift by Sundell, 2024) การรวม @Published กับ Combine เป็นแพทเทิร์นมาตรฐานสำหรับไปป์ไลน์เชิงโต้ตอบในแอปพลิเคชัน SwiftUI: การตรวจสอบ, debounce, throttle, combineLatest, merge กับ publishers อื่น @Published ทำหน้าที่เป็นสะพานระหว่างโค้ด UI แบบจำเป็นและ Combine แบบเชิงโต้ตอบ
ด้วยการเปิดตัว iOS 17 Apple ได้แนะนำมาโคร @Observable ซึ่งนำเสนอวิธีการอื่นสำหรับการโต้ตอบโดยไม่ต้องใช้ ObservableObject และ @Published @Observable จะติดตามการเข้าถึงคุณสมบัติโดยอัตโนมัติในระดับการอ่านแทนการเขียน ให้การวาดใหม่ที่แม่นยำยิ่งขึ้น — เฉพาะวิวที่อ่านคุณสมบัติที่เปลี่ยนแปลงเท่านั้นที่ถูกอัปเดต
อย่างไรก็ตาม นี่ไม่ได้หมายความว่า @Published ล้าสมัย @Published ยังคงจำเป็นเมื่อต้องการการรวมไปป์ไลน์ Combine — โปรเจคชัน $propertyName ให้ publisher ที่ @Observable ไม่มี นอกจากนี้ สำหรับความเข้ากันได้ย้อนหลังกับ iOS 16 และต่ำกว่า @Published+ObservableObject เป็นตัวเลือกเดียว ตามเซสชัน Apple WWDC 2023 “Discover Observation in SwiftUI” Apple แนะนำ @Observable สำหรับโปรเจกต์ใหม่ แต่ยังคงสนับสนุน @Published อย่างชัดเจนสำหรับโค้ดที่มีอยู่และสถานการณ์ Combine
ในทางปฏิบัติ หลายโปรเจกต์ใช้วิธีการแบบผสม: โมเดลข้อมูลใหม่ใช้ @Observable ในขณะที่ ObservableObject ที่มีอยู่กับ @Published ยังคงอยู่โดยไม่ปรับโครงสร้าง @Published ยังเป็นสิ่งจำเป็นเมื่อต้องการควบคุมการเผยแพร่อย่างละเอียด — ตัวอย่างเช่น เลื่อนการแจ้งเตือนจนกว่าการอัปเดตแบบแบตช์ของคุณสมบัติหลายอย่างจะเสร็จสมบูรณ์
ข้อผิดพลาดแรกคือ การใช้ @Published ในโครงสร้าง คอมไพเลอร์จะแสดงข้อผิดพลาด: “Property wrapper cannot be applied to a computed property” หรือ “‘@Published’ is only available on members of a class.” @Published ต้องมีความหมายแบบอ้างอิงเพราะ ObservableObjectPublisher เป็นคลาสที่ต้องไม่ซ้ำกันสำหรับแต่ละอินสแตนซ์
ข้อผิดพลาดที่สองคือ การเปลี่ยนเนื้อหาของคุณสมบัติอ้างอิง โดยไม่แทนที่การอ้างอิง หากคุณสมบัติ @Published เป็นชนิดอาร์เรย์ [String] และคุณเรียก array.append("ใหม่") @Published จะไม่ตรวจพบการเปลี่ยนแปลงเนื่องจากการอ้างอิงถึงอาร์เรย์ไม่เปลี่ยนแปลง วิธีแก้: กำหนดค่าใหม่ให้คุณสมบัติ array = array + ["ใหม่"] หรือใช้ objectWillChange.send() ด้วยตนเอง
ข้อผิดพลาดที่สามคือ จำนวนคุณสมบัติ @Published มากเกินไป แต่ละคุณสมบัติ @Published จะกระตุ้นการวาดใหม่ของวิวทั้งหมดที่สมัครรับข้อมูล ObservableObject ไม่ใช่เฉพาะวิวที่อ่านคุณสมบัตินั้น ตาม Point-Free (2025) การแบ่ง ObservableObject ขนาดใหญ่หนึ่งอันเป็นอันเล็กหลายอันด้วย @StateObject และ @EnvironmentObject ช่วยลดการวาดใหม่ที่ไม่จำเป็นและปรับปรุงประสิทธิภาพ
ตัวอย่างแรกคือ ViewModel ของฟอร์มลงทะเบียน พร้อมการตรวจสอบ คุณสมบัติ @Published email และ password กระตุ้นการแสดงข้อผิดพลาดการตรวจสอบผ่านไปป์ไลน์ Combine:
class RegistrationViewModel: ObservableObject {
@Published var email = ""
@Published var password = ""
@Published var emailError: String?
@Published var isFormValid = false
private var cancellables = Set<AnyCancellable>()
init() {
$email
.map { $0.contains("@") ? nil : "Invalid email" }
.assign(to: &$emailError)
.store(in: &cancellables)
$email.combineLatest($password)
.map { !$0.isEmpty && !$1.isEmpty }
.assign(to: &$isFormValid)
.store(in: &cancellables)
}
}
ตัวอย่างที่สองคือ การเผยแพร่ด้วยตนเอง สำหรับคอลเลกชันขององค์ประกอบอ้างอิง แทนที่จะแทนที่อาร์เรย์ทั้งหมดทุกครั้งที่มีการเปลี่ยนแปลงภายในองค์ประกอบ จะใช้ objectWillChange.send():
class TodoItem {
var title: String
var isDone = false
init(title: String) { self.title = title }
}
class TodoListViewModel: ObservableObject {
@Published var items: [TodoItem] = []
func toggle(item: TodoItem) {
item.isDone.toggle()
self.objectWillChange.send() // manual notification
}
}
ตัวอย่างที่สามคือ Assign ไปยังคุณสมบัติ @Published ผ่าน Combine การใช้ไวยากรณ์ Swift 5.9 ใหม่ คุณสามารถกำหนดโดยตรงผ่านโปรเจคชัน assign(to: &$property) โดยไม่ต้องใช้ Optional wrapper นี่เป็นวิธีที่สั้นที่สุดในการเชื่อมต่อ publisher กับคุณสมบัติ @Published โดยไม่ต้องสร้างการสมัครรับข้อมูล
คำถามที่พบบ่อย
ไม่ @Published สามารถใช้ได้เฉพาะภายในคลาสที่สอดคล้องกับ ObservableObject ในโครงสร้าง ให้ใช้ @State สำหรับสถานะภายในหรือ @Bindable กับมาโคร @Observable ใน iOS 17+ การพยายามใช้ @Published ในโครงสร้างจะทำให้เกิดข้อผิดพลาดในการคอมไพล์
วิธีการที่ถูกต้อง: กำหนดค่าใหม่ทั้งหมด (array = array + ["ใหม่"]) @Published ติดตามการแทนที่การอ้างอิง ไม่ใช่การเปลี่ยนแปลงเนื้อหา สำหรับคอลเลกชันของชนิดอ้างอิง ให้ใช้การเรียก objectWillChange.send() ด้วยตนเองหลังจากเปลี่ยนสถานะภายในขององค์ประกอบ
@State ออกแบบมาสำหรับสถานะภายในวิวเดียวและทำงานกับชนิดค่าเท่านั้น @Published สำหรับคุณสมบัติ ObservableObject ที่สามารถอ่านได้โดยหลายวิวผ่าน @ObservedObject หรือ @EnvironmentObject @State ง่ายกว่า @Published มีประสิทธิภาพมากกว่าด้วยการรวม Combine
เฉพาะคุณสมบัติที่ การเปลี่ยนแปลงควรอัปเดต UI คุณสมบัติสำหรับการคำนวณภายใน แคช หรือแฟล็กชั่วคราวไม่จำเป็นต้องมี @Published — ซึ่งช่วยลดการวาดใหม่ที่ไม่จำเป็น ใช้ @Published เป็นสัญญาณว่า “คุณสมบัตินี้สำคัญสำหรับส่วนติดต่อ”
SwiftUI รวมกับ Core Data ผ่าน @FetchRequest และ @ObservedObject สำหรับ NSManagedObject ManagedObject สอดคล้องกับ ObservableObject อยู่แล้ว ดังนั้นไม่จำเป็นต้องใช้ @Published — NSManagedObject แจ้งการเปลี่ยนแปลงด้วยตัวเอง @Published ใช้ในเลเยอร์ ViewModel ระหว่าง Core Data และ UI สำหรับการแปลงข้อมูล
สรุป
objectWillChange.send() สร้าง publisher ผ่านโปรเจคชัน $propertyassign(to: &$property) ใน Swift 5.9 อนุญาตให้สมัคร publisher โดยตรงกับคุณสมบัติ @Publishedเราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม