선생님들께서는 종종 봉사 지도 명단을 학생들에게 알려주는 것을 잊어버리곤 하죠. 그런 경우 담임 선생님과 학생 그리고 학생부 교사 사이에 불필요한 오해가 생길 수 있습니다. 😅 이를 방지하기 위해서 컴동쌤은 수업이 끝나는 시간 5분 전에 (4시 35분) 알람을 통해 담임 선생님께 명단을 확인하시도록 하겠습니다.
가장 이상적인 경우는 서버에서 푸시알림의 형태로 보내주는 것입니다. 그렇게 하면 학교 일정에 맞추어 다른 시간에 알림을 보낼 수도 있고 학생부 전달 사항이 있을 때 알림을 통해서 전달할 수도 있을 것 같습니다. 하지만 푸시 알림은 구현 까다롭습니다. 또한 iOS의 푸시 알림의 경우 반드시 애플의 서버를 거쳐서 보내야 합니다.
따라서 이번 포스팅에서는 계획을 단순화해서 구현하겠습니다. 선생님이 언제든 앱을 켜면 당일 4시 35분에 알람을 등록하겠습니다. 예를 들어 선생님이 아침에 학생부 앱을 켜면 그날 오후에 울릴 알람을 등록하는 방식입니다.
알람을 구체적으로 만들기 전에 일단은 알람 기능의 틀만 구현해놓겠습니다.
struct AlarmService {
//1️⃣
static let shared = AlarmService()
//2️⃣
func registerAlarm() {
let notiCenter = UNUserNotificationCenter.current()
notiCenter.requestAuthorization(options: [.alert, .sound, .badge]) { didAllow, err in
addAlarm()
}
}
private func addAlarm() {
let notiCenter = UNUserNotificationCenter.current()
let notiContents = UNMutableNotificationContent()
notiContents.title = "종례 시간 알림"
notiContents.sound = UNNotificationSound.default
notiContents.subtitle = "오늘 생활지도 학생은 3명입니다."
let notiTrigger = UNTimeIntervalNotificationTrigger(timeInterval: 3, repeats: false)
let request = UNNotificationRequest(identifier: "teacherAlarm", content: notiContents, trigger: notiTrigger)
notiCenter.add(request) { err in
if let err = err {
print("DEBUG: Failed to addAlarm \(err)")
}
}
}
}
앱이 실행되면 바로 해당 알림을 등록할 수 있도록 AppDelegate의 didFinishLaunchingWithOptions부분에 알림을 등록하는 코드를 넣겠습니다.
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
AlarmService.shared.registerAlarm()
return true
}
// 후략...
}
앱을 켜고 백그라운드로 보내면 3초 후에 알림이 뜨는 것을 볼 수 있습니다.
정석대로라면 서버에 학년과 반을 보내고 생활 지도 학생 수를 받아오는 API를 만들어야 합니다. 하지만 지금은 서버를 약식으로 구현하고 있으니 클라이언트에서 전체 생활지도 명단을 받아와서 학년 반을 기준으로 필터링 합시다.
목록을 불러오고 Guidance객체의 배열로 바꾸는 것은 동일합니다.
마지막에 고차함수 filter를 통해서 1학년 1반 학생들만 골라내고 count를 통해서 갯수를 세어 completionHandler에 넣어 실행합니다.
func fetchGuidanceNumOfHR(completionHandler: @escaping (Int) -> Void) {
AF.request("\(SERVER_BASE_URL)/guidances").responseDecodable(of: Response<[GuidanceRawData]>.self) { data in
guard let response = data.value else { return }
guard response.isSuccess == true else { return }
guard let rawdata = response.result else { return }
let guidances = rawdata.map { rawData in
return Guidance(rawData: rawData)
}
let guidanceCount = guidances.filter { guidance in
guidance.student.grade == 1 && guidance.student.classNumber == 1
}.count
completionHandler(guidanceCount)
}
}
completion handler 내부에 메시지를 등록하는 코드를 넣었습니다. API를 통해 받아온 학생 수를 메시지에 반영했습니다.
private func addAlarm() {
GuidanceService.shared.fetchGuidanceNumOfHR { numOfGuidances in
let notiCenter = UNUserNotificationCenter.current()
let notiContents = UNMutableNotificationContent()
notiContents.title = "종례 시간 알림"
notiContents.sound = UNNotificationSound.default
notiContents.subtitle = "오늘 생활지도 학생은 \(numOfGuidances)명입니다."
let notiTrigger = UNTimeIntervalNotificationTrigger(timeInterval: 3, repeats: false)
let request = UNNotificationRequest(identifier: "teacherAlarm", content: notiContents, trigger: notiTrigger)
notiCenter.add(request) { err in
if let err = err {
print("DEBUG: Failed to addAlarm \(err)")
}
}
}
}
위에 틀을 짤 때 사용한 NotificationTrigger는 일정 시간 이후에 알림이 뜨도록 하는 트리거였습니다. 하지만 우리가 원하는 것은 특정 시간에 알림이 뜨는 트리거입니다. 이 트리거를 사용하기 위해서는 DateComponents 객체를 인자로 넣어야 합니다.
따라서 필요한 DateComponents를 만드는 함수를 만들어 Utilities 클래스에 만들어 둡시다. Date()를 통해 오늘 날짜를 가지고 오고 이를 통해 DateComponents객체에 오늘 날짜와 16시 35분이라는 시간을 입력합시다.
중간에 guard문을 통해서 주말이면 알림을 등록하지 않도록 합시다! (주말에 업무 관련 알림 싫잖아요ㅎㅎ 🤬 )
class Utilities {
// 오늘 알람이 울릴 dataComponents 만들기
func getTodayAlarmDateComponents() -> DateComponents? {
let date = Date()
let calendar = Calendar.current
// 주말이면 알림 등록 안함.
guard calendar.isDateInWeekend(date) == false else { return nil }
let year = calendar.component(.year, from: date)
let month = calendar.component(.month, from: date)
let day = calendar.component(.day, from: date)
var dateComponents = DateComponents()
dateComponents.timeZone = .current
dateComponents.year = year
dateComponents.month = month
dateComponents.day = day
dateComponents.hour = 16
dateComponents.minute = 35
return dateComponents
}
}
위에 말했듯이 UNCalendarNotificationTrigger는 특정 날짜에 알림을 울릴 수 있도록 해주는 트리거입니다. 기존의 트리거를 UNCalendarNotificationTrigger로 바꾸어 줍시다.
private func addAlarm() {
GuidanceService.shared.fetchGuidanceNumOfHR { numOfGuidances in
let notiCenter = UNUserNotificationCenter.current()
let notiContents = UNMutableNotificationContent()
notiContents.title = "종례 시간 알림"
notiContents.sound = UNNotificationSound.default
notiContents.subtitle = "오늘 생활지도 학생은 \(numOfGuidances)명입니다."
let dateComponents = Utilities().getTodayAlarmDateComponents()
let notiTrigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: false)
let request = UNNotificationRequest(identifier: "teacherAlarm", content: notiContents, trigger: notiTrigger)
notiCenter.add(request) { err in
if let err = err {
print("DEBUG: Failed to addAlarm \(err)")
}
}
}
}
담임선생님들은 이제 매일 4시 35분에 자신의 반에 학생이 몇명 교문에서 지도를 받았는지 알 수 있습니다.