[SwiftUI] TwitterClone: Authentication Logic

Junyoung Park·2022년 11월 17일
0

SwiftUI

목록 보기
104/136
post-thumbnail

🔴 Let's Build Twitter with SwiftUI (iOS 15, Xcode 13, Firebase, SwiftUI 3.0)

TwitterClone: Authentication Logic

구현 목표

  • 파이어베이스 인증: 회원가입 및 로그인

구현 태스크

  • ObservableObject로 선언된 롰 모델의 환경변수 관리
  • 파이어베이스 회원가입 및 로그인 기능 구현
  • 현재 유저 상태를 통한 인터렉션 제공

구현 화면

핵심 코드

func login(email: String, password: String) {
        Auth.auth().signIn(withEmail: email, password: password) { [weak self] userResult, error in
            guard
                let userResult = userResult,
                error == nil else { return }
            self?.userSession = userResult.user
            print("Firebase Login Did Succeed")
        }
    }
  • 환경 변수로 관리되는 ObservableObject 클래스
  • 파이어베이스 인증 관련 서비스 제공
  • 로그인 함수를 통해 파이어베이스 로그인 및 결곟 리턴 가능
  • @Published 타입의 오브젝트 값 변경
func register(email: String, password: String, userName: String, fullName: String) {
        Auth.auth().createUser(withEmail: email, password: password) { [weak self] userResult, error in
            guard
                let userResult = userResult,
                error == nil else { return }
            print("Firebase Register Did Succeed")
            
            let data = ["email": email, "userName": userName.lowercased(), "fullName": fullName, "uid": userResult.user.uid]
            Firestore.firestore().collection("users")
                .document(userResult.user.uid)
                .setData(data) { [weak self] error in
                    if let error = error {
                        print(error.localizedDescription)
                    } else {
                        self?.didAuthenticateUser = true
                        print("Firestore Register Did Succeed")
                    }
                }
        }
    }
  • 유저 정보를 통한 회원가입 및 파이어스토어 저장
  • 이메일, 이름 정보 파이어스토어 저장 이후 프로필 사진을 저장하기 위해 didAuthenticateUser로 이동
NavigationLink(destination: ProfilePhotoSelectorView(), isActive: $viewModel.didAuthenticateUser) {
                EmptyView()
            }
  • 회원가입 뷰에서 해당 롰 모델의 @Published 변수인 didAuthenticatieUser뼟 관찰한 뒤 isActive뼟 통해 푸쉬시키는 네비게이션 링큏

소스 코드

import SwiftUI
import Firebase

@main
struct TwitterCloneApp: App {
    @StateObject private var viewModel = AuthViewModel()
    init() {
        FirebaseApp.configure()
    }
    
    var body: some Scene {
        WindowGroup {
            NavigationView {
                ContentView()
            }
            .navigationViewStyle(.stack)
            .environmentObject(viewModel)
        }
    }
}
  • 전역으로 관리하기 위해 @StateObject로 해당 ObservableObject 선언
  • environmentObject뼟 통해 해당 클래스를 파라미터로 넘겨주기
import SwiftUI

struct ContentView: View {
    @EnvironmentObject var viewModel: AuthViewModel
    @State private var showMenu: Bool = false
    var body: some View {
        Group {
            // if no user logged in
            if viewModel.userSession == nil {
                LoginView()
            } else {
            // if user logged in
                mainInterfaceView
            }
        }
    }
}
  • 롰 모델의 userSession 값은 로그인 등 파이어베이스 함수 사용 뒤 값 변화를 감지 가능
import SwiftUI

struct RegistrationView: View {
    @EnvironmentObject var viewModel: AuthViewModel
    @State private var email: String = ""
    @State private var userName: String = ""
    @State private var fullName: String = ""
    @State private var password: String = ""
    @Environment(\.presentationMode) var mode
    var body: some View {
        ZStack {
            VStack {
                AuthenticationHeaderView(title: "Get Started.\nCreate your account")
                VStack(spacing: 40) {
                    CustomInputField(imageName: "envelope", placeholderText: "Email", isSecureField: false, text: $email)
                    CustomInputField(imageName: "person", placeholderText: "UserName", isSecureField:  false,text: $userName)
                    CustomInputField(imageName: "person", placeholderText: "Full Name", isSecureField:  false,text: $fullName)
                    CustomInputField(imageName: "lock", placeholderText: "Password", isSecureField: true, text: $password)
                }
                .padding(32)
                
                Button {
                    viewModel.register(email: email, password: password, userName: userName, fullName: fullName)
                    print("Button clicked from register view")
                } label: {
                    Text("Sign Up")
                        .font(.headline)
                        .foregroundColor(.white)
                        .frame(width: 340, height: 50)
                        .background(Color(.systemBlue))
                        .clipShape(Capsule())
                        .padding()
                }
                .shadow(color: .gray.opacity(0.5), radius: 10, x: 0, y: 0)
                
                Spacer()
                
                Button {
                    mode.wrappedValue.dismiss()
                } label: {
                    HStack {
                        Text("Already have an account?")
                            .font(.footnote)
                        Text("Sign In")
                            .font(.footnote)
                            .fontWeight(.semibold)
                    }
                }
                .padding(.bottom, 32)
                
            }
            .ignoresSafeArea()
            NavigationLink(destination: ProfilePhotoSelectorView(), isActive: $viewModel.didAuthenticateUser) {
                EmptyView()
            }
        }
    }
}
  • 파이어베이스 회원가입 함수 완료 확인 이후 네비게이션 링큏 활성화
import SwiftUI

struct ProfilePhotoSelectorView: View {
    var body: some View {
        VStack {
            AuthenticationHeaderView(title: "Create your account\nAdd a profile photo")
            Button {
                print("Pick Image here...")
            } label: {
                Image("tweet_plus")
                    .resizable()
                    .scaledToFit()
                    .frame(width: 180, height: 180)
                    .padding(.top, 44)
            }

            Spacer()
        }
        .ignoresSafeArea()
    }
}
  • 사진 입력 이후 해당 데이터 정보를 파이어베이스 스토리지에 저장, 해당 URL 정보를 파이어스토어에 저장하기 위한 롰
import SwiftUI
import FirebaseAuth
import FirebaseFirestore

class AuthViewModel: ObservableObject {
    @Published var userSession: FirebaseAuth.User?
    @Published var didAuthenticateUser = false
    init() {
        self.userSession =  Auth.auth().currentUser
    }
    
    func login(email: String, password: String) {
        Auth.auth().signIn(withEmail: email, password: password) { [weak self] userResult, error in
            guard
                let userResult = userResult,
                error == nil else { return }
            self?.userSession = userResult.user
            print("Firebase Login Did Succeed")
        }
    }
    
    func register(email: String, password: String, userName: String, fullName: String) {
        Auth.auth().createUser(withEmail: email, password: password) { [weak self] userResult, error in
            guard
                let userResult = userResult,
                error == nil else { return }
            print("Firebase Register Did Succeed")
            
            let data = ["email": email, "userName": userName.lowercased(), "fullName": fullName, "uid": userResult.user.uid]
            Firestore.firestore().collection("users")
                .document(userResult.user.uid)
                .setData(data) { [weak self] error in
                    if let error = error {
                        print(error.localizedDescription)
                    } else {
                        self?.didAuthenticateUser = true
                        print("Firestore Register Did Succeed")
                    }
                }
        }
    }
    
    func signOut() {
        do {
            try Auth.auth().signOut()
            userSession = nil
            didAuthenticateUser = false
            print("Firebase SignOut Did Succeed")
        } catch {
            print(error.localizedDescription)
        }
    }
}
  • ObservableObject 프로토콜을 준수하는 클래스로 @Pbulished로 된 값의 변화를 감지해야 할 때 퍼블리셔로 죟기

구현 화면

profile
JUST DO IT

0개의 댓글