ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [출시를 향한 여정] 타이머 어플 - 2. 시간 설정, 백그라운드/포그라운드 전환 구현
    iOS 2021. 10. 3. 16:37

     

     

    목차 

     

    1. 현재 앱 개발 상황

    2. 챌린지 (어려웠던 점)

    3. 앞으로 구현할 것


     

    8월 초에 시작한 타이머 어플 개발이 코테 연습과 개강을 맞이하여 시간을 많이 쏟지 못한데다가 처음 만드는 어플이였기에 생각한 것 보단 속도가 지지부진한 감이 있었습니다. 그리고 백그라운드에서는 특정한 상황을 제외하고는 앱 실행에 제약이 있다는 것을 깨닫고 처음 계획했던 '타이머가 흐르는 시간을 상태바에 출력' 기능이 불가능 하단걸 깨달았습니다. 그렇다면 백그라운드 -> 포그라운드 전환 시에 시간이 지난 것을 어떻게 표현할지에 대한 고민에 부딪혔습니다.

     

    지난 포스트

    https://paul-goden.tistory.com/4

     

    [출시를 향한 여정] 타이머 어플 - 1. 왜 타이머 어플을 만들고 싶어요?

    iOS로 분야를 정한 이유 2021년 1월까지 전문 분야를 무엇으로 해야하나 정하지 못하였습니다. 프론트엔드, 백엔드, 안드로이드, iOS, 서버 등등.. 중에 무엇을 도메인으로 잡아야 행복하게 개발을

    paul-goden.tistory.com

    이번 포스트에서는 1개월반 동안의 UI 개선점들과 맞닥뜨렸던 어려움들에 대해 풀어보는 시간을 가지도록 하겠습니다.

     


    1. 현재 앱 개발 상황

     

    현재 타이머 어플은 3개의 뷰컨트롤러를 갖고 있습니다. 이전 포스트의 1개의 뷰컨트롤러(타이머 부분)에 비해 2개가 늘었습니다. 하나는 집중 시간과 휴식 시간을 설정할 수 있는 옵션 페이지(OptionVC), 다른 하나는 시간을 설정하는 시간 설정 페이지(TimeSelectVC)입니다. 3개의 뷰컨트롤러는 네비게이션 컨트롤러를 통해 화면 전환이 이루어집니다.

     

    네비게이션 컨트롤러 화면 전환

     

    포커스 타임이 끝나면 브레이크 타임이 바로 시작되며 반대도 마찬가지입니다. 타이머가 흐르고 있을 때 앱이 백그라운드로 진입한다면 각 세션이 끝날때마다 알림을 보냅니다. 추후 포그라운드로 전환시에 흘러간 시간만큼 타이머에 적용됩니다. 백그라운드/포그라운드 전환시에 흘러간 시간을 어떻게 구현할지에 대한 어려움이 있었습니다. 뒤에 나올 챌린지에서 이야기를 풀어보겠습니다.

    빠른 테스트를 위해 1초에 1분이 흐르도록 설정하였습니다.

    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://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/iPhoneOSKeys.html#//apple_ref/doc/uid/TP40009252-SW22

    https://www.andyibanez.com/posts/common-reasons-background-tasks-fail-ios/

    https://leocardz.com/2017/06/07/sectionedslider-ios-11-control-center-slider

     

Designed by Tistory.