개발
iOS Combine으로 로그인 로직 만들기
한번사는인생~키야
2024. 7. 10. 12:29
728x90
iOS Combine을 사용한 로그인 로직 구현
Combine 프레임워크를 사용하여 iOS 앱에서 효율적이고 반응형 로그인 로직을 구현하는 방법을 살펴보겠습니다.
1. 로그인 양식 UI 구성
- 이메일 및 비밀번호 입력 필드를 포함하는 UI를 구성합니다.
- 각 필드에 대한 입력 유효성 검사 로직을 구현합니다.
2. Combine을 사용한 입력 스트림 처리
- 각 입력 필드에서 CurrentValueSubject를 사용하여 입력 값을 Observable로 변환합니다.
- map연산자를 사용하여 입력 값을 필요한 형식으로 변환합니다.
- validate()함수를 사용하여 입력 값의 유효성을 검사하고 오류 메시지를 생성합니다.
3. Combine을 사용한 로그인 로직 구현
- combineLatest 연산자를 사용하여 이메일 및 비밀번호 Observable을 결합합니다.
- flatMap 연산자를 사용하여 로그인 API 호출을 수행합니다.
- decode연산자를 사용하여 API 응답 데이터를 디코딩합니다.
- subscribe메서드를 사용하여 응답 처리 및 결과 UI 업데이트를 수행합니다.
예시 코드
import SwiftUI
import Combine
struct LoginView: View {
@State private var email = ""
@State private var password = ""
@State private var errorMessage = ""
@State private var isLoading = false
@State private var isLoggedIn = false
private let disposeBag = DisposeBag()
var body: some View {
VStack {
TextField("이메일", text: $email)
.padding()
SecureField("비밀번호", text: $password)
.padding()
Button("로그인") {
isLoading = true
let input = Publishers.CombineLatest(
emailPublisher,
passwordPublisher
)
.map { email, password in
(email, password)
}
.validate()
.flatMap { userData in
loginAPI(userData: userData)
}
.decode(type: LoginResponse.self)
.map { response in
response.isSuccess
}
.subscribe(on: DispatchQueue.main) { [weak self] success in
guard let self = self else { return }
self->isLoading = false
if success {
self->isLoggedIn = true
} else {
self->errorMessage = "로그인에 실패했습니다."
}
}
}
.padding()
if isLoggedIn {
Text("로그인되었습니다.")
} else if !errorMessage.isEmpty {
Text(errorMessage)
.foregroundColor(.red)
}
if isLoading {
ProgressView()
}
}
.padding()
}
private var emailPublisher: AnyPublisher<String, Never> {
CurrentValueSubject(email).eraseToAnyPublisher()
}
private var passwordPublisher: AnyPublisher<String, Never> {
CurrentValueSubject(password).eraseToAnyPublisher()
}
}
private struct LoginResponse: Decodable {
let isSuccess: Bool
}
private func loginAPI(userData: (String, String)) -> AnyPublisher<LoginResponse, Error> {
// 실제 API 호출 코드를 작성합니다.
return Just((isSuccess: true)).setFailureType(to: Error.self).eraseToAnyPublisher()
}
private extension Publishers {
static func validate<T: Combine.CombinePublisher>(
_ publisher: T,
errorPrefix: String = "",
validations: [(_ value: T.Value) -> String?] = []
) -> AnyPublisher<T.Value, Error> {
publisher
.flatMap { value in
validations.reduce(Optional.none) { result, validation in
result.orElse(validation(value)) {
Error.init(message: errorPrefix + validation(value)!)
}
}
}
.eraseToAnyPublisher()
}
}
Combine의 장점:
- 비동기 작업 처리를 간편하게 수행할 수 있습니다.
- 코드가 명확하고 읽기 쉽습니다.
- 오류 처리가 용이합니다.