Skip to content

[IDLE-451] 아이디 비밀번호 입력검증 상태를 유저에게 즉시 알려주는 UI구현 #93

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Oct 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions project/Projects/Domain/DomainTests/RegisterValidationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,22 +71,22 @@ final class RegisterValidationTests: XCTestCase {
let usecase = DefaultAuthInputValidationUseCase()

// 유효한 비밀번호 테스트
XCTAssertTrue(usecase.checkPasswordIsValid(password: "Password1"))
XCTAssertTrue(usecase.checkPasswordIsValid(password: "pass1234"))
XCTAssertTrue(usecase.checkPasswordIsValid(password: "1234Abcd!"))
XCTAssertTrue(usecase.checkPasswordIsValid(password: "Valid123"))
XCTAssertTrue(usecase.checkPasswordIsValid(password: "StrongPass1!"))
XCTAssertTrue(usecase.checkPasswordIsValid(password: "Password1").isPasswordValid)
XCTAssertTrue(usecase.checkPasswordIsValid(password: "pass1234").isPasswordValid)
XCTAssertTrue(usecase.checkPasswordIsValid(password: "1234Abcd!").isPasswordValid)
XCTAssertTrue(usecase.checkPasswordIsValid(password: "Valid123").isPasswordValid)
XCTAssertTrue(usecase.checkPasswordIsValid(password: "StrongPass1!").isPasswordValid)
}

