-
Notifications
You must be signed in to change notification settings - Fork 3
[Feat] #50 - 로그인・스플래쉬 뷰 UI 구성 및 API 연결, 자동 로그인 기능 구현 #53
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
Changes from all commits
d055194
097acc8
97692f8
589d062
b6fbd2e
cb2cc09
2e508a9
7c967cf
42b81f4
c06a803
73154da
4820f51
79f3db8
83f2df6
f454828
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| // | ||
| // C.swift | ||
| // RD-CoreTests | ||
| // | ||
| // Created by 정은희 on 2022/12/04. | ||
| // Copyright © 2022 RecorDream. All rights reserved. | ||
| // | ||
|
|
||
| public struct C { | ||
| public static let KAKAO_APP_KEY = "000e1e31f022f98fbe16c76ab287abd1" | ||
| public static let accessToken = "accessToken" | ||
| public static let refreshToken = "refreshToken" | ||
| public static let nickname = "nickname" | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| // | ||
| // Key.swift | ||
| // RD-Core | ||
| // | ||
| // Created by 정은희 on 2022/12/05. | ||
| // Copyright © 2022 RecorDream. All rights reserved. | ||
| // | ||
|
|
||
| public enum Key: String { | ||
| case platform = "key.platform" | ||
| case userToken = "key.userToken" | ||
| case accessToken = "key.accessToken" | ||
| case nickname = "key.nickname" | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| // | ||
| // UserDefaultManager.swift | ||
| // RD-Core | ||
| // | ||
| // Created by 정은희 on 2022/12/05. | ||
| // Copyright © 2022 RecorDream. All rights reserved. | ||
| // | ||
|
|
||
| import Foundation | ||
|
|
||
| public protocol UserDefaultManager { | ||
| static func set(value: Any, keyPath: Key.RawValue) | ||
| static func string(key: Key) -> String? | ||
| static func int(key: Key) -> Int? | ||
| static func remove(key: Key) | ||
| static func clearUserData() | ||
| } | ||
|
Comment on lines
+11
to
+17
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 저는 PropertyWrapper를 이용했었는데 이 방식도 좋네요!! |
||
|
|
||
| public class DefaultUserDefaultManager: UserDefaultManager { | ||
| public static func set(value: Any, keyPath: Key.RawValue) { | ||
| UserDefaults.standard.setValue(value, forKeyPath: keyPath) | ||
| } | ||
| public static func string(key: Key) -> String? { | ||
| return UserDefaults.standard.string(forKey: key.rawValue) | ||
| } | ||
| public static func int(key: Key) -> Int? { | ||
| return UserDefaults.standard.integer(forKey: key.rawValue) | ||
| } | ||
| public static func remove(key: Key) { | ||
| UserDefaults.standard.removeObject(forKey: key.rawValue) | ||
| } | ||
| public static func clearUserData() { | ||
| self.remove(key: .platform) | ||
| self.remove(key: .userToken) | ||
| self.remove(key: .accessToken) | ||
| self.remove(key: .nickname) | ||
|
Comment on lines
+33
to
+36
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. casIterable을 채택해서 allcases로 지워주는 방법도 있을 것 같습니다~~! 취향 차이인 것 같아여
Comment on lines
+19
to
+36
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 구웃ㅎㅎㅎ !! |
||
| } | ||
| } | ||
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| // | ||
| // DefaultAuthRepository.swift | ||
| // Data | ||
| // | ||
| // Created by 정은희 on 2022/12/04. | ||
| // Copyright © 2022 RecorDream. All rights reserved. | ||
| // | ||
|
|
||
| import Foundation | ||
|
|
||
| import Domain | ||
| import RD_Network | ||
|
|
||
| import RxSwift | ||
|
|
||
| public final class DefaultAuthRepository { | ||
|
|
||
| private let authService: AuthService | ||
| private let disposeBag = DisposeBag() | ||
|
|
||
| init(authService: AuthService) { | ||
| self.authService = authService | ||
| } | ||
| } | ||
|
|
||
| extension DefaultAuthRepository: AuthRepository { | ||
| public func requestAuth(request: AuthRequest) -> RxSwift.Observable<AuthEntity?> { | ||
| return Observable.create { observer in | ||
| self.authService.login(kakaoToken: request.kakaoToken, appleToken: request.appleToken, fcmToken: request.fcmToken) | ||
| .subscribe(onNext: { response in | ||
| guard let response = response else { return } | ||
| observer.onNext(.init(duplicated: response.duplicated, accessToken: response.accessToken, refreshToken: response.refreshToken)) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 여기서는 transform 파일에서 toDomain 메서드를 구현해줘도 좋을 것 같습니다!
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 반영하겠습니다! |
||
| }, onError: { err in | ||
| observer.onError(err) | ||
| }) | ||
| .disposed(by: self.disposeBag) | ||
| return Disposables.create() | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| // | ||
| // AuthEntity.swift | ||
| // Domain | ||
| // | ||
| // Created by 정은희 on 2022/12/04. | ||
| // Copyright © 2022 RecorDream. All rights reserved. | ||
| // | ||
|
|
||
| public struct AuthEntity: Codable { | ||
| public let duplicated: Bool | ||
| public let accessToken: String | ||
| public let refreshToken: String | ||
|
|
||
| public init(duplicated: Bool, accessToken: String, refreshToken: String) { | ||
| self.duplicated = duplicated | ||
| self.accessToken = accessToken | ||
| self.refreshToken = refreshToken | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| // | ||
| // AuthRequest.swift | ||
| // Domain | ||
| // | ||
| // Created by 정은희 on 2022/12/05. | ||
| // Copyright © 2022 RecorDream. All rights reserved. | ||
| // | ||
|
|
||
| import Foundation | ||
|
|
||
| public struct AuthRequest: Codable { | ||
| public var kakaoToken: String? = nil | ||
| public var appleToken: String? = nil | ||
| public let fcmToken: String | ||
|
|
||
| public init(kakaoToken: String?, appleToken: String?, fcmToken: String) { | ||
| self.kakaoToken = kakaoToken | ||
| self.appleToken = appleToken | ||
| self.fcmToken = fcmToken | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| // | ||
| // AuthRepository.swift | ||
| // Domain | ||
| // | ||
| // Created by 정은희 on 2022/12/04. | ||
| // Copyright © 2022 RecorDream. All rights reserved. | ||
| // | ||
|
|
||
| import RxSwift | ||
|
|
||
| public protocol AuthRepository { | ||
| func requestAuth(request: AuthRequest) -> Observable<AuthEntity?> | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| // | ||
| // AuthUseCase.swift | ||
| // Domain | ||
| // | ||
| // Created by 정은희 on 2022/12/05. | ||
| // Copyright © 2022 RecorDream. All rights reserved. | ||
| // | ||
|
|
||
| import Foundation | ||
|
|
||
| import RD_Core | ||
|
|
||
| import RxSwift | ||
|
|
||
| public protocol AuthUseCase { | ||
| func login(request: AuthRequest) | ||
|
|
||
| var authSuccess: PublishSubject<AuthEntity> { get set } | ||
| var authFail: PublishSubject<Error> { get set } | ||
| } | ||
|
|
||
| public final class DefaultAuthUseCase { | ||
| private let repository: AuthRepository | ||
| private let disposeBag = DisposeBag() | ||
|
|
||
| public var authSuccess = PublishSubject<AuthEntity>() | ||
| public var authFail = PublishSubject<Error>() | ||
|
|
||
| init(repository: AuthRepository) { | ||
| self.repository = repository | ||
| } | ||
| } | ||
|
|
||
| extension DefaultAuthUseCase: AuthUseCase { | ||
| public func login(request: AuthRequest) { | ||
| self.repository.requestAuth(request: request) | ||
| .filter { $0 != nil } | ||
| .subscribe(onNext: { [weak self] entity in | ||
| guard let self = self else { return } | ||
| guard let entity = entity else { | ||
| return } | ||
| DefaultUserDefaultManager.set(value: entity.accessToken, keyPath: C.accessToken) | ||
| DefaultUserDefaultManager.set(value: entity.refreshToken, keyPath: C.refreshToken) | ||
| self.authSuccess.onNext(entity) | ||
| }, onError: { err in | ||
| self.authFail.onNext(err) | ||
| }).disposed(by: disposeBag) | ||
|
Comment on lines
+37
to
+47
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 요 부분에서 필요하다면 response가 nil로 오는 경우에 대한 처리를 할 수도 있을 것 같네요! nil인 경우에 authFail에 넥스트 이벤트를 보내줄 수 있곘네요~~
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 반영하겠습니다! |
||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| { | ||
| "images" : [ | ||
| { | ||
| "filename" : "Kakaotalk.png", | ||
| "idiom" : "universal", | ||
| "scale" : "1x" | ||
| }, | ||
| { | ||
| "filename" : "[email protected]", | ||
| "idiom" : "universal", | ||
| "scale" : "2x" | ||
| }, | ||
| { | ||
| "filename" : "[email protected]", | ||
| "idiom" : "universal", | ||
| "scale" : "3x" | ||
| } | ||
| ], | ||
| "info" : { | ||
| "author" : "xcode", | ||
| "version" : 1 | ||
| }, | ||
| "properties" : { | ||
| "template-rendering-intent" : "original" | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| { | ||
| "images" : [ | ||
| { | ||
| "filename" : "apple.png", | ||
| "idiom" : "universal", | ||
| "scale" : "1x" | ||
| }, | ||
| { | ||
| "filename" : "[email protected]", | ||
| "idiom" : "universal", | ||
| "scale" : "2x" | ||
| }, | ||
| { | ||
| "filename" : "[email protected]", | ||
| "idiom" : "universal", | ||
| "scale" : "3x" | ||
| } | ||
| ], | ||
| "info" : { | ||
| "author" : "xcode", | ||
| "version" : 1 | ||
| }, | ||
| "properties" : { | ||
| "template-rendering-intent" : "original" | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| // | ||
| // RDLoginButton.swift | ||
| // RD-DSKit | ||
| // | ||
| // Created by 정은희 on 2022/12/01. | ||
| // Copyright © 2022 RecorDream. All rights reserved. | ||
| // | ||
|
|
||
| import UIKit | ||
|
|
||
| import SnapKit | ||
|
|
||
| public class RDLoginButton: UIButton { | ||
| public enum PlatformType: String { | ||
| case kakao = "kakao" | ||
| case apple = "apple" | ||
| } | ||
|
|
||
| public let iconImageView: UIImageView = { | ||
| let iv = UIImageView() | ||
| iv.image = UIImage() | ||
| iv.contentMode = .scaleAspectFit | ||
| return iv | ||
| }() | ||
|
|
||
| // MARK: - Initialization | ||
| public convenience init(platform: PlatformType, title: String) { | ||
| self.init(frame: .zero) | ||
|
|
||
| self.setupView(at: platform, for: title) | ||
| self.setupConstraint() | ||
| } | ||
| override init(frame: CGRect) { | ||
| super.init(frame: frame) | ||
| } | ||
| required init?(coder: NSCoder) { | ||
| fatalError("init(coder:) has not been implemented") | ||
| } | ||
| } | ||
|
|
||
| // MARK: - Extensions | ||
| extension RDLoginButton { | ||
| private func setupView(at platform: PlatformType, for title: String) { | ||
| switch platform { | ||
| case .kakao: | ||
| self.iconImageView.image = RDDSKitAsset.Images.kakaotalk.image | ||
| case .apple: | ||
| self.iconImageView.image = RDDSKitAsset.Images.apple.image | ||
| } | ||
|
|
||
| self.backgroundColor = .white.withAlphaComponent(0.05) | ||
| self.makeRoundedWithBorder(radius: 12, borderColor: UIColor(white: 1.0, alpha: 0.1).cgColor) | ||
| self.setTitle(title, for: .normal) | ||
| self.titleLabel?.font = RDDSKitFontFamily.Pretendard.regular.font(size: 14) | ||
| self.titleLabel?.textAlignment = .center | ||
| self.titleLabel?.textColor = .white | ||
|
|
||
| self.addSubview(iconImageView) | ||
| } | ||
| private func setupConstraint() { | ||
| self.iconImageView.snp.makeConstraints { make in | ||
| make.leading.equalToSuperview().offset(24) | ||
| make.centerY.equalToSuperview() | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| // | ||
| // AuthResponse.swift | ||
| // RD-Network | ||
| // | ||
| // Created by 정은희 on 2022/12/04. | ||
| // Copyright © 2022 RecorDream. All rights reserved. | ||
| // | ||
|
|
||
| import Foundation | ||
|
|
||
| public struct AuthResponse: Codable { | ||
| public let duplicated: Bool | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 혹시 duplicated는 어떤 정보를 나타내는 걸까요? 이미 로그인 중이라는 뜻인감..
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 현재 서버 쪽에서 하나의 API로 로그인과 회원가입을 동시에 수행하도록 구현되어 있는데요, (자세한 사항은 아래와 같음)
SplashVC TODO 주석 부분 구현할 때 해당 내용 추가하겠습니다!
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 적어놓으신 내용을 바탕으로 생각해봤는데, 저도 userDefault에 저장해둔 값으로 판단하기보다는 서버에서 넘어온 값으로 판단하는게 정확할 것 같다는 생각이 듭니다 ~~! 앱에 저장된 userDefault 값이 실수로 제거될 수도 있고.. ㅇ.ㅇ... 여러 오류의 가능성이 있을 것 같다는 생각?? 네이밍도 더 명확하게 변경하는게 좋을 것 같아요 ~~ |
||
| public let accessToken: String | ||
| public let refreshToken: String | ||
|
|
||
| enum CodingKeys: String, CodingKey { | ||
| case duplicated = "isAlreadyUser" | ||
| case accessToken, refreshToken | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
여기서는 축약어를 사용하기 보다는 AuthConstants 나 LoginConstants와 같이 어떤 정보를 상수로 묶어 두셨는지 네이밍해주시면 좋을 것 같습니다 !!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
반영하겠습니다!