NotificationCenter — adalah mekanisme sistem iOS untuk mengirim dan menerima pemberitahuan antar komponen aplikasi tanpa koneksi langsung antara pengirim dan penerima. Berdasarkan pola Observer, NotificationCenter memungkinkan objek berlangganan peristiwa dan bereaksi secara asinkron. Menurut Apple Documentation (2025), NSNotificationCenter mendukung pengiriman pemberitahuan sinkron melalui post(name:object:) maupun pengiriman tertunda melalui NotificationQueue. Pusat pemberitahuan bekerja dalam satu proses dan tidak melintasi batas aplikasi.
Poin utama
NotificationCenter (NSNotificationCenter) — adalah mekanisme bawaan iOS untuk mengimplementasikan komunikasi terhubung longgar antar objek. Pola Observer memungkinkan satu objek (pengirim) memberi tahu banyak objek lain (pengamat) tentang terjadinya peristiwa tanpa referensi langsung ke mereka. NotificationCenter beroperasi dengan tiga entitas: Notification.Name (pengidentifikasi pemberitahuan), Notification (wadah data) dan NotificationCenter (dispatcher). Setiap aplikasi memiliki default center bersama.
Notification.Name — adalah struktur yang mengidentifikasi jenis pemberitahuan. Dibuat melalui extension Name: Notification.Name("MyNotification"). Notification — adalah objek yang berisi name, object (pengirim) dan userInfo (kamus data). Pemberitahuan sistem dideklarasikan sebagai konstanta: UIApplication.didBecomeActiveNotification, UIResponder.keyboardWillShowNotification. Pemberitahuan kustom harus dikelompokkan melalui extension untuk menghindari benturan nama. Nama harus terbalik-domain.
// Mendefinisikan pemberitahuan kustom
extension Notification.Name {
static let dataDidUpdate =
Notification.Name("com.app.dataDidUpdate")
static let userLoggedOut =
Notification.Name("com.app.userLoggedOut")
}
// Mengirim pemberitahuan dengan data
let userInfo: [String: Any] = [
"userId": 123,
"timestamp": Date()
]
NotificationCenter.default.post(
name: .dataDidUpdate,
object: nil,
userInfo: userInfo
)
Pengamat berlangganan pemberitahuan melalui metode addObserver(_:selector:name:object:). Selector — metode yang akan dipanggil saat menerima pemberitahuan. Parameter object memungkinkan menyaring pemberitahuan dari pengirim tertentu. Jika object nil, pengamat menerima semua pemberitahuan dengan nama yang ditentukan dari pengirim mana pun. Mulai iOS 9, addObserver tidak memerlukan penghapusan manual untuk block-based API, tetapi selector-based masih memerlukan removeObserver.
// Berlangganan pemberitahuan (selector-based)
NotificationCenter.default.addObserver(
self,
selector: #selector(handleDataUpdate),
name: .dataDidUpdate,
object: nil
)
@objc func handleDataUpdate(_ notification: Notification) {
guard let userId = notification.userInfo?["userId"] as? Int else { return }
updateUI(for: userId)
}
// Berlangganan pemberitahuan (block-based, iOS 9+)
var observer: NSObjectProtocol?
observer = NotificationCenter.default.addObserver(
forName: .dataDidUpdate,
object: nil,
queue: .main
) { [weak self] notification in
guard let self else { return }
self.handleNotification(notification)
}
NotificationCenter menyimpan tabel pemetaan (name → kumpulan pengamat). Saat pengirim memanggil post(name:object:), pusat pemberitahuan secara sinkron menelusuri semua pengamat yang berlangganan nama ini dan memanggil selector atau blok mereka. Fitur utama: post memblokir thread saat ini hingga semua handler selesai. Jika handler menjalankan operasi berat, ini akan menunda pengirim. NotificationQueue memecahkan masalah ini dengan menunda pengiriman pemberitahuan.
Metode post(name:object:userInfo:) mengirim pemberitahuan segera ke semua pengamat. Panggilan bersifat sinkron — kode setelah post hanya dijalankan setelah semua handler selesai. Urutan panggilan pengamat tidak dijamin dan dapat berubah antar eksekusi. Untuk pemrosesan berurutan gunakan NotificationQueue dengan coalescing. Jangan memanggil post di dalam handler pemberitahuan yang sama — ini menyebabkan rekursi tak terbatas.
NotificationQueue menambahkan pemberitahuan ke antrian untuk pengiriman asinkron. Mendukung coalescing (penggabungan pemberitahuan identik) dan pemilihan antrian pengiriman (asap, idle, modal). Coalescing berguna untuk peristiwa sering (kemajuan pemuatan), ketika perlu memberi tahu hanya dengan nilai terakhir. NotificationQueue menggunakan run loop untuk aktivasi, sehingga hanya berfungsi di thread dengan run loop aktif.
// Pengiriman tertunda melalui NotificationQueue
let notification = Notification(
name: .dataDidUpdate,
object: self,
userInfo: ["progress": 0.5]
)
// Coalescing: beberapa pemberitahuan digabung menjadi satu
NotificationQueue.default.enqueue(
notification,
postingStyle: .whenIdle,
coalesceMask: .onName,
forModes: [.common]
)
// Pengiriman asinkron melalui DispatchQueue
DispatchQueue.main.async {
NotificationCenter.default.post(name: .dataDidUpdate, object: nil)
}
iOS menyediakan tiga mekanisme utama untuk komunikasi antar objek: NotificationCenter, Delegate dan KVO (Key-Value Observing). Masing-masing memecahkan masalah pemberitahuan, tetapi dengan kompromi berbeda dalam hal keterhubungan, kinerja, dan keamanan tipe. Pemilihan mekanisme tergantung pada hubungan satu-ke-satu atau satu-ke-banyak dan kebutuhan transfer data.
| Karakteristik | NotificationCenter | Delegate | KVO |
|---|---|---|---|
| Keterhubungan | Longgar (nama pemberitahuan) | Kuat (protokol) | Sedang (kunci) |
| Hubungan | Satu-ke-banyak | Satu-ke-satu | Satu-ke-banyak |
| Keamanan tipe | Rendah (userInfo sebagai Dictionary) | Tinggi (metode protokol) | Sedang (Any?) |
| Kinerja | Sedang (penelusuran tabel) | Tinggi (panggilan langsung) | Rendah (NSObject) |
| Asinkronitas | Sinkron (post memblokir) | Sinkron di thread pengirim | Sinkron saat perubahan |
NotificationCenter ideal untuk peristiwa yang harus ditanggapi oleh beberapa komponen independen. Contoh: perubahan pengaturan aplikasi, logout pengguna, menerima pemberitahuan push di latar belakang. NotificationCenter juga cocok untuk modul yang terhubung longgar (fitur A tidak perlu tahu tentang fitur B). Kekurangan — tidak ada keamanan tipe: kunci userInfo adalah string, bukan enum.
Delegate pilih untuk hubungan satu-ke-satu dengan kontrak jelas (tableView.delegate). Delegate lebih cepat dan aman secara tipe. KVO pilih untuk mengamati perubahan properti tertentu model (isLoading, progress). KVO memerlukan pewarisan NSObject dan dapat menyulitkan debugging (string kunci ajaib). Di Swift modern, Combine dan async sequences menggantikan ketiga pendekatan.
Metode addObserver mendukung dua varian langganan: selector-based (tradisional) dan block-based (dengan closure). Selector-based memerlukan kompatibilitas @objc dan penghapusan manual pengamat. Block-based (iOS 9+) memungkinkan penggunaan capture list dan dikelola otomatis oleh OS saat menggunakan blok tanpa referensi kuat. Block-based juga mendukung queue — pengamat menerima pemberitahuan di antrian yang ditentukan.
Cara tradisional berlangganan melalui selector. Metode handler harus ditandai dengan @objc dan menerima Notification opsional. Kelebihan: dapat digunakan oleh kelas apa pun, termasuk legacy Objective-C. Kekurangan: tidak ada keamanan tipe selector, risiko salah ketik nama selector, removeObserver wajib di deinit. Jika pengamat dihapus sebelum objek, handler tidak akan dipanggil.
Block-based API menerima closure yang dijalankan saat menerima pemberitahuan. Parameter queue menentukan di antrian mana blok dijalankan — main queue untuk pembaruan UI atau background queue untuk pemrosesan data. Nilai kembalian NSObjectProtocol digunakan untuk menghapus pengamat: NotificationCenter.default.removeObserver(observer). Block-based lebih disukai di Swift modern.
protocol NotificationToken {
func dispose()
}
extension NotificationCenter {
func observe(
name: NSNotification.Name,
object: Any? = nil,
queue: OperationQueue? = .main,
using block: @escaping (Notification) -> Void
) -> NotificationToken {
let observer = addObserver(forName: name, object: object,
queue: queue, using: block)
return NotificationTokenWrapper(observer: observer, center: self)
}
}
// Penggunaan dengan penghapusan otomatis
class ViewModel {
private var tokens: [NotificationToken] = []
func startObserving() {
let token = NotificationCenter.default.observe(
name: .dataDidUpdate,
queue: .main
) { [weak self] notification in
self?.handleUpdate(notification)
}
tokens.append(token)
}
deinit {
tokens.forEach { $0.dispose() }
}
}
Kebocoran memori — salah satu masalah utama saat bekerja dengan NotificationCenter. Jika pengamat tidak dihapus sebelum dealokasi, saat mengirim pemberitahuan pusat akan mencoba memanggil metode objek yang sudah dibebaskan, menyebabkan EXC_BAD_ACCESS. Mulai iOS 9, block-based addObserver menggunakan referensi lemah, tetapi selector-based masih memerlukan removeObserver manual. Best practice: hapus pengamat di deinit.
Selector-based: wajib memanggil NotificationCenter.default.removeObserver(self) di deinit. Jika pengamat berlangganan beberapa pemberitahuan, dapat dihapus semua sekaligus (tanpa parameter) atau spesifik berdasarkan nama. Block-based: hapus melalui removeObserver dengan token yang diperoleh dari addObserver. Untuk block-based di iOS 9+ tidak terjadi kebocoran, tetapi penghapusan tetap disarankan untuk kinerja: pengamat yang dibebaskan tidak akan dilalui saat post.
class SafeObserver {
private var observers: [NSObjectProtocol] = []
func addSubscriptions() {
let token1 = NotificationCenter.default.addObserver(
forName: .dataDidUpdate, object: nil,
queue: .main) { [weak self] _ in
self?.refreshData()
}
let token2 = NotificationCenter.default.addObserver(
forName: .userLoggedOut, object: nil,
queue: .main) { [weak self] _ in
self?.logout()
}
observers.append(contentsOf: [token1, token2])
}
deinit {
observers.forEach { NotificationCenter.default.removeObserver($0) }
}
private func refreshData() { }
private func logout() { }
}
Pola Token mengotomatiskan manajemen pengamat. Saat berlangganan, objek token (NSObjectProtocol) dikembalikan, yang saat dealokasi secara otomatis menghapus pengamat. NotificationTokenWrapper menyimpan referensi lemah ke NotificationCenter dan token pengamat, memanggil removeObserver di deinit. Ini mendekatkan NotificationCenter ke pendekatan Combine, di mana AnyCancellable mengelola siklus hidup langganan.
Thread safety NotificationCenter menjamin bahwa post dapat dipanggil dari thread mana pun dan semua pengamat akan menerima pemberitahuan di thread yang sama tempat post dipanggil. Ini penting untuk aplikasi multi-thread: jika pemberitahuan dikirim dari thread latar belakang, handler juga akan dijalankan di thread latar belakang. Untuk pembaruan UI, pemrosesan harus didispatch ke main queue melalui DispatchQueue.main.async.
NotificationCenter aman untuk thread untuk panggilan post dan addObserver dari thread berbeda. Sinkronisasi internal menggunakan penguncian, jadi post sering dari beberapa thread dapat membuat contention. Untuk skenario beban tinggi (kemajuan pemuatan 1000 file) gunakan antrian pemberitahuan terpisah atau Combine publisher. NotificationQueue dengan postingStyle .now setara dengan post langsung.
NotificationCenter mendukung Combine publisher melalui NotificationCenter.default.publisher(for:object:). Publisher mengubah setiap pemberitahuan menjadi peristiwa Combine yang dapat ditransformasi melalui map, filter, debounce dan throttle. Ini memecahkan masalah pengiriman sinkron: Combine memproses pemberitahuan secara asinkron pada Scheduler yang ditentukan. NotificationCenter.publisher — jembatan antara mekanisme legacy dan pemrograman reaktif modern.
import Combine
class ReactiveViewModel {
private var cancellables = Set<AnyCancellable>()
func setupCombineSubscription() {
NotificationCenter.default
.publisher(for: .dataDidUpdate)
.receive(on: DispatchQueue.main)
.compactMap { $0.userInfo?["progress"] as? Float }
.debounce(for: .seconds(0.3), scheduler: RunLoop.main)
.sink { [weak self] progress in
self?.progressLabel.text = "\(Int(progress * 100))%"
}
.store(in: &cancellables)
}
}
Pertanyaan yang sering diajukan
Ya, NotificationCenter aman untuk thread untuk panggilan post dan addObserver dari thread mana pun. Namun handler dijalankan di thread yang sama tempat post dipanggil. Untuk pembaruan UI gunakan queue: .main di block-based addObserver atau DispatchQueue.main.async di dalam handler. Combine publisher dengan receive(on:) juga memecahkan masalah thread.
Selector-based: crash EXC_BAD_ACCESS saat mengirim pemberitahuan setelah dealokasi pengamat. Block-based (iOS 9+): tidak ada kebocoran berkat referensi lemah, tetapi pusat pemberitahuan tetap menyimpan blok di memori sampai removeObserver eksplisit. Disarankan selalu menghapus pengamat di deinit atau menggunakan pola Token untuk manajemen otomatis.
NotificationCenter — penyiaran peristiwa arbitrer antar komponen yang tidak terkait. KVO — pengamatan perubahan properti tertentu dari objek tertentu. KVO memerlukan pewarisan NSObject dan memberi tahu secara otomatis saat properti berubah melalui setter. NotificationCenter memberi tahu hanya saat pemanggilan post eksplisit. Untuk pengamatan model, KVO atau Combine lebih disukai.
Satu default center per proses aplikasi. Pusat tambahan dapat dibuat melalui NotificationCenter(), tetapi dalam praktiknya default bersama digunakan. Setiap pusat bekerja independen — post di satu pusat tidak dikirim ke pengamat pusat lain. Untuk isolasi modul, gunakan ruang nama Name terpisah melalui nama pemberitahuan terbalik-domain.
Sebagian. Combine menyediakan NotificationCenter.Publisher yang membungkus NotificationCenter dalam aliran reaktif. Combine memecahkan masalah sinkronitas (melalui receive(on:)), menambahkan operator transformasi dan manajemen langganan otomatis (AnyCancellable). Namun NotificationCenter tetap ada untuk pemberitahuan sistem iOS (UIApplication, UIKeyboard) dan kode legacy. Combine adalah lapisan tambahan, bukan pengganti.
Ringkasan
Kami akan mengembangkan aplikasi seluler turnkey
IT Sectr membuat aplikasi iOS dan Android untuk startup dan bisnis sejak 2017. Kami akan memberi saran dan mengusulkan solusi terbaik.
Baca juga