APP-iOS3rd / PJ3T2_Mymory

멋쟁이사자처럼 iOS 앱스쿨 3기 팀 프로젝트
10 stars 3 forks source link

Refactor/my page view model separation #161

Closed jeonguk29 closed 7 months ago

jeonguk29 commented 7 months ago

PR 가이드라인

PR Checklist

PR 날릴 때 체크 리스트

PR Type

어떤 종류의 PR인가요?

연관되는 issue 정보를 알려주세요

Issue Number: #160

PR 설명하기

MyPage와 관련된 상위, 하위 뷰를 재활용 가능하게 만들고 프로토콜 지향 패러다임에 맞춰 MyPageViewModel 분리

어떻게 작동하나요? code 기반으로 설명해주세요

MemoListViewModel 라는 프로토콜을 만들어 MyPageViewModel와 OtherUserViewModel에서 채택하여 사용하도록 구현 하였습니다.

//
//  MemoListViewModel.swift
//  MyMemory
//
//  Created by 정정욱 on 2/2/24.
//

import SwiftUI
import _PhotosUI_SwiftUI
import FirebaseAuth
import FirebaseCore
import FirebaseFirestore

protocol MemoListViewModel: ObservableObject {
    var memoList: [Memo] { get set }
    var selectedFilter: SortedTypeOfMemo { get set }
    var isShowingOptions: Bool { get set }
    var isCurrentUserLoginState: Bool { get set }
    var user: User? { get set }
    var currentLocation: CLLocation? { get set }
    var lastDocument: QueryDocumentSnapshot? { get set }

    func fetchDistanceOfUserAndMemo(myLocation: CLLocationCoordinate2D, memoLocation: Location) -> Double
    func sortMemoList(type: SortedTypeOfMemo)
    func fetchUserState()
    func fetchCurrentUserLoginState() -> Bool
    func fetchCurrentUserLocation(returnCompletion: @escaping (CLLocation?) -> Void)
    func pagenate(userID: String) async
}

뷰에서 프로토콜 타입으로 넘겨받아 재사용 가능하게 만들었으며 상태에 따라 각각의 뷰 모델을 사용하게 했습니다.

var body: some View {
        ZStack(alignment: .top) {
            Color.bgColor.edgesIgnoringSafeArea(.top)

            ScrollView(.vertical, showsIndicators: false) {
                VStack(alignment: .leading) {
                    // 로그인 되었다면 로직 실행
                    if let currentUser = authViewModel.currentUser, let userId = UserDefaults.standard.string(forKey: "userId") {
                        let isCurrentUser = authViewModel.userSession?.uid == userId

                        // 상대방 프로필을 표시할 때는 제네릭을 사용하여 OtherUserViewModel을 전달 MyPage를 표시할 때는 MypageViewModel 전달
                        if fromDetail == true && otherUserViewModel.memoCreator.isCurrentUser == false  {
                            OtherUserTopView(memoCreator: $otherUserViewModel.memoCreator, viewModel: otherUserViewModel)
                            createHeader(isCurrentUser: isCurrentUser)
                            ProfileMemoList<OtherUserViewModel>().environmentObject(otherUserViewModel)
                        } else {
                            MypageTopView() //
                            createHeader(isCurrentUser: isCurrentUser)
                            ProfileMemoList<MypageViewModel>().environmentObject(mypageViewModel)
                        }
                    } else {
                        showLoginPrompt()
                    }
                }

            }

    }

//이 구조체는 MemoListViewModel 프로토콜을 준수하는 어떤 뷰모델 타입(ViewModel)을 받을 수 있습니다.
struct ProfileMemoList<ViewModel: MemoListViewModel>: View {
    @EnvironmentObject var viewModel: ViewModel
    @State private var isLoadingFetchMemos = false

    var body: some View {
        LazyVStack(spacing: 12) {
            // 각각의 뷰 모델을 활용하여 메모 리스트를 가져옴
            ForEach($viewModel.memoList, id: \.self) { memo in
                NavigationLink {
                    MemoDetailView(memo: memo)
                } label: {
                    ProfileMemoListCell(memo: memo, viewModel: viewModel)
                        .onAppear {
                            Task {
                                // 1. 로그인 되어있는지 체크
                                // 2. 뷰모델에 따라 알맞은 로직을 실행
                                if let userId = UserDefaults.standard.string(forKey: "userId") {
                                    if let mypageViewModel = viewModel as? MypageViewModel {                                        
                                        self.isLoadingFetchMemos = true
                                        await mypageViewModel.pagenate(userID: userId)
                                        self.isLoadingFetchMemos = false
                                    } else if let otherUserViewModel = viewModel as? OtherUserViewModel {
                                        self.isLoadingFetchMemos = true
                                        let userId = otherUserViewModel.memoCreator.id?.description
                                        await otherUserViewModel.pagenate(userID: userId ?? "")
                                        self.isLoadingFetchMemos = false
                                    }
                                }
                            }
                        }
                }
                .buttonStyle(.plain)
            }
        }

struct ProfileMemoListCell<ViewModel: MemoListViewModel>: View {

    @Binding var memo: Memo
    @ObservedObject var viewModel: ViewModel

가능하다면 추가해주세요

변경 사항 스크린샷 혹은 화면 녹화

폴더, 파일, 클래스, 구조체 이름이 변경되었습니다. image

Test 여부

Test 정보

//예시
let testDatas: [TestData] = [.init(...),...]

기타 언급해야 할 사항들