SwiftUI 팀 프로젝트 혼자 리팩토링하기 #11 : API 연결, NotificationPublisher

·2024년 7월 22일
0

리팩토링 일지

목록 보기
12/13

요약



API 연결

투표 삭제, 투표 즉시 종료 API 연결 완료

<리팩토링 전 ViewModel>


    func deletePost(postId: Int) {
        appLoginState.serviceRoot.apimanager
            .request(.postService(.deletePost(postId: postId)),
                            decodingType: NoData.self)
             .sink { completion in
                 switch completion {
                 case .finished:
                     break
                 case .failure(let error):
                     print("error: \(error)")
                 }
             } receiveValue: { _ in
//                 self.appLoginState.appData.postManager.deleteReviews(postId: postId)
//                 self.appLoginState.appData.postManager.removeCount += 1
                 NotificationCenter.default.post(name: NSNotification.voteStateUpdated, object: nil)
             }
             .store(in: &cancellables)
     }

     func closePost(postId: Int, index: (Int?, Int?)) {
         appLoginState.serviceRoot.apimanager
             .request(.postService(.closeVote(postId: postId)),
                            decodingType: NoData.self)
             .sink { completion in
                 switch completion {
                 case .finished:
                     break
                 case .failure(let error):
                     print("error: \(error)")
                 }
             } receiveValue: { _ in
                 if let postIndex = index.0 {
                     self.appLoginState.appData.postManager.posts[postIndex].postStatus
                            = PostStatus.closed.rawValue
                 }

                 if let myPostIndex = index.1 {
                     self.appLoginState.appData.postManager.myPosts[myPostIndex].postStatus
                            = PostStatus.closed.rawValue
                 }
                 self.fetchPostDetail(postId: postId)
             }
             .store(in: &cancellables)

     }
  • viewModel에서 request를 보내고 receiveValue 호출 시 알맞은 로직 수행

<리팩토링 후 ViewModel>

        case .deleteVote:
            voteUseCase.deleteVote(postId: postId)
                .sink { _ in
                } receiveValue: { [weak self] _ in
                    NotificationCenter.default.post(name: .voteDeleted, object: nil)
                    self?.isVoteManageSucceed.toggle()
                }
                .store(in: &cancellables)

        case .closeVote:
            voteUseCase.closeVote(postId: postId)
                .sink { _ in
                } receiveValue: { [weak self] _ in
                    NotificationCenter.default.post(name: .voteClosed, object: nil)
                    self?.send(action: .loadDetail)
                }
                .store(in: &cancellables)
        }
  • 각각의 viewModel에서 action이 호출될 때, useCase에 접근한다.
  • receiveValue가 호출되었을 때 필요한 처리를 한다.
    - 각 action이 실행되면, 투표 목록 뷰에서 종료된 투표는 종료되었다는 UI 업데이트, 삭제된 투표는 목록에서 remove되어야 한다.
    - 이를 NotificationCenter 을 통해 구현했다.

NotifcationCenter 관련

<리팩토링 전>

  • View
        .onDisappear {
            NotificationCenter.default.removeObserver(NSNotification.voteStateUpdated)
            NotificationCenter.default.removeObserver(NSNotification.userBlockStateUpdated)
        }
        .onReceive(NotificationCenter.default.publisher(for: NSNotification.voteStateUpdated)) { _ in
            viewModel.fetchPosts(visibilityScope: visibilityScope)
        }
        .onReceive(NotificationCenter.default.publisher(for: NSNotification.userBlockStateUpdated)) { _ in
            vie
  • view에서 직접 onReceive 를 통해 받고, onDisappear 이 실행될 때, removeObserver 을 직접 수행
  • 이 코드가 나쁜 코드는 아니지만, 옵저버를 직접 해제해 줘야 한다는 것은 프로젝트가 커질수록 쉽게 까먹을 수 있는 부분이라고 생각한다.

<리팩토링 후>

  • ViewModel
    private func bind() {
        NotificationCenter.default.publisher(for: .voteDeleted)
            .receive(on: DispatchQueue.main)
            .sink { [weak self] _ in
                guard let self = self else { return }
                self.votes.remove(at: self.currentVote)
            }
            .store(in: &cancellables)

        NotificationCenter.default.publisher(for: .voteClosed)
            .receive(on: DispatchQueue.main)
            .sink { [weak self] _ in
                guard let self = self else { return }
                self.votes[self.currentVote].postStatus = "CLOSED"
            }
            .store(in: &cancellables)
    }
  • viewModelbind() 함수
  • Notification의 알림을 Combine의 Publisher 로 전환하였다.
  • sink 시 목록에서 제거 및 종료 UI로 업뎃되도록 했다.
  • store하여 removeObserver 을 수행하지 않아도 메모리가 정리될 수 있도록 한다.

removeObserver 하는 것을 매번 까먹었었는데, Combine으로 전환하여 사용하니 직접 remove해 주지 않아도 돼서 매우 만족.

또한, View와 ViewModel의 역할을 좀 더 명확하게 분리할 수 있는 것 같음.


0개의 댓글

관련 채용 정보