선생님! 종례할 시간입니다!

SteadySlower·2022년 1월 19일
0
post-thumbnail

기능 소개 ⏰

선생님들께서는 종종 봉사 지도 명단을 학생들에게 알려주는 것을 잊어버리곤 하죠. 그런 경우 담임 선생님과 학생 그리고 학생부 교사 사이에 불필요한 오해가 생길 수 있습니다. 😅 이를 방지하기 위해서 컴동쌤은 수업이 끝나는 시간 5분 전에 (4시 35분) 알람을 통해 담임 선생님께 명단을 확인하시도록 하겠습니다.

계획

이상적인 계획 🤩

가장 이상적인 경우는 서버에서 푸시알림의 형태로 보내주는 것입니다. 그렇게 하면 학교 일정에 맞추어 다른 시간에 알림을 보낼 수도 있고 학생부 전달 사항이 있을 때 알림을 통해서 전달할 수도 있을 것 같습니다. 하지만 푸시 알림은 구현 까다롭습니다. 또한 iOS의 푸시 알림의 경우 반드시 애플의 서버를 거쳐서 보내야 합니다.

현실적인 계획 🤔

따라서 이번 포스팅에서는 계획을 단순화해서 구현하겠습니다. 선생님이 언제든 앱을 켜면 당일 4시 35분에 알람을 등록하겠습니다. 예를 들어 선생님이 아침에 학생부 앱을 켜면 그날 오후에 울릴 알람을 등록하는 방식입니다.

구현 (알람 기능 틀 구현)

알람을 구체적으로 만들기 전에 일단은 알람 기능의 틀만 구현해놓겠습니다.

AlarmService 만들기

  1. 다른 서비스들 처럼 알람 서비스도 싱글톤으로 구현했습니다.
  2. 외부에서 알람을 등록하기 위해 사용할 함수입니다. UNUserNotificationCenter를 통해 알림 권한을 얻은 이후에 알림을 등록할 수 있도록 합니다.
  3. 알림센터에 실제 알림을 추가하는 함수입니다. 일단은 정적인 메시지를 넣어두었습니다만 서버와 연동해서 담당 반의 실제 데이터를 받아올 예정입니다. 그리고 일단은 테스트를 위해서 3초 후에 알림이 울리도록 구현해보았습니다.
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에서 알림 붙이기

앱이 실행되면 바로 해당 알림을 등록할 수 있도록 AppDelegate의 didFinishLaunchingWithOptions부분에 알림을 등록하는 코드를 넣겠습니다.

class AppDelegate: UIResponder, UIApplicationDelegate {

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        
        AlarmService.shared.registerAlarm()
        
        return true
    }

// 후략...

}

결과

앱을 켜고 백그라운드로 보내면 3초 후에 알림이 뜨는 것을 볼 수 있습니다.

구현 (동적 메시지)

동적 메시지를 위한 GuidanceService API 구현

정석대로라면 서버에 학년과 반을 보내고 생활 지도 학생 수를 받아오는 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)
    }
}

동적 메시지 AlarmService에 적용

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)")
            }
        }
    }
}

구현 (원하는 시간)

DateComponents를 만들어 주는 Utility 함수 만들기

위에 틀을 짤 때 사용한 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는 특정 날짜에 알림을 울릴 수 있도록 해주는 트리거입니다. 기존의 트리거를 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분에 자신의 반에 학생이 몇명 교문에서 지도를 받았는지 알 수 있습니다.

마치며...

  1. 알람 기능은 새로운 도전이었습니다. 하지만 공식문서와 구글이 있으니 두렵지 않더군요!
  2. 영어 교육 관련 알바(?)를 하고 왔습니다. 포스팅이 불규칙적이라 반성하게 됩니다. 앞으로 꾸준히 하려구요!
  3. 기존에 계획했던 기능들이 차근차근 완성되고 있네요. 뿌듯합니다^^
profile
백과사전 보다 항해일지(혹은 표류일지)를 지향합니다.

0개의 댓글