[iOS] πŸ“¬ Local Notification

Sangwon ShinΒ·2021λ…„ 12μ›” 5일
0

iOS

λͺ©λ‘ 보기
3/9

πŸ€·β€β™‚οΈ μ΄λ²ˆμ—” μ™œ Notification μΈκ°€μš”?

μ΄λ²ˆμ— μΆœμ‹œν•œ μ•±μ˜ 컨셉이 μŒμ‹μΌκΈ° 인데, μ‚¬μš©μžκ°€ μ„€μ •ν•œ μ•„μΉ¨, 점심, 저녁 μ‹œκ°„μ— λ‘œμ»¬μ•Œλ¦Όμ„ μ€˜μ„œ 일기λ₯Ό κΉŒλ¨Ήμ§€ μ•Šκ³  μž‘μ„±ν•  수 μžˆλ„λ‘ ν•˜λ©΄ μ–΄λ–¨κΉŒ? ν•˜λŠ” 생각에 미루고 λ―Έλ€„μ™”λ˜ Local Notification 을 곡뢀해보렀고 ν•©λ‹ˆλ‹€


πŸ“¦ λ‘œμ»¬μ•Œλ¦Όκ³Ό ν‘Έμ‹œμ•Œλ¦Ό

μ•Œλ¦Όμ—λŠ” μ•± λ‚΄λΆ€μ—μ„œ κ°œλ°œμžκ°€ μ„€μ •ν•œ 컨텐츠λ₯Ό 기기에 μ „λ‹¬ν•˜λŠ” λ‘œμ»¬μ•Œλ¦Όκ³Ό μ„œλ²„μ—μ„œ μ‚¬μš©μž 기기에 μ•Œλ¦Όμ„ μ „λ‹¬ν•˜λŠ” μ›κ²©μ•Œλ¦Ό(ν‘Έμ‹œ μ•Œλ¦Ό) 2가지가 μžˆμŠ΅λ‹ˆλ‹€.

μ΄λ²ˆμ—λŠ” λ‘œμ»¬μ•Œλ¦Όμ„ μ€‘μ μ μœΌλ‘œ λ‹€λ€„λ³΄κ² μŠ΅λ‹ˆλ‹€.

λ‘œμ»¬μ•Œλ¦Όμ˜ 경우 μœ„μ˜ κ·Έλ¦Όκ³Ό 같이, μ‚¬μš©μžκ°€ μ•Œλ¦Όμ„ μ˜ˆμ•½ν•˜λ©΄ Notification Center 에 μŒ“μ΄κ²Œ 되고 μ„€μ •ν•œ trigger 쑰건에 맞게 μ‚¬μš©μžμ˜ νœ΄λŒ€ν°μ— μ•ŒλžŒμ„ λ„μ›Œμ€λ‹ˆλ‹€.

Notification을 μš”μ²­ν•˜κΈ° μœ„ν•΄μ„œλŠ”

  • identifier
  • content
  • trigger

각 μ•Œλ¦Όμ˜ κ³ μœ ν•œ μ‹λ³„μž, μ–΄λ–€ λ‚΄μš©μ„ μ‚¬μš©μžκ²Œμ— 보여쀄지, 그리고 μ–΄λ–€ 타이밍에 μ•Œλ¦Όμ„ 쀄지λ₯Ό μ„€μ •ν•΄μ€˜μ•Ό ν•©λ‹ˆλ‹€.

μ‚¬μš©μžκ°€ μ„€μ •ν•œ μ‹œκ°„μ— λ‘œμ»¬μ•Œλ¦Όμ΄ μ˜€λ„λ‘ κ΅¬ν˜„ν•΄λ³΄κ² μŠ΅λ‹ˆλ‹€.


1 ) identifier

μš°μ„ , λ‘œμ»¬μ•Œλ¦Ό μ˜ˆμ•½μ„ ν•˜κΈ° μœ„ν•΄μ„œλŠ” μ•Œλ¦Όμ— λŒ€ν•œ identifier λ₯Ό μ„€μ •ν•˜λŠ” 과정이 ν•„μš”ν•©λ‹ˆλ‹€.
identifier κ°€ λ™μΌν•˜λ‹€λ©΄ μ—¬λŸ¬ μ•Œλ¦Όμ„ 보내도 μ•Œλ¦Όμ΄ μˆ˜μ •λ˜λŠ” ν˜•νƒœλ‘œ λ³€κ²½λ©λ‹ˆλ‹€.

