[iOS] 북킵: UserDefaults를 사용하여 비정상 종료 대응하기

문제

앱의 주요 기능 중 하나인 독서 기록하기는 타이머를 사용하여 사용자의 독서시간을 실시간으로 트래킹하고 있습니다. 타이머는 앱이 백그라운드를 진입하여도 동작하며 다시 포어그라운드로 돌아왔을때 시차를 계산하여 독서 시간을 계산하도록 되어있습니다. 독서를 하는 도중 앱이 중단되거나 사용자가 의도치 않게 앱을 종료했을 경우 그 동안에 기록된 시간과 책의 정보를 가지고 있다가 다음번에 앱이 실행되었을때 이것을 이어서 기록할 수 있는 기능을 추가해야했습니다.

해결방안

Home

이것을 위해 읽는 중에 UserDefaults를 사용하여 현재 독서 세션의 정보를 저장하는 방법을 채택했습니다.

전체적인 흐름은 다음과 같습니다.

먼저 홈 뷰 진입시 UserDefaults에 마지막으로 읽었던 상태를 확인하기 위해 enum으로 LastReadingState를 정의하여 관리합니다.

enum은 다음과 같이 정의되어있습니다.


//ReadingViewModel.swift
enum ReadingState: String{
    case reading
    case standby
}

독서기록뷰에 진입하면 해당 enum타입의 변수는 reading으로 바뀌며 중간에 독서를 중지 할 경우 standby가 됩니다. 따라서 최초 진입시 저 값을 UserDefaults에서 확인하여 alert 표기 후 분기처리를 하여 독서기록을 저장하는 뷰인 ReadCompleteView를 sheet presentation을 통해 보여지게하였습니다.

Image 1Image 2
UserDefaults 확인 후 alert로 대응불러오기 선택시 ReadingCompleteView Push

ReadingCompleteView를 통해 독서기록은 해당 책의 NestedTable로 Realm에 영구저장합니다.

ReadingView

홈 화면에서 UserDefaults를 불러오기 위해선 독서하는 화면인 ReadingView에서 실시간으로 값을 기록해줘야합니다.
여기서 고민한 점은 시간 저장 빈도수였고 저는 앱이 포어그라운드 상태일땐 1초 단위로 시간을 기록하기로 결정했습니다.
UseCase에 따라 책 정보, 상태 등은 UserDefaults에 write횟수가 적었으나 독서 시간은 적게는 초, 많게는 분단위로 저장 할 수 있다고 생각했습니다. 처음엔 1분 단위로 저장할까 생각하였으나 UserDefaults가 작고 크리티컬하지 않은 primitive type을 앱의 샌드박스 내에서 좋은 퍼포먼스를 낸다는 특성을 고려하여 1초에 한번씩 write하기로 결정했습니다.

Image 1Image 2
책을 읽는 중 초 단위로 UserDefaults에 저장 독서완료시 ReadingCompleteView Push

사용자가 정상적으로 독서를 완료하거나 홈 화면에서 버리기를 선택한 경우 UserDefaults를 nil상태로 초기화했습니다.

아쉬운 점

개선의 여지가 있는 점은 아무래도 타이머 기능일것 같습니다. 현재 동작 방식은 앱이 백그라운드에 들어갔다가 다시 포어그라운드로 진입 할 시에 시차를 계산하여 독서시간을 업데이트하는 방식이기 때문에 백그라운드에서 시간 기록이 다소 어렵다는 점이 있습니다. 그러나 이 부분은 서버를 사용하게 된다면 간단하게 해결 가능할것 같으며 추후 위젯을 도입 할 때 ActivityKit 을 사용한 잠금화면 화면 위젯에서 대응 할 수 있을것으로 생각합니다.

0개의 댓글