인프런 영문 브랜드 로고
인프런 영문 브랜드 로고

인프런 커뮤니티 질문&답변

ycc3819님의 프로필 이미지
ycc3819

작성한 질문수

Part2: 초중급 iOS 인스타그램 클론(SwiftUI, MVVM, Firebase, 2024)

31. 회원가입 기능 구현하기

@Bindable VS @Environment + @Bindable

해결된 질문

작성

·

144

2

안녕하세요. 강의 잘 듣고 있습니다.

첫번째 질문.

다름이 아니라, 처음에 회원가입 기능을 구현할 때,

ViewModel@Observable을 통해 관찰 가능한 상태로 두고,

ViewModel을 다루는 가장 상위 View인 ContentView에서 @State로 선언하는 것까지는 이해가 됩니다.

ContentView의 하위 뷰들 중에서,

로그인과 관련한 뷰는

Login - EnterEmail - EnterPassword - EnterName - EnterUserName - Complete인데,

강사님께서 코드로 작성해주신 간접적으로 접근하는 방법 말고,

    @Environment(SignupViewModel.self) var signupViewModel
    var body: some View {
        @Bindable var signupViewModel = signupViewModel

처음 설명해주신 직접적인 접근 방식을 활용했습니다.

@Bindable var signupViewModel: SignupViewModel
    var body: some View{

와 같이, ViewModel에 read-write가 가능하기 위해 @Bindable을 사용한다는 사실은 알고 있습니다.

하지만 문제는 아래와 같이 입력한 결과에 대해서 출력도 잘 하는데, @Bindable을 적용하였을 때, 마지막 CompleteView에서 아래의 완료 버튼을 눌렀을 때 MainTabView으로 넘어가지 않은 문제가 생겼습니다.

스크린샷 2024-07-24 오전 11.47.40.png스크린샷 2024-07-24 오전 11.47.32.png

기존

Auth.auth().currentUser 을 사용했을 때, swiftUI에서 변화를 감지 못하기 때문에, 알려주신대로 ViewModel 내부에서 var currentUserSession: FirebaseAuth.User? 프로퍼티를 만들어 했는데.. 왜 이러한 문제점이 생기는걸까요?

if signupViewModel.currentUserSession != nil{
            MainTabView()
        } else {
            LoginView()
                .environment(signupViewModel)
        }

물론, 강사님이 알려주신 방법대로 하면 잘 넘어갑니다 ^^.

두 번째 질문

간접적인 방법 .environment를 사용했을 때, 오류가 떠서 CANVAS를 끄고 작업을 하셨는데 오류를 없앨수 있는 방법이 있을까요?

1) 최상단 InstagramCloneApp에 ViewModel을 적용?

2) environment로 설정되는 모든 뷰의 #Preview에 .enivronment() 적용?

긴 글 읽어주셔서 감사합니다 😃

답변 기다리겠습니다.

답변 1

0

애구마(agmma)님의 프로필 이미지
애구마(agmma)
지식공유자

안녕하세요 ycc3819님

먼저 강의 수강해주셔서 감사합니다.

  1. 직접 viewModel을 넘겨줄때 회원가입이 완료되지 않는 문제

저는 직접 넘겨줘도 회원가입이 완료되어 화면이 전환되거든요? 제가 사용한 코드 참고해서 다시 한번 시도해보시겠어요?

import SwiftUI
import FirebaseAuth

struct ContentView: View {
    @State var signupViewModel = SignupViewModel()
    
    var body: some View {
//        if Auth.auth().currentUser != nil {
        if signupViewModel.currentUserSession != nil {
            MainTabView()
        } else {
            LoginView(signupViewModel: signupViewModel)
//                .environment(signupViewModel)
        }
    }
}

 

LoginView, EnterEmailView, EnterPasswordView, EnterNameView, EnterUsernameView, CompleteSignupView에서

전부 @Bindable var singupViewModel: SignupViewModel 로 뷰모델을 위한 바인더블 변수 생성및

다음으로 이어지는 뷰에 인자로 singupViewModel을 넘겨줌

아래는 LoginView에 코드만 넣었는데 위에 해당하는 모든 뷰에 다 적용했습니다.

struct LoginView: View {
    @Bindable var signupViewModel: SignupViewModel
    var body: some View {
        NavigationStack {
            ZStack {
             ...       
                    NavigationLink {
                        EnterEmailView(signupViewModel: signupViewModel)
                    } label: {
                        ...
                    }
                }
            }
        }
    }
}

 

이렇게 한번 해보시고 안되면 또 질문주세요 ㅎㅎ

 

  1. Environment canvas에러 문제

이건 environment를 사용하면 나타나는 문제입니다.

예를들어 EnterEmailView() 파일만 봤을때는 아직 Environment에 넣어준 뷰가 없는데, 찾을 수가 없어서 나는 거죠.

반면 코드를 실행(cmd + r)하면 EnterEmailView뿐만아니라 ContentView도 다 연결이 되어있어서 이전화면에서 environment에 넣어준 signupViewModel을 찾을 수 있어서 뷰모델을 잘 전달할 수 있게 됩니다.

 

프리뷰에서도 environment에 signupViewModel을 잘 넣어주면 캔버스에 에러가 사라집니다.

아래 코드처럼 SingupViewModel을 environment에 넣고 프리뷰를 실행하면 됩니다.

혹시 프리뷰에 에러가 나면 xcode를 재시작해주면 됩니다.

#Preview {
    EnterEmailView()
        .environment(SignupViewModel())
}

 

혹시 1번사항에서 계속 문제가 생기면 질문 다시 남겨주세요~

감사합니다.

ycc3819님의 프로필 이미지
ycc3819
질문자

감사합니다 😃

1번 질문에 대한 내용을, 오늘 다시 작성해서 진행해보니 문제 없이 잘 작동합니다.

간접적인 방법(@Environment)이 각 View에 인자를 전달하지 않고도 진행할 수 있게 되네요.

2번 질문 해결방법도 잘 적용됩니다.

 

ycc3819님의 프로필 이미지
ycc3819

작성한 질문수

질문하기