Skip to content

[#270 푸시 알람 배지가 앱 아이콘에 뜨도록 구현한다#271

Merged
opficdev merged 4 commits into
developfrom
feat/#270-badge
Mar 13, 2026
Merged

[#270 푸시 알람 배지가 앱 아이콘에 뜨도록 구현한다#271
opficdev merged 4 commits into
developfrom
feat/#270-badge

Conversation

@opficdev

Copy link
Copy Markdown
Owner

@opficdev opficdev self-assigned this Mar 13, 2026
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

이 PR은 앱 아이콘에 푸시 알림 배지가 표시되도록 기능을 구현합니다. 앱 내에서 읽지 않은 알림 수를 Firebase Firestore와 동기화하고, 새로운 푸시 알림이 전송될 때마다 클라우드 함수에서 현재 읽지 않은 알림 수를 계산하여 APNs 페이로드에 포함시킴으로써 앱 아이콘 배지가 자동으로 업데이트되도록 합니다.

Highlights

  • 앱 아이콘 배지 카운트 동기화: 읽지 않은 알림 수를 기반으로 앱 아이콘 배지 카운트를 동기화하는 로직이 AppDelegate.swift에 구현되었습니다.
  • Firebase 인증 상태 관찰: Firebase 인증 상태 변경을 관찰하여 사용자 로그인/로그아웃 시 배지 업데이트를 적절히 처리하도록 했습니다.
  • 클라우드 함수 업데이트: Firebase 클라우드 함수를 수정하여 푸시 알림 전송 시 APNs 페이로드에 현재 읽지 않은 알림 수를 포함하도록 했습니다.
Changelog
  • DevLog/App/Delegate/AppDelegate.swift
    • Combine 프레임워크를 임포트했습니다.
    • Firestore, AuthStateListener, Combine cancellable을 위한 프로퍼티를 추가했습니다.
    • application(_:didFinishLaunchingWithOptions:)에서 observeAuthState()를 호출하도록 했습니다.
    • applicationDidBecomeActive(_:) 메서드를 구현하여 syncBadgeCount()를 호출하도록 했습니다.
    • updateUserTimeZone() 메서드를 private extension으로 리팩토링했습니다.
    • 배지 카운트 관리를 위한 observeAuthState(), startObservingBadgeCount(), fetchUnreadNotificationCount(), observeUnreadNotificationCount(), syncBadgeCount(), updateBadgeCount(_:) 메서드를 추가했습니다.
  • Firebase/functions/src/fcm/notification.ts
    • sendPushNotification 함수를 수정하여 FCM 토큰과 읽지 않은 알림 수를 동시에 가져오도록 했습니다.
    • APNs 페이로드에 unreadNotificationCount를 badge 값으로 포함시켰습니다.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

이번 PR은 푸시 알림 수신 시 앱 아이콘에 배지를 표시하는 기능을 구현합니다. AppDelegate.swift에서는 사용자 인증 상태를 감지하고, 로그인된 사용자에 대해 Firestore의 읽지 않은 알림 수를 관찰하여 앱 아이콘 배지를 동기화하는 로직이 추가되었습니다. Firebase 함수(notification.ts)에서는 푸시 알림을 보낼 때 읽지 않은 알림 수를 함께 조회하여 APNs 페이로드에 포함하도록 수정되었습니다. 전반적으로 클라이언트 측 동기화와 서버 측 푸시 페이로드 수정이 잘 이루어졌습니다. 코드의 안정성과 가독성을 높이기 위해 AppDelegate.swift 파일에 몇 가지 개선 사항을 제안합니다.

class AppDelegate: UIResponder, UIApplicationDelegate, MessagingDelegate {
private let logger = Logger(category: "AppDelegate")
private var store: Firestore { Firestore.firestore() }
private var authStateListenerHandle: AuthStateDidChangeListenerHandle?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

addStateDidChangeListener로 등록한 리스너는 앱이 종료될 때 해제하는 것이 좋습니다. AppDelegatedeinit을 추가하여 authStateListenerHandle을 제거하는 것을 고려해보세요. 이렇게 하면 잠재적인 리소스 누수를 방지할 수 있습니다.

deinit {
    if let handle = authStateListenerHandle {
        Auth.auth().removeStateDidChangeListener(handle)
    }
}

Comment thread DevLog/App/Delegate/AppDelegate.swift Outdated
Comment on lines +128 to +143
func startObservingBadgeCount() {
cancellable = try? observeUnreadNotificationCount()
.receive(on: DispatchQueue.main)
.sink(
receiveCompletion: { [weak self] completion in
guard let self else { return }

if case .failure(let error) = completion {
self.logger.error("Failed to observe unread notification count", error: error)
}
},
receiveValue: { [weak self] count in
self?.updateBadgeCount(count)
}
)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

startObservingBadgeCount() 함수에서 try?를 사용하여 observeUnreadNotificationCount()에서 발생하는 오류를 암묵적으로 무시하고 있습니다. observeUnreadNotificationCount()는 사용자가 인증되지 않은 경우 오류를 발생시킬 수 있으며, 이 경우 cancellablenil이 되고 아무런 동작도 하지 않게 됩니다. 오류가 발생할 가능성이 있는 경우, do-catch 구문을 사용하여 명시적으로 오류를 처리하고 로그를 남기는 것이 더 안전한 방법입니다.

    func startObservingBadgeCount() {
        do {
            cancellable = try observeUnreadNotificationCount()
                .receive(on: DispatchQueue.main)
                .sink(
                    receiveCompletion: { [weak self] completion in
                        guard let self else { return }

                        if case .failure(let error) = completion {
                            self.logger.error("Failed to observe unread notification count", error: error)
                        }
                    },
                    receiveValue: { [weak self] count in
                        self?.updateBadgeCount(count)
                    }
                )
        } catch {
            logger.error("Failed to start observing badge count", error: error)
        }
    }

Comment on lines +178 to +190
.addSnapshotListener { [weak self] snapshot, error in
if let error {
self?.logger.error("Failed to observe unread notification count", error: error)
subject.send(completion: .failure(error))
return
}

guard let snapshot else { return }

let unreadNotificationCount = snapshot.documents.count
self?.logger.info("Observed unread notification count: \(unreadNotificationCount)")
subject.send(unreadNotificationCount)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

addSnapshotListener의 클로저 내에서 self?를 여러 번 사용하고 있습니다. 클로저 시작 부분에서 guard let self = self else { return }을 사용하여 self를 안전하게 언래핑하면 코드가 더 간결해지고, 클로저 실행 동안 self가 nil이 되지 않음을 보장할 수 있습니다.

            .addSnapshotListener { [weak self] snapshot, error in
                guard let self else { return }
                if let error {
                    self.logger.error("Failed to observe unread notification count", error: error)
                    subject.send(completion: .failure(error))
                    return
                }

                guard let snapshot else { return }

                let unreadNotificationCount = snapshot.documents.count
                self.logger.info("Observed unread notification count: \(unreadNotificationCount)")
                subject.send(unreadNotificationCount)
            }

@opficdev opficdev marked this pull request as ready for review March 13, 2026 01:38
@opficdev opficdev merged commit a85996c into develop Mar 13, 2026
2 checks passed
@opficdev opficdev deleted the feat/#270-badge branch March 13, 2026 02:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

푸시 알람 배지가 앱 아이콘에 뜨도록 구현한다

1 participant