Todo앱 시리즈3 - Rx + UrlSession + MVVM

라무·2023년 8월 9일

TodoApp 시리즈

목록 보기
3/3

반드시 해야하는 작업

  • 할일 목록 전체 불러오기
    • 데이터 날짜 별로 그룹핑해서 데이터 보여주기
      • 즉, createdAt 혹은 updatedAt날짜가 section이 되고 section에는 해당 날짜에 올리거나 수정된 todo가 들어가야 한다
    • 스크롤이 바닥(맨 마지막)이 보이면 데이터를 더 불러와야 한다
      • 즉, 다음 페이지를 불러와야 한다
  • +버튼 누르면 할일 목록 추가하기 → 할일 목록 db에 데이터 보내기 + tableView데이터 수정
  • 해당 할일을 클릭하면 모달이 뜨고 데이터 수정하기(수정하기 api이용)
  • 해당 할일의 체크박스를 누르면 is_done변수가 true로 변경되고 db데이터가 수정되어야 한다
  • 해당 할일을 클릭하면 모달이 뜨고 데이터 삭제하기 (삭제하기 api이용)
  • searchBar를 통해서 관련된 할일 목록을 찾을 수 있어야 한다
    • 검색어가 있는 상태에서 스크롤을 하면 그 해당하는 데이터만 더 불러와야 한다
  • 완료 숨기기 버튼을 누르면 현재 데이터들 중에 isDone이 true인 데이터들을 숨겨야 한다

하면서 어려웠던 부분

  1. 날짜 formate이 제대로 이루어지지 않는다

    1. string → date의 formatting이 제대로 이루어지지 않아서 안되는데 string에서 date로 formatting을 진행할 때 dateFormate의 형식을 string과 동일하게 맞춰줘야 date로 formate이 된다
  2. urlSession부분 rx로 변경하기

    1. rx를 사용하지 않을 때
      1. dataTask의 {}에서 urlSession을 통해서 요청한 데이터를 받아와서 어떤 처리를 할 건지 작성해준다
      2. completion을 사용하는 이유는 가공한 데이터를 사용하는 곳이 여기가 아니라 viewController에서 사용할 것이기 때문
        • 즉, 응답에 대한 결과를 해당 함수 내에서 사용하는 것이 아니라 다른 곳에서 사용한다
        • 받아온 데이터를 tableview에서 보여줄 데이터로 만들어주는 과정을 하는 곳은 TodosRouter쪽이 아니라 ViewController이다.
    2. rx.response를 사용했을 때
    3. rx. data를 사용했을 때
  3. urlSession으로 데이터를 요청하는 부분에서 제대로 응답이 왔나 확인하기 위해서 TodosRouter.fetchTodos()를 그냥 찍어봤는데 unknown context라는 오류가 발생

    → observable이 발생하는데 이 observable을 받아주는 observer가 없어서 발생한 오류(,,,확실하지 않다,,)

    → subscribe나 bind로 observable를 받아주면 사라진다

  4. string을 date로 변환하는 부분

    • createdAt을 원하는 formate으로 변환하기 위해서는 일단 string → date을 해야하는데 이 때 formate을 몰라서 formate이 되지 않는다,,,,
    • string → date를 하기 위해서는 dateFormat을 string의 형식과 동일하게 맞춰줘야 한다
      • ex) string이 2012/07/06이면 dateFormat도 yyyy/mm/dd여야한다
      • 근데 createAt의 formate이 2023-05-11T12:12:47.000000Z이거,,,,
    • 2023-05-11T12:12:47.000000Z
      • 이거는 ISO8601DateFormatter() dateFormate형식

      • 000000Z가 문제인데,,,, → formateOptions을 이용해서 .withInternetDateTime, .withFractionalSeconds를 추가해준다

        let dateFormatter = ISO8601DateFormatter()
        dateFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
  5. 생성된 날짜별로 데이터를 Grouping해서 보여줘야 한다

    → 딕셔너리를 Grouping한다

    init(grouping:by:) | Apple Developer Documentation

  6. 그룹핑해서 데이터를 보여주는 부분까지는 좋았지만 새로운 데이터를 받아오거나 수정해줄때 header를 일일이 찾아서 해당 header에 붙여줘야 해서 별로 효율이 좋을것 같지는 않다,,,,,

    → Cell에 header label을 넣어서 해결(하면서 고민되는 부분의 1번 확인)

  7. 바닥에 도달했을 때 fetchTodos를 한번더 불러서 데이터를 가져온 후에 footer에 있는 spinner부분을 nil처리를 해줘야 하는데 어떻게 할수 있는지 모르겠다,,,,,,,

  8. search데이터를 했을 때 데이터가 없을 경우 에러를 잡아서 처리해줄 때 그냥 notifySearchDataNotFound(true)해주고 tableview의 backgroundView에 searchDataNotFoundView를 넣어줬더니 안되서,,, todosVM의 todoListData을 빈배열로 만들어주고 backgroundView를 넣어줌,,,,