struct Alert: Codable {
    var id: String = UUID().uuidString
    var date: Date
    var isOn: Bool
    
    //μ‹œκ°„μ„ ν‘œν˜„ν•˜κΈ° μœ„ν•΄μ„œ
    var time: String {
        let timeFormatter = DateFormatter()
        timeFormatter.dateFormat = "hh:mm"
        return timeFormatter.string(from: date)
    }
    
    //μ˜€μ „, μ˜€ν›„λ₯Ό ν‘œν˜„ν•˜κΈ° μœ„ν•΄μ„œ
    var day: String {
        let timeFormatter = DateFormatter()
        timeFormatter.dateFormat = "a"
        timeFormatter.locale = Locale(identifier: "ko")
        return timeFormatter.string(from: date)
    }
}

μš°λ¦¬λŠ” μ„€μ •ν•œ μ‹œκ°„μ— 맞게 μ•ŒλžŒμ„ λ„μ›Œμ£Όλ„λ‘ ν•  μ˜ˆμ •μž…λ‹ˆλ‹€.

그럼 앱을 μ’…λ£Œν•˜λ”λΌλ„ μš°λ¦¬κ°€ μ„€μ •ν•œ μ‹œκ°„μ΄ μ•± 내에 μ €μž₯λ˜μ–΄ μžˆμ–΄μ•Όν•©λ‹ˆλ‹€.

DataBase에 μ €μž₯ν•  수 도 μžˆμ§€λ§Œ User Defaultsλ₯Ό ν†΅ν•΄μ„œ 앱내에 μ €μž₯ν•  수 μžˆλ„λ‘ μ„€μ •ν•΄λ³΄κ² μŠ΅λ‹ˆλ‹€.
(λΈ”λ‘œκ·Έμ—μ„œ UserDefaults κΈ°λ³Έ μžλ£Œν˜•μ΄ μ•„λ‹Œ 경우 μ–΄λ–»κ²Œ μ €μž₯ν•΄μ•Ό 할지λ₯Ό κ³ λ―Όν–ˆμ—ˆλŠ”λ° 였늘과 같은 λ°©λ²•μœΌλ‘œ ν•΄κ²°ν•  수 μžˆμŠ΅λ‹ˆλ‹€.)

κ³ μœ ν•œ μ‹λ³„μžλŠ” UUID λ₯Ό ν†΅ν•΄μ„œ κ³ μœ ν•œ id λ₯Ό μƒμ„±ν•˜λ„λ‘ μ„€μ •ν•˜κ³ , μš°λ¦¬λŠ” μ‚¬μš©μžκ°€ μ„€μ •ν•œ μ‹œκ°„κ³Ό ν•΄λ‹Ή μ•ŒλžŒμ„ On,Off ν•˜λŠ”μ§€λ₯Ό μ €μž₯ν•˜λ„λ‘ μ»€μŠ€ν…€ νƒ€μž…μ„ λ§Œλ“€μ–΄μ•Ό ν•˜κΈ° λ•Œλ¬Έμ— Codable νƒ€μž…μœΌλ‘œ μ„€μ •ν•©λ‹ˆλ‹€.
πŸ”₯ 인코딩, 디코딩을 ν†΅ν•΄μ„œ μžμ‹  <-> μ™ΈλΆ€ν¬ν˜„ 으둜 λ³€ν™˜ν•˜κΈ° μœ„ν•΄μ„œ


2) Content & Trigger

extension UNUserNotificationCenter {
    func addNotificationRequest(by alert: Alert) {
        let content = UNMutableNotificationContent()
        content.title = "μ„€μ •ν•œ μ‹œκ°„μ΄μ—μš”"
        content.body = "식사λ₯Ό κΈ°λ‘ν•΄λ³΄μ„Έμš”"
        content.sound = .default
        content.badge = 1
        
        let component = Calendar.current.dateComponents([.hour, .minute], from: alert.date)
        let trigger = UNCalendarNotificationTrigger(dateMatching: component, repeats: alert.isOn)
        
        let request = UNNotificationRequest(identifier: alert.id, content: content, trigger: trigger)
        self.add(request, withCompletionHandler: nil)
    }
}