func testInvalidPassword() {

let usecase = DefaultAuthInputValidationUseCase()

// 유효하지 않은 비밀번호 테스트
XCTAssertFalse(usecase.checkPasswordIsValid(password: "short1")) // 너무 짧음
XCTAssertFalse(usecase.checkPasswordIsValid(password: "alllowercase")) // 숫자 없음
XCTAssertFalse(usecase.checkPasswordIsValid(password: "ALLUPPERCASE")) // 숫자 없음
XCTAssertFalse(usecase.checkPasswordIsValid(password: "12345678")) // 영문자 없음
XCTAssertFalse(usecase.checkPasswordIsValid(password: "123456789012345678901")) // 너무 길음
XCTAssertFalse(usecase.checkPasswordIsValid(password: "short1").isPasswordValid) // 너무 짧음
XCTAssertFalse(usecase.checkPasswordIsValid(password: "alllowercase").isPasswordValid) // 숫자 없음
XCTAssertFalse(usecase.checkPasswordIsValid(password: "ALLUPPERCASE").isPasswordValid) // 숫자 없음
XCTAssertFalse(usecase.checkPasswordIsValid(password: "12345678").isPasswordValid) // 영문자 없음
XCTAssertFalse(usecase.checkPasswordIsValid(password: "123456789012345678901").isValid) // 너무 길음
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,36 @@ public class DefaultAuthInputValidationUseCase: AuthInputValidationUseCase {
.requestCheckingIdDuplication(id: id)
}

public func checkPasswordIsValid(password: String) -> Bool {
let passwordLengthAndCharRegex = "^(?=.*[A-Za-z])(?=.*\\d)[A-Za-z\\d!@#$%^&*()_+=-]{8,20}$"
let predicate = NSPredicate(format: "SELF MATCHES %@", passwordLengthAndCharRegex)
public func checkPasswordIsValid(password: String) -> PasswordValidationState {

return predicate.evaluate(with: password)
// 1. 8자 ~ 20자 사이
let lengthRegex = "^.{8,20}$"
let lengthIsValid = evaluateStringWith(regex: lengthRegex, targetString: password)

// 2. 영문자와 숫자 반드시 하나씩 포함
let letterAndNumberRegex = "^(?=.*[A-Za-z])(?=.*[0-9]).*$"
let letterAndNumberIsValid = evaluateStringWith(regex: letterAndNumberRegex, targetString: password)

// 3. 공백 문자 사용 금지
let noWhitespaceRegex = "^\\S*$"
let noWhitespaceIsValid = evaluateStringWith(regex: noWhitespaceRegex, targetString: password)

// 4. 연속된 문자 3개 이상 사용 금지
let noTripleRepeatedCharsRegex = "^(?!.*(.)\\1{2,}).*$"
let noTripleRepeatedCharsIsValid = evaluateStringWith(regex: noTripleRepeatedCharsRegex, targetString: password)

return PasswordValidationState(
characterCount: lengthIsValid ? .valid : .invalid,
alphabetAndNumberIncluded: letterAndNumberIsValid ? .valid : .invalid,
noEmptySpace: noWhitespaceIsValid ? .valid : .invalid,
unsuccessiveSame3words: noTripleRepeatedCharsIsValid ? .valid : .invalid
)
}

private func evaluateStringWith(regex: String, targetString: String) -> Bool {

let predicate = NSPredicate(format: "SELF MATCHES %@", regex)

return predicate.evaluate(with: targetString)
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,90 @@
//
// PasswordValidationState.swift
// ConcreteUseCase
// Domain
//
// Created by choijunios on 7/7/24.
// Created by choijunios on 10/22/24.
//

import Foundation

public enum PasswordValidationState {
public class PasswordValidationState {

case invalidPassword
case unMatch
case match
public enum State {
case valid
case invalid
}

public let characterCount: State
public let alphabetAndNumberIncluded: State
public let noEmptySpace: State
public let unsuccessiveSame3words: State
public private(set) var isEditingAndCheckingPasswordsEqual: Bool = false

public init(
characterCount: State,
alphabetAndNumberIncluded: State,
noEmptySpace: State,
unsuccessiveSame3words: State
) {
self.characterCount = characterCount
self.alphabetAndNumberIncluded = alphabetAndNumberIncluded
self.noEmptySpace = noEmptySpace
self.unsuccessiveSame3words = unsuccessiveSame3words
}

public var isValid: Bool {

return (
isPasswordValid
&&
isEditingAndCheckingPasswordsEqual
)
}

public var isPasswordValid: Bool {
characterCount == .valid
&&
alphabetAndNumberIncluded == .valid
&&
noEmptySpace == .valid
&&
unsuccessiveSame3words == .valid
}

public func setEqualState(state: Bool) {
isEditingAndCheckingPasswordsEqual = state
}
}

public extension PasswordValidationState {

var description: String {
var descriptions: [String] = []

if characterCount == .valid {
descriptions.append("비밀번호 길이: 유효함 (8자 이상 20자 이하)")
} else {
descriptions.append("비밀번호 길이: 유효하지 않음 (8자 이상 20자 이하이어야 함)")
}

if alphabetAndNumberIncluded == .valid {
descriptions.append("영문자와 숫자: 유효함 (영문자와 숫자가 모두 포함됨)")
} else {
descriptions.append("영문자와 숫자: 유효하지 않음 (영문자와 숫자가 반드시 포함되어야 함)")
}

if noEmptySpace == .valid {
descriptions.append("공백 문자: 없음 (공백 문자를 사용할 수 없음)")
} else {
descriptions.append("공백 문자: 유효하지 않음 (공백 문자가 포함되어 있음)")
}

if unsuccessiveSame3words == .valid {
descriptions.append("연속된 문자 3개 이상 사용: 유효함 (연속된 동일 문자가 없음)")
} else {
descriptions.append("연속된 문자 3개 이상 사용: 유효하지 않음 (연속된 동일 문자가 3개 이상 포함됨)")
}

return descriptions.joined(separator: "\n")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,18 @@ public struct CenterJoinStatusInfoVO: Codable {
public let managerName: String
public let phoneNumber: String
public let centerManagerAccountStatus: CenterAccountStatus

public init(
id: String,
managerName: String,
phoneNumber: String,
centerManagerAccountStatus: CenterAccountStatus
) {
self.id = id
self.managerName = managerName
self.phoneNumber = phoneNumber
self.centerManagerAccountStatus = centerManagerAccountStatus
}
}

public enum CenterAccountStatus: String, Codable {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@ public protocol AuthInputValidationUseCase: BaseUseCase {
/// - parameters:
/// - password : "password1234"
/// - returns:
/// - Bool, true: 가능, flase: 불가능
func checkPasswordIsValid(password: String) -> Bool
/// - PasswordValidationState
func checkPasswordIsValid(password: String) -> PasswordValidationState

// #9.
/// 이름 유효성 확인 로직
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x4D",
"green" : "0xC3",
"red" : "0x2C"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x4D",
"green" : "0xC3",
"red" : "0x2C"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,68 @@

import UIKit

import AuthFeature
import BaseFeature
import Testing
import Core

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

var window: UIWindow?

var authCoordinator: AuthCoordinator?
var centerAccountRegisterCoordinator: CenterAccountRegisterCoordinator?
var centerLogInCoordinator: CenterLogInCoordinator?

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {

guard let windowScene = scene as? UIWindowScene else { return }


window = UIWindow(windowScene: windowScene)
DependencyInjector.shared.assemble(MockAssemblies)
DependencyInjector.shared.register(CenterRegisterLogger.self, CenterAuthLogger())

authCoordinator = .init()

authCoordinator?.startFlow = { [weak self] desination in

switch desination {
case .centerRegisterPage:

let coordinator = CenterAccountRegisterCoordinator()
self?.centerAccountRegisterCoordinator = coordinator
coordinator.start()

case .loginPage:
let coordinator = CenterLogInCoordinator()

coordinator.startFlow = { desination in
switch desination {
default:
// 센터 메인페이지로 이동
return
}
}

self?.centerLogInCoordinator = coordinator
coordinator.start()
default:
// 테스트시 추가가능
return
}
}

window = UIWindow(windowScene: windowScene)
window?.makeKeyAndVisible()

authCoordinator?.start()
}
}

class CenterAuthLogger: CenterRegisterLogger {

func logCenterRegisterStep(stepName: String, stepIndex: Int) { }

func startCenterRegister() { }

func logCenterRegisterDuration() { }
}
1 change: 1 addition & 0 deletions project/Projects/Presentation/Feature/Auth/Project.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ let project = Project(
resources: ["ExampleApp/Resources/**"],
dependencies: [
.target(name: "AuthFeature"),
D.Testing,
],
settings: .settings(
configurations: IdleConfiguration.presentationConfigurations
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "v_f_mark.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "v_s_mark.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,11 @@ public class CenterAccountRegisterCoordinator: Coordinator {
// 완료화면으로 이동
self?.router.presentAnonymousCompletePage(object)
}

vm.presentAlert = { [weak self] object in

self?.router.presentDefaultAlertController(object: object)
}

self.stageViewControllers = [
EnterNameViewController(viewModel: vm),
Expand Down
Loading
Loading