-
[출시를 향한 여정] 타이머 어플 - 2. 시간 설정, 백그라운드/포그라운드 전환 구현iOS 2021. 10. 3. 16:37
목차
1. 현재 앱 개발 상황
2. 챌린지 (어려웠던 점)
3. 앞으로 구현할 것
8월 초에 시작한 타이머 어플 개발이 코테 연습과 개강을 맞이하여 시간을 많이 쏟지 못한데다가 처음 만드는 어플이였기에 생각한 것 보단 속도가 지지부진한 감이 있었습니다. 그리고 백그라운드에서는 특정한 상황을 제외하고는 앱 실행에 제약이 있다는 것을 깨닫고 처음 계획했던 '타이머가 흐르는 시간을 상태바에 출력' 기능이 불가능 하단걸 깨달았습니다. 그렇다면 백그라운드 -> 포그라운드 전환 시에 시간이 지난 것을 어떻게 표현할지에 대한 고민에 부딪혔습니다.
지난 포스트
https://paul-goden.tistory.com/4
이번 포스트에서는 1개월반 동안의 UI 개선점들과 맞닥뜨렸던 어려움들에 대해 풀어보는 시간을 가지도록 하겠습니다.
1. 현재 앱 개발 상황
현재 타이머 어플은 3개의 뷰컨트롤러를 갖고 있습니다. 이전 포스트의 1개의 뷰컨트롤러(타이머 부분)에 비해 2개가 늘었습니다. 하나는 집중 시간과 휴식 시간을 설정할 수 있는 옵션 페이지(OptionVC), 다른 하나는 시간을 설정하는 시간 설정 페이지(TimeSelectVC)입니다. 3개의 뷰컨트롤러는 네비게이션 컨트롤러를 통해 화면 전환이 이루어집니다.
포커스 타임이 끝나면 브레이크 타임이 바로 시작되며 반대도 마찬가지입니다. 타이머가 흐르고 있을 때 앱이 백그라운드로 진입한다면 각 세션이 끝날때마다 알림을 보냅니다. 추후 포그라운드로 전환시에 흘러간 시간만큼 타이머에 적용됩니다. 백그라운드/포그라운드 전환시에 흘러간 시간을 어떻게 구현할지에 대한 어려움이 있었습니다. 뒤에 나올 챌린지에서 이야기를 풀어보겠습니다.
2. 챌린지 (어려웠던 점)
지금까지의 과정에서 만났던 어려움은 크게 두가지였습니다.
1) 백그라운드 -> 포그라운드 전환시 흐른 시간 표시
2) 시간 선택 UI 구현
1) 백그라운드 -> 포그라운드 전환시 흐른 시간 표시
애플에서는 배터리 소모를 줄이고 성능을 유지하기 위해 백그라운드에서의 앱 실행을 제재하고 있습니다. 특별한 경우에서는 가능하지만 그 중에 이 타이머 어플을 유지시킬수 있는 경우는 없는 것 같습니다. 그렇다면 흐르는 시간을 어떻게 표시를 할까 고민이 되었습니다. 제가 생각한 방법은 SceneDelegate를 이용하는 것이였습니다. sceneDidEnterBackground 가 호출 될 때, 즉, 앱이 백그라운드로 전환되고 난 후 바로 현재 시간을 저장합니다. sceneWillEnterForeground 가 호출 될 때, 즉, 앱이 포그라운드로 전환 되기 전에 현재 시간과 백그라운드로 들어갔던 시간을 계산하여 흐른 시간을 계산하였습니다.
class SceneDelegate: UIResponder, UIWindowSceneDelegate { ... 생략 func sceneWillEnterForeground(_ scene: UIScene) { // Called as the scene transitions from the background to the foreground. // Use this method to undo the changes made on entering the background. guard let start = UserDefaults.standard.object(forKey: "sceneDidEnterBackground") as? Date else { return } print("sceneWillEnterForeground", Date()) let interval = Int(Date().timeIntervalSince(start)) NotificationCenter.default.post(name: NSNotification.Name("sceneWillEnterForeground"), object: nil, userInfo: ["time" : interval]) } func sceneDidEnterBackground(_ scene: UIScene) { // Called as the scene transitions from the foreground to the background. // Use this method to save data, release shared resources, and store enough scene-specific state information // to restore the scene back to its current state. NotificationCenter.default.post(name: NSNotification.Name("sceneDidEnterBackground"), object: nil) UserDefaults.standard.setValue(Date(), forKey: "sceneDidEnterBackground") print("sceneDidEnterBackground", Date()) } }
class ViewModel { @objc func timerEnterForeground(_ notification:Notification) { UNUserNotificationCenter.current().removeAllPendingNotificationRequests() if model.isTimerFlow { print("wholeSec",model.wholeSeconds) //노티피케이션센터를 통해 값을 받아옴 var time = notification.userInfo?["time"] as? Int ?? 0 print("time", time) time *= acceleration //배속 if time < model.wholeSeconds { //타이머 전환이 일어나지 않음 model.wholeSeconds -= time } else { let focusSec = model.minuteFocus * 60 let breakSec = model.minuteBreak * 60 if model.isFocusTime { time -= model.wholeSeconds let timeRange = time % (breakSec + focusSec) if timeRange < breakSec { model.isFocusTime = false model.wholeSeconds = breakSec - timeRange } else { model.isFocusTime = true model.wholeSeconds = focusSec - (timeRange - breakSec) } } else { time -= model.wholeSeconds let timeRange = time % (focusSec + breakSec) if timeRange < focusSec { model.isFocusTime = true model.wholeSeconds = focusSec - timeRange } else { model.isFocusTime = false model.wholeSeconds = breakSec - (timeRange - focusSec) } } } print("wholeSec",model.wholeSeconds) print("focusTime",model.isFocusTime) reload() } } }
SceneDelegate를 이용해 흐른 시간을 나타낼 수 있었고 동시에 앱 생명 주기를 상기시켜주는 챌린지였습니다.
2) 시간 선택 UI 구현
타이머 어플을 만들기 시작했을때 부터 시간을 설정할때 생각해둔 UI 가 있었습니다. 아이폰 기본 타이머 어플에서 짧은 실행으로 제공해주는 시간 설정에서의 UI 인데 평소 즐겨쓰는 기능으로서 꼭 나중에 구현해보고 싶은 생각이 있던 UI 였습니다.
결론을 말씀드리면 제가 구현하지는 못하였습니다. 여러 시도를 해보았지만 시간도 많이 걸릴것 같고 어려운 점도 많이 있었습니다. 이 기능을 포기해야 하나 고민하던 와중에 다행히도 깃헙에서 SectionedSlider 의 이름으로 LeonardoCardoso 가 구현해 놓은 UI가 있었습니다. 직접 구현하는 것이 여의치 않는다면 다른 사람의 코드를 사용하여 생산성을 높이는 방법도 괜찮다는 것을 깨달았습니다. 출시 할 때에 라이선스를 잘 확인하여 어플을 제출해야겠습니다.
3. 앞으로 구현할 것
1) 타이머 타입 전환시 알림으로 flash light 사용
제가 타이머를 사용할때 원하던 기능은 조용한 곳에서도 시간이 다 되었다는 것을 인지시켜주는 기능이였습니다. 아이폰 뒷면 flash light 를 이용하여 소리를 킬 수 없는 도서관, 독서실 등에서도 집중 시간 종료, 휴식 시간 종료를 인지시켜주는 기능을 구현하려고 합니다.
생각 되는 어려움
어플을 껐을때 flash light 기능이 된다면 가장 이상적이겠지만 백그라운드에서 앱 실행에 제약이 있기에 어려울 것 같습니다. 그렇다면 이 기능을 사용하기 위해 어플을 계속 키고 있어야 하기 때문에 배터리 수명을 위해 다크 모드를 지원해야 될 것 같습니다. 더 나아가서 아예 화면을 까맣게 만들고 어플을 포그라운드로 상태로 유지시키는 기능을 추가하는 방법도 고민해보려고 합니다.
References
https://www.andyibanez.com/posts/common-reasons-background-tasks-fail-ios/
https://leocardz.com/2017/06/07/sectionedslider-ios-11-control-center-slider
'iOS' 카테고리의 다른 글
[출시를 향한 여정] 타이머 어플 - 3. LED 플래시를 통해 알림받기 (0) 2021.10.23 [iOS] GCD : 1. 동시성(Concurrency) 프로그래밍 (0) 2021.09.18 [iOS] View Controller의 생명주기 (0) 2021.09.04 Delegate 패턴 (0) 2021.08.28 [출시를 향한 여정] 타이머 어플 - 1. 타이머 어플을 만드려는 이유 (0) 2021.08.15