본문은 야곰 아카데미 커리어 스타터 캠프를 통해 학습한 내용을 회고한 글입니다.
실험 1: 각 View 역할 이해 및 UserDefault를 활용한 Password의 저장
import UIKit
class LogInViewController: UIViewController {
@IBOutlet weak var pwTextField: UITextField!
var diaryViewController: DiaryViewController?
override func viewDidLoad() {
super.viewDidLoad()
diaryViewController = self.storyboard?.instantiateViewController(withIdentifier: "diary") as? DiaryViewController
}
@IBAction func tapLogInButton(_ sender: Any) {
guard let diaryViewController = diaryViewController else { return }
let storedPW = UserDefaults.standard.string(forKey: "passwordKey")
guard pwTextField.text == storedPW else {
let alert = UIAlertController(title: "비밀번호가 일치하지 않습니다", message: "등록된 비밀번호와 입력한 비밀번호가 일치하지 않습니다", preferredStyle: .alert)
let confirm = UIAlertAction(title: "돌아가기", style: .default)
alert.addAction(confirm)
present(alert, animated: true, completion: nil)
return
}
diaryViewController.modalPresentationStyle = .fullScreen
present(diaryViewController, animated: true)
}
@IBAction func addNewPassword(_ sender: Any) {
let passwordRegex = "^(?=.*[A-Za-z])(?=.*[0-9])(?=.*[!@#$%^&*()_+=-]).{8,16}"
let validPw = pwTextField.text?.range(of: passwordRegex, options: .regularExpression) != nil
if validPw {
guard let newPassword = pwTextField.text, !newPassword.isEmpty else { return }
UserDefaults.standard.set(newPassword, forKey: "passwordKey")
let alert = UIAlertController(title: "성공", message: "비밀번호가 성공적으로 등록되었습니다.", preferredStyle: .alert)
let confirm = UIAlertAction(title: "확인", style: .default)
alert.addAction(confirm)
present(alert, animated: true, completion: nil)
} else {
let alert = UIAlertController(title: "비밀번호 오류", message: "영어 대소문자, 숫자, 특수기호를 넣어 8자리 이상 16자리 이하 비밀번호를 만들어주세요.", preferredStyle: .alert)
let confirm = UIAlertAction(title: "확인", style: .default)
alert.addAction(confirm)
present(alert, animated: true, completion: nil)
}
}
}
실험 2: KeyChain을 활용해 비밀번호를 저장하고, 검증하기
KeyChainManager:
import Foundation
final class KeychainManager {
static let shared = KeychainManager()
private init() {}
func createKeychain(credentials: Credentials) throws {
let account = credentials.userName
let password = credentials.password.data(using: String.Encoding.utf8)!
let query: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: account,
kSecValueData as String: password]
let status = SecItemAdd(query as CFDictionary, nil)
guard status != errSecDuplicateItem else {
throw KeychainError.duplicateItem
}
guard status == errSecSuccess else {
throw KeychainError.unhandledError(status: status)
}
}
func readKeyChain(account: String) throws -> Credentials {
let query: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
kSecMatchLimit as String: kSecMatchLimitOne,
kSecAttrAccount as String: account,
kSecReturnAttributes as String: true,
kSecReturnData as String: true]
var item: CFTypeRef?
let status = SecItemCopyMatching(query as CFDictionary, &item)
guard status != errSecItemNotFound else {
throw KeychainError.noPassword
}
guard status == errSecSuccess else {
throw KeychainError.unhandledError(status: status)
}
guard let existingItem = item as? [String: Any],
let passwordData = existingItem[kSecValueData as String] as? Data,
let password = String(data: passwordData, encoding: String.Encoding.utf8),
let account = existingItem[kSecAttrAccount as String] as? String else {
throw KeychainError.unexpectedPasswordData
}
let credentials = Credentials(userName: account, password: password)
return credentials
}
func updateKeyChain(credentials: Credentials) throws {
let account = credentials.userName
let password = credentials.password.data(using: String.Encoding.utf8)!
let query: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: account]
let attributes: [String: Any] = [kSecValueData as String: password]
let status = SecItemUpdate(query as CFDictionary, attributes as CFDictionary)
guard status != errSecDuplicateItem else {
throw KeychainError.duplicateItem
}
guard status != errSecItemNotFound else {
throw KeychainError.noPassword
}
guard status == errSecSuccess else {
throw KeychainError.unhandledError(status: status)
}
}
}
LogInViewController:
import UIKit
class LogInViewController: UIViewController {
let account = "Rhode"
let keyChainManager = KeychainManager.shared
let alertManager = AlertManager.shared
@IBOutlet weak var pwTextField: UITextField!
var diaryViewController: DiaryViewController?
var changePasswordViewController: ChangePasswordViewController?
override func viewDidLoad() {
super.viewDidLoad()
diaryViewController = self.storyboard?.instantiateViewController(withIdentifier: "diary") as? DiaryViewController
changePasswordViewController = self.storyboard?.instantiateViewController(identifier: "newPW") as? ChangePasswordViewController
}
@IBAction func tapLogInButton(_ sender: Any) {
do {
let loaded = try keyChainManager.readKeyChain(account: account)
guard loaded.password == pwTextField.text else {
alertManager.presentAlert(viewController: self, title: "비밀번호 불일치", message: "등록된 비밀번호와 입력한 비밀번호가 일치하지 않습니다.")
return
}
} catch {
alertManager.presentAlert(viewController: self, title: "에러", message: "\(error)")
}
guard let diaryViewController = diaryViewController else { return }
diaryViewController.modalPresentationStyle = .fullScreen
present(diaryViewController, animated: true)
}
@IBAction func addNewPassword(_ sender: Any) {
let passwordRegex = "^(?=.*[A-Za-z])(?=.*[0-9])(?=.*[!@#$%^&*()_+=-]).{8,16}"
let validPw = pwTextField.text?.range(of: passwordRegex, options: .regularExpression) != nil
if validPw {
guard let newPassword = pwTextField.text, !newPassword.isEmpty else {
alertManager.presentAlert(viewController: self, title: "비밀번호 입력 요망", message: "비밀번호를 입력해주세요.")
return
}
let credentials = Credentials(userName: account, password: newPassword)
do {
try keyChainManager.createKeychain(credentials: credentials)
alertManager.presentAlert(viewController: self, title: "성공", message: "비밀번호가 성공적으로 등록되었습니다.")
} catch KeychainError.duplicateItem {
alertManager.presentAlert(viewController: self, title: "중복된 계정", message: "이미 계정에 비밀번호가 있습니다.")
} catch {
alertManager.presentAlert(viewController: self, title: "에러", message: "\(error)")
}
} else {
alertManager.presentAlert(viewController: self, title: "비밀번호 오류", message: "영어 대소문자, 숫자, 특수기호를 넣어 8자리 이상 16자리 이하 비밀번호를 만들어주세요.")
return
}
}
@IBAction func tapChangePasswordButton(_ sender: UIButton) {
guard let changePasswordViewController = changePasswordViewController else { return }
changePasswordViewController.modalPresentationStyle = .popover
present(changePasswordViewController, animated: true)
}
}
실험 3: 비밀번호를 대조하여 새로운 비밀번호로 업데이트 하기
ChangePasswordViewController:
import UIKit
class ChangePasswordViewController: UIViewController {
let account = "Rhode"
let keyChainManager = KeychainManager.shared
let alertManager = AlertManager.shared
@IBOutlet weak var currentPWTextField: UITextField!
@IBOutlet weak var newPWTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func tapChangePasswordButton(_ sender: Any) {
do {
let loaded = try keyChainManager.readKeyChain(account: account)
guard loaded.password == currentPWTextField.text else {
alertManager.presentAlert(viewController: self, title: "비밀번호 불일치", message: "등록된 비밀번호와 입력한 비밀번호가 일치하지 않습니다.")
return
}
} catch {
alertManager.presentAlert(viewController: self, title: "에러", message: "\(error)")
}
do {
let passwordRegex = "^(?=.*[A-Za-z])(?=.*[0-9])(?=.*[!@#$%^&*()_+=-]).{8,16}"
let validPw = newPWTextField.text?.range(of: passwordRegex, options: .regularExpression) != nil
if validPw {
guard let newPassword = newPWTextField.text, !newPassword.isEmpty else {
alertManager.presentAlert(viewController: self, title: "비밀번호 입력 요망", message: "비밀번호를 입력해주세요.")
return
}
let credentials = Credentials(userName: account, password: newPassword)
try keyChainManager.updateKeyChain(credentials: credentials)
alertManager.presentAlert(viewController: self, title: "비밀번호 변경 성공", message: "비밀번호가 변경되었습니다.") {
self.dismiss(animated: true)
}
} else {
alertManager.presentAlert(viewController: self, title: "비밀번호 오류", message: "영어 대소문자, 숫자, 특수기호를 넣어 8자리 이상 16자리 이하 비밀번호를 만들어주세요.")
}
} catch {
alertManager.presentAlert(viewController: self, title: "에러", message: "\(error)")
}
}
}
'YAGOM CAREER STARTER' 카테고리의 다른 글
[TIL] 20230508: Mirror (0) | 2023.05.09 |
---|---|
[TIL] 20230509: Core Location, Getting the current location of a device (0) | 2023.05.09 |
[TIL] 20230411: URL Loading System (0) | 2023.04.14 |
[TIL] 20230404: URLSession (0) | 2023.04.05 |
[TIL] 20230330: Escaping closure/Defer (0) | 2023.04.04 |