μš°λ¦¬κ°€ μ•žμ„œ μ„€μ •ν•œ ꡬ쑰체λ₯Ό ν†΅ν•΄μ„œ content 와 trigger λ₯Ό λ§Œλ“€μ–΄μ„œ μ•Œλ¦Όμ„ μ˜ˆμ•½ ν•΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

content의 κ²½μš°μ—λŠ” μ•„λž˜μ˜ 4가지λ₯Ό μ„€μ •ν•΄ 쀄 수 μžˆμŠ΅λ‹ˆλ‹€.

  • title : 제λͺ©
  • body : λ‚΄μš©
  • sound : μ•ŒλžŒ μ†Œλ¦¬
  • badge : μ•± μ•„μ΄μ½˜ μš°μΈ‘μƒλ‹¨μ˜ 숫자

trigger의 κ²½μš°μ—λŠ” μ•„λž˜μ˜ 3가지 트리거λ₯Ό μ΄μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

  • μ‹œκ°„κ°„κ²© : μ΅œμ†Œ 60초 μ΄μƒμ˜ μ‹œκ°„κ°„κ²©μœΌλ‘œ μ•Œλ¦Όμš”μ²­
  • μΊ˜λ¦°λ” : νŠΉμ •λ‚ μ§œ(μ‹œκ°„)에 μ•ŒλžŒ μš”μ²­
  • μœ„μΉ˜κΈ°λ°˜ : μ‚¬μš©μžκ°€ νŠΉμ •μœ„μΉ˜μ— μ ‘κ·Όν–ˆμ„ λ•Œ μ•ŒλžŒμš”μ²­

μœ„μ™€ 같이 id, content, trigger λ₯Ό μ„€μ •ν•΄μ£Όκ³  NotificationCenter에 NotificationRequestλ₯Ό μΆ”κ°€ν•˜λ©΄ λ©λ‹ˆλ‹€.


πŸ”¨ User Defautls

그럼 μ‚¬μš©μž μ»€μŠ€ν…€ νƒ€μž…μ„ User Defaults 에 μ €μž₯ν•˜κΈ° μœ„ν•΄μ„œλŠ” μ–΄λ–»κ²Œ ν•΄μ•Όν• κΉŒμš”?

var alertList: [Alert] = []
let userNotificationCenter = UNUserNotificationCenter.current()

func makeAlertList() -> [Alert] {
        guard let data = UserDefaults.standard.value(forKey: "alerts") as? Data,
              let alerts = try? PropertyListDecoder().decode([Alert].self, from: data) else { return [] }
        return alerts
    }

μš°μ„  UserDefaults 에 μ €μž₯된 데이터λ₯Ό λ°°μ—΄ alertList에 μ €μž₯ν•˜κΈ° μœ„ν•΄μ„œλŠ” λ””μ½”λ”© ν•΄μ£ΌλŠ” 과정이 ν•„μš”ν•©λ‹ˆλ‹€.

λ””μ½”λ”© 된 값듀을 Alert νƒ€μž… 배열에 μ €μž₯μ‹œν‚€κ³  ν•΄λ‹Ή 배열을 λ°˜ν™˜ν•©λ‹ˆλ‹€.

@IBAction func addButtonClicked(_ sender: UIBarButtonItem) {
        let sb = UIStoryboard(name: "Main", bundle: nil)
        let vc = sb.instantiateViewController(withIdentifier: "AddViewController") as! AddViewController
        vc.pickedDate = { [weak self] date in
            guard let self = self else { return }
            let newAlert = Alert(date: date, isOn: true)
            var alerts = self.makeAlertList()
            //ν˜„μž¬ 배열에 μΆ”κ°€
            alerts.append(newAlert)
            alerts.sort { $0.date < $1.date }
            self.alertList = alerts
            //μœ μ € λ””ν΄νŠΈμ— μƒˆλ‘œμš΄ μ•ŒλžŒμ„ μΆ”κ°€
            UserDefaults.standard.set(try? PropertyListEncoder().encode(self.alertList), forKey: "alerts")
            self.userNotificationCenter.addNotificationRequest(by: newAlert)
            
            self.tableView.reloadData()
        }
        self.present(vc, animated: true, completion: nil)
    }

