인프런 커뮤니티 질문&답변

랑프_Langpeu님의 프로필 이미지
랑프_Langpeu

작성한 질문수

[Lv.2] 레벨업 - SwiftUI intermediate with Core Data, SwiftData

Local Notification

Local Notification 예제 문의

작성

·

194

0

예제를 전부 구현한 상태에서

아래 코드를 통해서 뱃지 숫자를 1씩 올려서 보내게

되어 있습니다.

 

func scheduleNotification() {
        //notification 내용 설정
        let content = UNMutableNotificationContent()
        content.title = "Local Notification 테스트 1"
        content.subtitle = "앱 알람 테스트 중입니다."
        content.sound = .default
        content.badge = NSNumber(value: UIApplication.shared.applicationIconBadgeNumber + 1)

하지만 아래 코드에 의해 뱃지가 0 으로 리셋되기에
항상 1만 발송하게 됩니다.

.onChange(of: scenePhase) { newValue in
            if newValue == .active {
                UIApplication.shared.applicationIconBadgeNumber = 0
            }
        }

추가적으로 5초 딜레이로 설정후 로컬노티 여러번 누른후에
앱을 종료하고 기다리면 노티가 여러개 오지만
모두 뱃지 1로 오기 때문에 앱아이콘에 뱃지는 1로 계속

옵니다.

 

실무에서 로컬노티에 카운터를 현재 뱃지 카운터에 + 1 해서
보내는건 실효성이 없어보이는데요.

 

따로 해결할 방법이 있을까요 ? 조언 부탁드립니다.

 

강의 관련된 질문은 언제나 환영입니다 😄

  • 코드 관련 상세하게 작성해주시면 더 좋아요! (상세코드를 붙여넣기 해주세요)

  • 마크다운을 사용해서 편리하게 글을 작성하세요

  • 먼저 유사한 질문이 있었는지 먼저 검색 해보는 습관~

  • 인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요

답변 2

1

Jacob(제이콥)님의 프로필 이미지
Jacob(제이콥)
지식공유자

안녕하세요 랑프님.

앱이 활성화될 때마다 UIApplication.shared.applicationIconBadgeNumber = 0으로 뱃지가 초기화되므로, 로컬 알림을 여러 번 발송해도 모든 알림이 뱃지 값이 1인 상태로 도착합니다. 이는 알림의 뱃지 값이 항상 초기화되기 때문에 발생합니다.

이를 해결하기 위해서 UserDefaults 를 사용해서 뱃지 값을 저장하고 불러오는 방법을 사용하면 됩니다.

수정된 코드는 다음과 같습니다. *수정한 부분만 사진으로 올려드리고 전체 코드는 맨아래 있습니다.

  1. 앱이 종료 되어도 뱃지값 저장하기

image

  1. CancelNotifiactaion 에서 값 초기화

image

  1. View에서 화면이 나타날때 reset 하는거 삭제

image

  • 수정된 코드

class NotificationManager {
    
    static let instance = NotificationManager() // 인스턴스 생성
    
    func requestAuthorization() {
        
        let option: UNAuthorizationOptions = [.alert, .sound, .badge]
        
        // UserNotification 접근
        UNUserNotificationCenter.current().requestAuthorization(options: option) { (success, error) in
            if let error = error {
                print("에러: \(error)")
            } else {
                print("성공")
            }
        }
    }
    
    func scheduleNotification() {
        
        // UserDefaults 에다가 자장할 currentBadgeCount
        let currentBadgeCount = UserDefaults.standard.integer(forKey: "badgeCount")
        
        // 값을 1씩 증가 시킴
        let newBadgeCount = currentBadgeCount + 1
        // 증가한다음에서는 값을 저장
        UserDefaults.standard.set(newBadgeCount, forKey: "badgeCount")
        
        // notification 내용 설정
        let content = UNMutableNotificationContent()
        content.title = "Local Notification 테스트 1"
        content.subtitle = "앱 알람 테스트 중입니다"
        content.sound = .default
        content.badge = NSNumber(value: newBadgeCount) // 1씩 증가 시킴
        
        
        // Trigger 2가지 종류
        
        
        // 1.시간 기준 : Interval - 몇 초 뒤에 울릴것인지 딜레이 설정 repeats 반복 여부 설정 (최소 1분이여지 반복이 돔)
        let timeTrigger = UNTimeIntervalNotificationTrigger(timeInterval: 5.0, repeats: false)
        
        
        // 2.날짜 기준 : DateMating 은 DateComponent 기준맞는 알림
        var dateComponents = DateComponents()
        dateComponents.hour = 8 // hour 를 24시간 기준
        dateComponents.minute = 30
        dateComponents.weekday = 1 // 1은 일요일이 되고, 6은 금요일이 됨
        
        //		let calendarTigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: true)
        
        
        // 설정한 값을 NotificationCenter 에 요청하기
        let request = UNNotificationRequest(
            identifier: UUID().uuidString, // 각각의 request ID 값 String 을 uuid 값으로 설정
            content: content,
            trigger: timeTrigger)
        UNUserNotificationCenter.current().add(request)
    }
    
    // 생성된 Notification Cancel 하기
    func cancelNotification() {
        
        // peding notification 은 tigger 상에서 만족된 조건이 되어도 더이상 notification 되지 않게 하기
        UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
        
        // 아이폰 상태바를 내렸을때 남아있는 notification 없애기
        UNUserNotificationCenter.current().removeAllDeliveredNotifications()
        
        
        // Cancel 하면 UserDefault 값 초기화 하기
        UserDefaults.standard.set(0, forKey: "badgeCount")
        UIApplication.shared.applicationIconBadgeNumber = 0
    }
    
    
}


struct LocalNotificationInter: View {
    
    @Environment(\.scenePhase) var scenePhase
    
    var body: some View {
        VStack (spacing: 40) {
            Button {
                NotificationManager.instance.requestAuthorization()
            } label: {
                Text("권한 요청하기")
                    .font(.title2)
                    .fontWeight(.semibold)
                    .foregroundColor(.black)
                    .padding()
                    .background(Color.green)
                    .cornerRadius(10)
            }
            
            Button {
                NotificationManager.instance.scheduleNotification()
            } label: {
                Text("Time Notification")
                    .font(.title2)
                    .fontWeight(.semibold)
                    .foregroundColor(.black)
                    .padding()
                    .background(Color.green)
                    .cornerRadius(10)
            }
            
            Button {
                NotificationManager.instance.scheduleNotification()
            } label: {
                Text("Calendar Notification")
                    .font(.title2)
                    .fontWeight(.semibold)
                    .foregroundColor(.black)
                    .padding()
                    .background(Color.green)
                    .cornerRadius(10)
            }
            
            
            Button {
                NotificationManager.instance.cancelNotification()
            } label: {
                Text("Notification 취소하기")
                    .font(.title2)
                    .fontWeight(.semibold)
                    .foregroundColor(.black)
                    .padding()
                    .background(Color.green)
                    .cornerRadius(10)
            }
            
        } //: VSTACK
        // schne 이 나타 날때 Badge 0 으로 초기화 하기
        .onChange(of: scenePhase) { newValue in
            if newValue == .active {
                // 0으로 초기화 되지 않게 reset 하지 않기
//                UIApplication.shared.applicationIconBadgeNumber = 0
            }
        }
    }
}

답변 감사합니다.

0

안녕하세요, 인프런 AI 인턴입니다.

랑프_Langpeu님의 질문에 대해 답변 드리겠습니다.

문제의 핵심은 앱이 활성 상태로 전환될 때 뱃지 숫자가 0으로 초기화되어, 로컬 알림으로 설정한 뱃지 숫자가 기대와 다르게 작동한다는 점입니다.

이문제를 해결하기 위해서는 애플리케이션이 활성 상태가 될 때마다 뱃지 숫자를 초기화하지 않고, 알림을 확인하거나 필요한 조치를 취했을 때 명시적으로 뱃지 숫자를 조정하는 로직을 구현해야 합니다.

전송되는 알림의 뱃지 숫자를 올바르게 관리하기 위해서는 애플리케이션 내부적으로 알림이 몇 번 전송되었는지를 추적할 필요가 있습니다. 이를 위해, 앱의 로컬 저장소나 데이터 베이스를 사용하여 알림이 발송될 때마다 카운터를 증가시키고, 앱이 활성화될 때 해당 카운터를 참조하여 UIApplication.shared.applicationIconBadgeNumber을 올바르게 설정하시는 것이 좋습니다.

예를 들어, 알림 발송 함수 내에서 뱃지 숫자를 증가시키는 로직을 추가하고:

func increaseBadgeNumberByOne() {
    let currentBadgeNumber = UserDefaults.standard.integer(forKey: "badgeNumber")
    UserDefaults.standard.set(currentBadgeNumber + 1, forKey: "badgeNumber")
    content.badge = NSNumber(value: currentBadgeNumber + 1)
}

앱이 활성 상태가 될 때 뱃지 숫자를 리셋하는 대신 사용자가 알림을 확인하였다는 가정하에 뱃지 숫자를 관리할 수 있는 로직을 추가합니다:

.onChange(of: scenePhase) { newValue in
    if newValue == .active {
        // 사용자가 알림을 확인했다고 가정하고 뱃지 숫자를 초기화
        UserDefaults.standard.set(0, forKey: "badgeNumber")
        UIApplication.shared.applicationIconBadgeNumber = 0
    }
}

이렇게 변경하면 앱이 활성화될 때마다 뱃지 숫자가 초기화되는 문제를 보완할 수 있습니다. 단, 이러한 방법은 알림이 발송될 때마다 앱 내부에서 뱃지 숫자를 관리해야 한다는 추가적인 구현이 필요합니다.

실제로 이 외에도 다양한 방법과 조건에 따라 뱃지 숫자를 관리할 수 있으므로, 애플리케이션의 특성과 필요에 맞게 적절한 로직을 설계하는 것이 중요할 것입니다.

랑프_Langpeu님의 프로필 이미지
랑프_Langpeu

작성한 질문수

질문하기