Swift NotificationCenter(+SwiftUI 사용법)

마이노·2024년 3월 28일
2

15주 글쓰기 🐣

목록 보기
5/10
post-thumbnail

NotificationCenter

NotificationCenter란 무엇일까요?

제가 처음 알게 되었던 때는 keyboard가 나타나고 사라질 때의 시점을 어떻게 알 수 있을까? 하다가 처음 사용해봤던 기억이 납니다.

NotificationCenter는 이름 그대로 알림센터에요. 관찰자(Observer)로 등록을 하면 필요한 정보를 수신할 수 있는 알림 발송 형식의 메커니즘입니다.


먼저 간단하게 어떻게 사용할까요? 바로 알아볼께요.

간단한 사용법

NotificationCenter.default.addObserver(self, 
									   selector:#selector(keyboardWillHide), 
									   name: UIResponder.keyboardWillHidNotification, 
									   object: nil)

우선 관찰자로 등록을 해줘야 합니다. 키보드가 사라질 예정임을 수신하고자 하는 뷰에서 관찰자로 등록을 해줍니다.

@objc private func keyboardWillHide(_ notification: Notification) {
	...
}

그리고 수신해주면 끝입니다. 간단하죠?

전역적으로 사용할 수 있기 때문에 필요한 뷰에 그때그때 추가해주면 됩니다.
너무너무 편한데요, 편한만큼 관리도 신경써서 잘 해줘야 합니다.

deinit {
    NotificationCenter.default.removeObserver(self)
}

메모리에 항상 올라가 있기 때문에 관찰을 하고자 하는 뷰에서 벗어난다? 그렇다면 바로 removeObserver를 통해 제거해주는 것이 좋습니다.

지금까지 NotificationCenter의 관찰을 추가하는 방법, 삭제하는 방법을 알아보았습니다. 이제 좀 더 파헤쳐봅시다!

addObserver와 removeObserver

open func addObserver(_ observer: Any, 
					  selector aSelector: Selector, 
					  name aName: NSNotification.Name?, 
					  object anObject: Any?)

addObserver

우선 addObserver의 매개변수부터 자세히 살펴볼께요.
observer, aSelector, aName, anObject 이렇게 4개가 보입니다.

  1. observer
    관찰자로 등록할 객체를 설정하는 매개변수입니다. 키보드를 노티를 설정할 때 self로 설정했던 것을 기억하시나요? 해당 뷰에서 관찰을 하겠다는 뜻이였습니다!

  2. aSelector
    수신하는 쪽에서 어떠한 메세지를 날려야 하는가에 대한 정의입니다.
    @objc func를 만들어 수신을 했을 때 키보드가 사라졌다는 메세지를 print로 찍어볼 수 있었어요. 이렇게 만들어진 Selector용 method는 하나의 argument만 있어야 합니다!

  3. aName
    관찰자에게 전달하기 위해 등록할 알림의 이름입니다. 다시말해 어떠한 동작을 알림으로 설정해 관찰할 것이냐 하는 어찌보면 가장 중요한 매개변수라고 볼 수 있어요. 저희는 알림의 이름을 UIResponder.keyboardWillHideNotification로 설정했었죠? 키보드가 사라지는 알림을 설정하고 있었던 것이에요!

4.anObject
관찰자에게 알림을 보낼 때 보낸사람을 지정할 수가 있어요! 보낸사람을 지정하지 않아도 된다면 nil로 설정할 수 있습니다. 저희는 키보드 코드에서 nil로 설정했었습니다.

removeObserver

func removeObserver(Any, name: NSNotification.Name?, object: Any?)
func removeObserver(Any)

우선 두가지가 겹치는 부분이 있어 먼저 설명을 드릴께요.

Any에 해당하는 부분을 removeObserver하고 있어요. 이는 관찰하고 있는 모든 항목을 제거할 수 있습니다. 보통의 케이스에서 self를 작성해 뷰가 사라질 때 모든 관찰하고 있는 NotificationCenter를 deinit에서 removeObserver해서 한꺼번에 날려버리는 방식이 자주 사용됩니다.

이제 첫번째 함수를 살펴볼께요.

Any는 위 설명과 동일합니다. name에는 NSNotification.Name이라는 아주 생소한 코드가 있습니다.


우선 설명드릴 것은 Notification.Name은 typealias입니다.

typealias Notification.Name = NSNotification.Name

이렇게 두개가 똑같습니다. 그렇다면 NSNotification.Name에는 무엇이 있느냐!!

https://developer.apple.com/documentation/foundation/nsnotification/name

어..우선 매우 많습니다. 알림의 이름들을 정의하는 구조체인 Name에는 매우 많은 알림들이 있으니 그때그때 찾아서 사용하시는 것을 추천드립니다.

찾은 케이스를 입력을 해주시면 되구요! 앞쪽에 UIResponder에서 정의되어 있는 static property라고 적혀있으니 저희는

Notification.Name(UIResponder.keyboardWillHideNotification.rawValue)

다음과 같이 작성해주시고 rawValue로 접근해버리면 되겠습니다.

이렇게 검색을 해서 키보드가 사라질 때의 알림 이름을 찾을 수 있어요.
object는 addObserver에서 설명한 object와 동일합니다. Name을 보시면 무언가 느껴지지 않으신가요?

'어 이거 커스텀되나?' 넵! 가능합니다.
addObserver의 name과 보낼 name이 동일하다면 똑같이 작동합니다.

NotificationCenter.default.addObserver(self,
									   selector:#selector(doSomething), 
									   name: Notification.Name("hello"), 
									   object: nil)

NotificationCenter.default.post(name: Notification.Name("hello"), 
								object: nil)

post는 직접 관찰자로 데이터를 슝~ 날려버릴 수 있어요. 중요한건 이름만 동일하면 됩니다. 제거할때도 해당 이름으로 제거할 수 있겠죠?

SwiftUI에서는 어떻게 써야 할까?

NotificationCenter는 publisher로 만들 수도 있습니다. 저희는 이것을 활용해서 스유에서도 잘 작동하는 노티를 만들 수 있어요!

let helloPublisher: AnyPublisher<Notification, Never> = NotificationCenter
																		.default
																		.publisher(for: Notification.Name("hello"))
																		.eraseToAnyPublisher()

AnyPublisher를 통해 초기값을 방출할 수 없는 형태로 래핑해줍니다. 어떤 노티 알림을 관찰할 것인지 이름을 받고있네요! 커스텀도, 지정된 그 모든 알림을 받을 수 있습니다.


두가지의 뷰에서 테스트를 진행해볼께요.
홈 뷰에서 디테일 뷰로 들어가서 이 디테일뷰에서 노티를 한번 날려보겠습니다.

// Home
var body: some View {
	NavigationStack {
		VStack { NavigationLink("이동", destination: DetailView()) }
		.onReceive(helloPublisher) { _ in
			print("Notification 수신")
		}
	}
}

자 다음과 같은 코드가 있습니다!

NavigationLink를 통해 디테일로 갈 수가 있어요. 수신해야 하는 뷰는 홈 뷰이기 때문에 onRecive를 통해 Publisher를 argument로 넣을 수 있습니다. 저희는 publisher만들었으니 넣어줄 수 있었습니다.
해당 홈뷰에서는 수신의 의미로 print를 출력해줄게요.

// Detail
var body: some View {
	Button("노티 보내기") {
		NotificationCenter.default.post(name: Notification.Name("hello"), 
        								object: nil)
	}
}

간단하게 Detail에서는 post를 통해 hello라는 이름으로 노티를 날려주고 있어요. 홈의 노티로 보내기 위해선 이름을 똑같이 작성해주면 되겠습니다.

예상한 결과처럼 노티를 수신할 수 있어요! 스유에도 어울리게 애플에서 깔끔하게 잘 만들어준 것 같죠?


지금까지 NotificationCenter를 살펴보았습니다. 어떠셨나요? 사실은 회사에서 노티를 쓸 일이 생겼었어요. 날짜가 변경될 때 앱을 실행하지 않더라도 이 알림을 수신해 데이터를 처리해야 하는 일이였는데요, 생각보다 간단하게 해결이 되었습니다.

Notification.Name(UIApplication.significantTimeChangeNotification.rawValue)

고작 이 코드 한줄로 날짜변경 시점을 잡을 수 있었다는것...🥲

읽어주셔서 감사합니다 :)

profile
아요쓰 정벅하기🐥

6개의 댓글

comment-user-thumbnail
2024년 3월 30일

iOS라는 플랫폼에서 제공해주는 시스템레벨에서의 binding을 이야기할때 나오는 기능이 notification인데
저는 생각보다 편하다!라고 느껴본적이없어서(수동으로 끊어줘야하는 문제도 있지만요 ㅠㅠ) 그래서 비동기처리를 위한 async await이 나온순간부터 binding을 위해 rx와 combine의 선호도가 늘지않았을까라는 생각이드네요 ㅎㅎ

만약에 notification center가 정말 편하게 쓸수있었다면 rx나 combine없이도 binding을 할수있었을테니까요 ㅎㅎ 좋은글 잘 읽었습니다!

1개의 답글
comment-user-thumbnail
2024년 3월 30일

NotificationCenter는 전달하는
오브젝트의 타입이 Any라서 아쉽더라고요
물론 어쩔 수 없는 선택이라는 걸 알지만서도요

1개의 답글
comment-user-thumbnail
2024년 3월 30일

처음 사용해 볼 때 등록만 하고 가끔씩 해제하는 걸 잊었던 기억이 나네요 ㅠㅠ
단계별로 파고들어가며 설명해주시니 더 이해하기 쉬워지는 느낌이라 더 좋았습니다!
좋은 글 감사 드립니다!!

1개의 답글