λ°˜λŒ€λ‘œ, μ‚¬μš©μžκ°€ μ„€μ •ν•œ μ•ŒλžŒ 정보λ₯Ό(ID) User Defaults 에 μ €μž₯ν•˜κΈ° μœ„ν•΄μ„œλŠ” 인코딩 μž‘μ—…μ΄ ν•„μš”ν•©λ‹ˆλ‹€.


⏲ 좔가적인 μ„€μ •

μ•Œλ¦Όμ„Όν„°μ— μ•ŒλžŒμ΄ ν‘œμ‹œ 되기 μœ„ν•΄μ„œλŠ” μ‚¬μš©μžκ°€ κΆŒν•œμ„ ν—ˆμš©ν•΄μ•Όλ§Œ μ•Œλ¦Όμ„Όν„°μ— μ•Œλ¦Όμ΄ ν‘œμ‹œλ  수 μžˆμŠ΅λ‹ˆλ‹€.

imagePickerμ—μ„œ κΆŒν•œ 섀정을 μœ„ν•΄μ„œ κΆŒν•œλ¬Έκ΅¬λ₯Ό plist둜 μ„€μ •ν–ˆλ˜ κ²ƒκ³ΌλŠ” 달리 μ‹œμŠ€ν…œμ—μ„œ κΆŒν•œ 문ꡬ가 미리 κ΅¬ν˜„λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€.

let userNotificationCenter = UNUserNotificationCenter.current()
//μ‚¬μš©μžλ‘œλΆ€ν„° κΆŒν•œμ„ 받도둝 ν•˜λŠ” μ„€μ •
let authorizationOptions = UNAuthorizationOptions(arrayLiteral: [.alert, .badge, .sound])

userNotificationCenter.requestAuthorization(options: authorizationOptions) { _, error in
            if let error = error {
                print("ERROR: \(error.localizedDescription)")
            }
        }

μœ„μ™€ 같은 μ½”λ“œλ₯Ό μ•ŒλžŒμ„ μ΄μš©ν•œ view의 viewDidLoad에 μž‘μ„±ν•΄μ„œ μ‚¬μš©ν•˜λ©΄ λ©λ‹ˆλ‹€.
❗️UNNotificiationλŠ” currnet λ₯Ό μ‚¬μš©ν•΄μ•Ό ν•©λ‹ˆλ‹€


🏷 P.S.

μ „μ²΄μ½”λ“œλŠ” κΉƒν—ˆλΈŒλ₯Ό ν†΅ν•΄μ„œ 확인 ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

κ°•μ˜λ₯Ό λ“€μœΌλ©΄μ„œ λ‹¨μˆœνžˆ μ•ŒλžŒμ„€μ • 뿐만 μ•„λ‹ˆλΌ

  • UserDefaults 에 μ»€μŠ€ν…€ νƒ€μž…μ„ μ„€μ •ν•˜λŠ” 법
  • ν΄λ‘œμ €λ₯Ό ν†΅ν•΄μ„œ ν™”λ©΄κ°„ κ°’ 전달

λ“± λͺ°λžλ˜ 기술(?) μ†Œν™€ν•˜κ²Œ μƒκ°ν•˜κ³  있던 κ°œλ…λ“€μ„ ν•œλ²ˆ 더 곡뢀할 수 μžˆμ—ˆμŠ΅λ‹ˆλ‹€.

이제 μ•± μ—…λ°μ΄νŠΈλ₯Ό μœ„ν•΄ μΆ”κ°€μ μœΌλ‘œ

  • Realm λ§ˆμ΄κ·Έλ ˆμ΄μ…˜
  • Scroll View

λ₯Ό μœ„μ£Όλ‘œ ν•™μŠ΅μ„ ν•˜κ³  λ‚΄μš©μ„ μ •λ¦¬ν•˜λŠ” μ‹œκ°„μ„ κ°€μ§ˆκ²ƒ κ°™μŠ΅λ‹ˆλ‹€.

profile
κ°œλ°œμžκ°€ λ˜κ³ μ‹Άμ–΄μš”

0개의 λŒ“κΈ€