Skip to content

[#284] 탭뷰에 있는 알림 이미지에 읽지 않은 푸시알림 데이터가 있을 경우 해당 데이터의 갯수를 배지로 보이도록 구현한다#290

Merged
opficdev merged 11 commits intodevelopfrom
feat/#284-Tab-Badge
Mar 17, 2026
Merged

[#284] 탭뷰에 있는 알림 이미지에 읽지 않은 푸시알림 데이터가 있을 경우 해당 데이터의 갯수를 배지로 보이도록 구현한다#290
opficdev merged 11 commits intodevelopfrom
feat/#284-Tab-Badge

Conversation

@opficdev
Copy link
Owner

@opficdev opficdev self-assigned this Mar 17, 2026
@gemini-code-assist
Copy link
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은 탭 뷰의 알림 아이콘에 읽지 않은 푸시 알림의 개수를 배지로 표시하는 기능을 구현합니다. 이를 위해 읽지 않은 알림 수를 실시간으로 관찰하는 새로운 유스케이스와 뷰모델을 도입하고, 기존 AppDelegate에 분산되어 있던 관련 로직을 해당 컴포넌트로 이동하여 코드의 응집도를 높였습니다. 사용자 경험을 개선하고 알림 관리의 효율성을 증대하는 것이 주요 목표입니다.

Highlights

  • 읽지 않은 푸시 알림 배지 기능 추가: 탭 뷰의 알림 아이콘에 읽지 않은 푸시 알림 수를 표시하는 배지 기능이 추가되었습니다. 이는 사용자가 새로운 알림이 있음을 즉시 인지할 수 있도록 돕습니다.
  • 새로운 UseCase 및 ViewModel 도입: 읽지 않은 푸시 알림 수를 관찰하고 관리하기 위해 ObserveUnreadPushCountUseCaseMainViewModel이 새로 도입되었습니다. 이를 통해 알림 배지 로직이 모듈화되고 관심사가 분리되었습니다.
  • AppDelegate 로직 분리 및 간소화: 기존 AppDelegate에 있던 알림 배지 관련 로직(인증 상태 관찰, 배지 카운트 동기화 및 업데이트)이 새로운 UseCase와 ViewModel로 이동되어 AppDelegate의 역할이 간소화되었습니다.
  • DI 컨테이너 업데이트: 새로 추가된 ObserveUnreadPushCountUseCaseDomainAssembler에 등록되어 의존성 주입을 통해 사용될 수 있도록 설정되었습니다.
Changelog
  • DevLog/App/Assembler/DomainAssembler.swift
    • ObserveUnreadPushCountUseCase를 등록했습니다.
  • DevLog/App/Delegate/AppDelegate.swift
    • FirebaseFirestore, FirebaseMessaging, Combine, UserNotifications 관련 불필요한 import를 제거했습니다.
    • Firestore 인스턴스, 인증 상태 리스너 핸들, Cancellable 변수 등 사용되지 않는 프로퍼티를 제거했습니다.
    • applicationDidBecomeActive(:) 메서드와 observeAuthState(), syncBadgeCount(), startObservingBadgeCount(), fetchUnreadNotificationCount(), observeUnreadNotificationCount(), updateBadgeCount(:) 등 알림 배지 관련 로직을 제거했습니다.
    • FCM 토큰 알림을 위한 Notification.Name 확장자를 제거했습니다.
  • DevLog/App/RootView.swift
    • MainView 초기화 시 ObserveUnreadPushCountUseCase를 주입하도록 변경했습니다.
  • DevLog/Data/Repository/PushNotificationRepositoryImpl.swift
    • observeUnreadPushCount() 메서드를 구현하여 서비스 계층에 위임했습니다.
  • DevLog/Domain/Protocol/PushNotificationRepository.swift
    • observeUnreadPushCount() 메서드를 프로토콜에 추가했습니다.
  • DevLog/Domain/UseCase/PushNotification/Fetch/ObserveUnreadPushCountUseCase.swift
    • 읽지 않은 푸시 알림 수를 관찰하는 새로운 UseCase 프로토콜을 추가했습니다.
  • DevLog/Domain/UseCase/PushNotification/Fetch/ObserveUnreadPushCountUseCaseImpl.swift
    • ObserveUnreadPushCountUseCase 프로토콜의 구현체를 추가했습니다.
  • DevLog/Infra/Service/PushNotificationService.swift
    • Firestore에서 읽지 않은 푸시 알림 수를 실시간으로 관찰하는 observeUnreadPushCount() 메서드를 추가했습니다.
  • DevLog/Presentation/ViewModel/MainViewModel.swift
    • 읽지 않은 푸시 알림 수와 알림 상태를 관리하는 MainViewModel을 새로 추가했습니다.
  • DevLog/UI/Common/MainView.swift
    • MainViewModel을 @State 프로퍼티로 받도록 변경했습니다.
    • 알림 탭에 읽지 않은 푸시 알림 수를 배지로 표시하도록 적용했습니다.
    • 알림 배지 로딩 중 발생할 수 있는 오류를 처리하기 위한 alert를 추가했습니다.
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.

Copy link
Contributor

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

Choose a reason for hiding this comment

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

Code Review

이번 PR은 읽지 않은 푸시 알림 갯수를 탭 바의 알림 아이콘에 배지로 표시하는 기능을 구현하고, 관련 로직을 AppDelegate에서 MainViewModel로 리팩토링하는 작업을 포함하고 있습니다. 책임 분리가 명확해지고 코드가 깔끔해진 점이 좋습니다. 몇 가지 개선점을 제안드립니다.

Comment on lines +34 to +39
init(
observeUnreadPushCountUseCase: ObserveUnreadPushCountUseCase
) {
self.observeUnreadPushCountUseCase = observeUnreadPushCountUseCase
observeUnreadPushCount()
}
Copy link
Contributor

Choose a reason for hiding this comment

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

high

MainViewModel이 해제될 때(예: 사용자가 로그아웃할 때) 앱 아이콘의 배지 카운트를 0으로 설정하는 로직이 누락되었습니다. 이로 인해 로그아웃 후에도 배지 카운트가 그대로 남아있게 됩니다. deinit 블록을 추가하여 배지를 초기화하는 것을 제안합니다.

deinit {
    updateBadgeCount(0)
}

Comment on lines +96 to +98
func updateBadgeCount(_ count: Int) {
UNUserNotificationCenter.current().setBadgeCount(count) { _ in }
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

setBadgeCount의 completion handler에서 오류가 발생할 경우를 대비하여 로깅을 추가하는 것이 좋습니다. 이전 AppDelegate 구현에서는 오류를 로깅했지만, 현재는 무시되고 있습니다. 디버깅을 위해 오류를 기록하는 것을 고려해보세요. MainViewModelLogger를 추가하고 다음과 같이 수정할 수 있습니다.

// In MainViewModel
private let logger = Logger(category: "MainViewModel")

// ...

func updateBadgeCount(_ count: Int) {
    UNUserNotificationCenter.current().setBadgeCount(count) { [weak self] error in
        if let error {
            self?.logger.error("Failed to update badge count", error: error)
        }
    }
}

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7e472ed6db

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines 19 to 23
if signIn && !viewModel.state.isFirstLaunch {
MainView()
MainView(viewModel: MainViewModel(
observeUnreadPushCountUseCase: container.resolve(ObserveUnreadPushCountUseCase.self)
))
} else {

Choose a reason for hiding this comment

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

P2 Badge Clear app badge when signed-out flow is shown

Badge updates now only happen through MainViewModel, which is instantiated only in the signed-in branch here; when a user signs out after having unread notifications, MainView is removed and no code path resets the app icon badge to 0. The previous AppDelegate auth-state handler used to clear badges on unauthenticated state, so this change can leave stale badge counts visible on the login screen (and to the next user on shared devices).

Useful? React with 👍 / 👎.

@opficdev
Copy link
Owner Author

@codex review

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 38cd53fb2a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +20 to +22
MainView(viewModel: MainViewModel(
observeUnreadPushCountUseCase: container.resolve(ObserveUnreadPushCountUseCase.self)
))

Choose a reason for hiding this comment

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

P2 Badge MainViewModel 생성을 body 재평가 경로에서 분리하세요

RootViewbody에서 MainViewModel(...)를 직접 만들면 RootViewModel 상태 변경(세션/테마/네트워크 등)마다 새 인스턴스가 계속 생성됩니다. MainViewModel.init이 즉시 observeUnreadPushCount()를 실행해 Firestore 구독을 시작하므로, 실제로는 MainView@State에 채택되지 않고 버려지는 인스턴스에서도 불필요한 리스너 등록/해제가 반복되어 성능과 네트워크 비용이 증가합니다.

Useful? React with 👍 / 👎.

@github-actions
Copy link

❌ iOS CI build failed.

Lines containing 'error:':

                self?.logger.error("Failed to update application badge count", error: error)

@opficdev opficdev merged commit f20d1a6 into develop Mar 17, 2026
2 checks passed
@opficdev opficdev deleted the feat/#284-Tab-Badge branch March 17, 2026 03:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant