기간

2022/05/02 → 2022/05/08

전공

소프트웨어학부 컴퓨터과학전공

학번

1815364

이름

@김도이


1) 사용자정보와 회원가입 및 로그인페이지

사용자정보와 고양이 정보 데이터형 선언

Firebase에 연동 전 로컬로 저장하기 위해 구조체를 선언.

같은 집사가족임을 묶기 위해 공동육묘코드 catFamilyCode 를 넣었는데 그렇게 맘에 드는 구조는 아니다. 기존에 SNS로그인과 이메일 로그인이 아닌, 휴대폰 연락처로 인증해서 연락처를 통해 한다거나... 이런 식으로 개선할 수도 있지 않을까 찾아보았지만 현재 메일 및 SNS 인증절차를 따로 넣지 않았기 때문에 일단은 가능한 영역에서 구현하는 것을 목표로 데이터를 짰다.

// 집사정보
struct UserInfo {
    let name: String
    let email: String
    let password: String
    let catFamilyCode: String
}

// 고양이가족
struct CatsFamilyInfo {
    let catFamilyCode: String // 주요키이자 외래키
    var cats: [CatsInfo] // 고양이가 추가될 수 있으므로
}

// 고양이 각자
struct CatsInfo {
    var ProfilePhoto: UIImage //프로필 이미지를 바꿀 수 있으니까
    let name: String
    let gender: Gender // 성별. 열거형으로
    var neutered: Bool // 중성화여부
    let birthday: Date // 생년월일
    var memo: String
}

enum Gender {
    case female
    case male
}

회원가입 뷰 컨트롤러 선언 및 정의

회원가입 화면에서 이름과 이메일, 비밀번호, 공동육묘 코드 유효성을 검사하고 이를 구조체로 저장하는 코드를 추가하였다. 또한 텍스트필드의 내용이 바뀌는 것을 감지하고, 유효성을 검사하여 모든 텍스트필드가 유효하게 채워져있을 경우 회원가입 버튼이 활성화되도록 하였다.

또한 뒤로가기 버튼을 추가한 후 PopViewController 를 이용하여 이전 화면으로 이동하게 만들었다.


//  RegisterViewController.swift

import UIKit

class RegisterViewController: UIViewController {

    // MARK: - Properties
    var email: String = ""
    var name: String = ""
    var password: String = ""
    var catFamilyCode: String = ""
    
    var userInfo: ((UserInfo) -> Void)?
    
    //  유효성 검사를 위한 프로퍼티
    var isValidEmail = false {
        didSet {
            self.validateUserInfo()
        }
    }

    var isValidName = false {
        didSet {
            self.validateUserInfo()
        }
    }
    
    var isValidPasssword = false  {
        didSet {
            self.validateUserInfo()
        }
    }
    
    var isValidCatFamilyCode = false  {
        didSet {
            self.validateUserInfo()
        }
    }
    
    // 텍스트필드 outlet
    @IBOutlet weak var nameTextField: UITextField!
    @IBOutlet weak var emailTextField: UITextField!
    @IBOutlet weak var passwordTextField: UITextField!
    
    @IBOutlet weak var passwordConfirmTextField: UITextField!
    @IBOutlet weak var catFamilyCodeTextField: UITextField!
    
    @IBOutlet weak var signupButton: UIButton!
    
    //  computed property로 처리하기
    var textFields: [UITextField] {
        [nameTextField, emailTextField, passwordTextField, catFamilyCodeTextField]
    }
    
    // MARK: - Lifecycle
    override func viewDidLoad() {
        super.viewDidLoad()
        setupTextField()
        
        // bug fix
        self.navigationController?.interactivePopGestureRecognizer?.delegate = nil

    }

    // MARK: - Action
    
    @objc
    func textFieldEditingChanged(_ sender: UITextField) {
        
        let text = sender.text ?? ""
        
        switch sender {
        case nameTextField:
            // 글자수만 2자 이상으로 확인
            self.isValidName = text.count > 2
            self.name = text
            
        case emailTextField:
            self.isValidEmail = text.isValidEmail()
            self.email = text
            
        case passwordTextField:
            self.isValidPasssword = text.isValidPassword()
            self.password = text
            
            
        case catFamilyCodeTextField:
            self.isValidCatFamilyCode = text.isValidCatFamilyCode()
            self.catFamilyCode = text
            
        default:
            fatalError("Missing TextField")
        }
    }
    
    
    @IBAction func backButtonDidTapped(_ sender: UIBarButtonItem) {
        
        // 뒤로가기는 popViewController
        self.navigationController?.popViewController(animated: true)
    }
    
    @IBAction func registerButtonDidTapped(_ sender: UIButton) {
        
        self.navigationController?.popViewController(animated: true)
        
        let userInfo = UserInfo(
            name: self.name,
            email: self.email,
            password: self.password,
            catFamilyCode: self.catFamilyCode
        )
        
        self.userInfo?(userInfo)
    }
    
    // MARK: - Helpers

    // 액션과 연결해주기위한 메소드
    private func setupTextField() {
        
        // addtarget은 해당 영역에서 이벤트가 처리되면 누가 처리할 것인가 하는 메서드
       
        textFields.forEach { tf in
            tf.addTarget(self, action: #selector(textFieldEditingChanged(_:)), for: .editingChanged)
        }
    }
    
    // 사용자가 입력한 회원정보를 확인하고 UI 업데이트(가입 버튼 색상 변경)
    private func validateUserInfo() {
        if isValidName
            && isValidEmail
            && isValidPasssword
            && isValidCatFamilyCode{
            self.signupButton.isEnabled = true
            UIView.animate(withDuration: 0.33) {
                self.signupButton.backgroundColor = UIColor.greengray900
            }
        } else {
            self.signupButton.isEnabled = false
            UIView.animate(withDuration: 0.33) {
                self.signupButton.backgroundColor = UIColor.gray300
            }
        }
        
    }
    
}

// 정규표현식
extension String {
    
    func isValidPassword() -> Bool {
        // 비밀번호: 대문자 , 소문자, 특수문자, 숫자 8자 이상일 때 True
        let passwordRegEx  = "^(?=.*[A-Z])(?=.*[a-z])(?=.*[\\\\d])(?=.*[~!@#\\\\$%\\\\^&\\\\*])[\\\\w~!@#\\\\$%\\\\^&\\\\*]{8,}$"

        let passwordValidation = NSPredicate.init(format: "SELF MATCHES %@", passwordRegEx)
        
        return passwordValidation.evaluate(with: self)
    }
    
    
    func isValidEmail() -> Bool {
        // 이메일: @포함 2글자 이상
        let emailRegEx = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\\\.[A-Za-z]{2,64}"
        let emailTest = NSPredicate(format:"SELF MATCHES %@", emailRegEx)
        
        return emailTest.evaluate(with: self)
    }
    
    func isValidCatFamilyCode() -> Bool {
        // 육묘초대코드: 영어대문자, 숫자 포함 5자
        let catFamilyCodeRegEx =
        "[A-Z-0-9]{5}"
        let catFamilyCodeValidation = NSPredicate.init(format: "SELF MATCHES %@", catFamilyCodeRegEx)
        
        return catFamilyCodeValidation.evaluate(with: self)
    }
    
}

로그인 뷰 컨트롤러