하면서 헷갈렸던 부분

  1. cell에 대한 이벤트 처리

    1. cell에 대한 이벤트(ex. 버튼 클릭 등)은 cell에 대한 정의가 되어있는 곳에서 하는 것이 맞다
    2. 하지만 tableview의 dataSource부분에 관련된 부분은 해당 데이터가 정의되어있는 곳(ex. main viewController 클래스)에서 해야 한다

    → 즉, cell의 UI변경에 해당하는 checkBox의 체크 표시가 달라지는 UI는 cell의 변경에 해당하므로 cell이 정의된 파일에서 해야하지만 checkBox의 상태변경에 대한 데이터는 viewModel이나 viewController에서 해야한다.

  2. urlSession의 dataTask의 completion 블록

    • urlSession의 dataTask의 completion블럭도 fetchTodos의 흐름을 벗어나서 실행이된다
    • 즉, fetchTodo가 끝나도 dataTask함수는 request에 대한 요청이 끝나기 전까지 끝나지 않는다

    → 그러므로 @escaping 클로저에 해당한다

  3. fetchTodo의 completion가 @escaping 클로저인 이유

    1. dataTask는 fetchTodo의 실행이 종료되어도 종료되지 않는 함수

    2. fetchTodo함수보다 urlSession에 request로 데이터를 요청하고 response를 통해서 받아오는 부분이 더 늦게 끝나서,,?

      • 즉, fetchTodo함수가 끝나도 UrlSession으로 데이터를 받는 부분은 비동기로 끝나지 않기 때문에
      • → 이렇게 되면 completion은 fetchTodo의 함수의 흐름에서 벗어난다 → 그러므로 escaping함수를 사용해야 한다

      Swift Escaping Closure 이해하기

    @escaping이 아닐경우 에러 발생

  4. TodoCell의 날짜라벨(Header라벨)의 hidden

    1. 그냥 TodoCell의 todoDate를 view로 감싸지 않고 isHidden을 했더니 label자체는 보이지 않지만 label이 view에 공간을 차지 하고 있었음

    → Label을 view로 감싸주니까 공간차지 자체가 없어짐

하면서 고쳐야 하는 부분

  1. tableview에서 cell에 대한 처리를 UI로 하고 있었던 것이다

    1. ex. cell의 체크박스를 클릭하는 부분은 데이터에 관련된 부분이므로 UI를 수정하는 것이 아니라 데이터를 수정하는 방식으로 가야한다

    → tableview는 데이터 기반으로 동작한다,,!

    항상 데이터의 관점으로 바라보자!(tableView는 데이터 기반으로 동작한다)

    → UI만 변경하는 것은 cell이 reuse될 때 적용이 되지 않을 수 있어서 생각하던 것과 다르게 보여질 수 있다

하면서 고민되는 부분

  1. 날짜별로 그룹핑해서 보여주는 부분이 시간이 오래걸려서 사용성이 안좋다(시간이 오래걸림)

    • 데이터를 그룹핑해야 하는 이유
      • 같은 날짜별로 데이터를 묶어주기 위해서
      • 새로운 데이터를 받아올 때 기존에 이미 해당 날짜가 있다면 그 날짜 아래 똑같이 붙여주기 위해서

    → grouping을 이용하는 것이 아니라 Todo데이터에 previousDate를 저장해서 앞에 데이터 날짜와 동일한지 파악한다

    • 즉, 헤더를 따로 만드는게 아니라 헤더를 TodoCell안에 포함시킨다
    • 일단 Todo데이터는 시간 순으로 정렬이 되어져 있음
      • 그러므로 들어올때 알아서 시간 순으로 들어와진다
    • 하나의 Todo데이터가 전 Todo데이터의 updateDate를 저장하도록 한다
    • 그리고 TodoCell이 자신의 날짜 Label을 가지고 있도록 하고 previosDate(이전 Todo의 날짜)와 updateDate가 같다면 날짜 Label을 보이지 않도록 하고 다르다면 날짜 Label이 보이도록한다
    • 내가 헷갈렸던 부분
      • DB에 저장되어있는 데이터가 시간 순으로 정렬되어있는게 아니라 중구난방으로 되어있을거라고 생각했음
      • 날짜 헤더를 TodoCell로 만드는게 아니라 table view의 section header로 만들어서 해야한다고 생각했음

깃허브 링크

https://github.com/Kimrayoung/ios_TodoList_RxMVVM

profile
ios 개발을 하고있는 라무의 사적인 기술 블로그

0개의 댓글