Skip to content
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
22 changes: 8 additions & 14 deletions Projects/CommonUI/Sources/View/Login/LoginView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import RxSwift
import Then
import SafariServices
import RxRelay
import AuthenticationServices

open class LoginView: UIView, SFSafariViewControllerDelegate {
let logoLabel = UILabel().then {
Expand All @@ -26,9 +27,10 @@ open class LoginView: UIView, SFSafariViewControllerDelegate {
}

var googleLoginButton = UIButton()
var appleLoginButton = UIButton()
var appleLoginButton = ASAuthorizationAppleIDButton(type: .default, style: .black)

public let googleLoginTapped = PublishRelay<Void>()
public let appleLoginTapped = PublishRelay<Void>()

let disposeBag = DisposeBag()

Expand All @@ -46,6 +48,10 @@ open class LoginView: UIView, SFSafariViewControllerDelegate {
googleLoginButton.rx.tap
.bind(to: googleLoginTapped)
.disposed(by: disposeBag)

appleLoginButton.rx.controlEvent(.touchUpInside)
.bind(to: appleLoginTapped)
.disposed(by: disposeBag)
}

func initAttribute() {
Expand All @@ -59,19 +65,7 @@ open class LoginView: UIView, SFSafariViewControllerDelegate {
$0.backgroundColor = .white
$0.layer.borderColor = CommonUIAssets.LMGray3?.cgColor
$0.layer.borderWidth = 1
$0.layer.cornerRadius = 12
$0.titleLabel?.font = UIFont.systemFont(ofSize: 15)
$0.semanticContentAttribute = .forceLeftToRight
$0.imageEdgeInsets = UIEdgeInsets(top: 0, left: -8, bottom: 0, right: 8)
}

appleLoginButton = appleLoginButton.then {
$0.setTitle("Apple๋กœ ์‹œ์ž‘ํ•˜๊ธฐ", for: .normal)
$0.setTitleColor(.white, for: .normal)
$0.setImage(CommonUIAssets.apple?
.resize(to: CGSize(width: 25, height: 25)), for: .normal)
$0.backgroundColor = .black
$0.layer.cornerRadius = 12
$0.layer.cornerRadius = 5
$0.titleLabel?.font = UIFont.systemFont(ofSize: 15)
$0.semanticContentAttribute = .forceLeftToRight
$0.imageEdgeInsets = UIEdgeInsets(top: 0, left: -8, bottom: 0, right: 8)
Expand Down
64 changes: 55 additions & 9 deletions Projects/Data/Sources/Repository/LoginRepository.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,32 +8,78 @@
import Domain
import RxSwift
import Alamofire
import Foundation

public class DefaultLoginRepository: LoginRepository {
public init() {}

public func postGoogleLogin() -> Single<LoginVO> {
return request(
endpoint: NetworkConfiguration.baseUrl,
id: 4,
endpoint: "/oauth2/authorization/google",
responseType: LoginDTO.self
)
.map { dto in
return LoginVO(accessToken: "")
}
}

private func request<T: Decodable>(endpoint: String, id: Int, responseType: T.Type) -> Single<T> {
public func postAppleLogin(userName: String?, identityToken: String) -> Single<LoginVO> {
let params: Parameters = [
"userName": userName,
"identityToken": identityToken
]

return Single.create { single in
let url = "\(NetworkConfiguration.baseUrl)\(endpoint)"
let parameters: Parameters = [
"id": id
]
let url = "\(NetworkConfiguration.baseUrl)/api/auth/apple/login"
let request = AF.request(url,
method: .post,
parameters: params,
encoding: JSONEncoding.default,
headers: nil)
.redirect(using: Redirector(behavior: .doNotFollow))
.response { response in
if let error = response.error {
single(.failure(error))
return
}

guard let httpResponse = response.response else {
single(.failure(AFError.responseValidationFailed(reason: .dataFileNil)))
return
}

if let location = httpResponse.allHeaderFields["Location"] as? String,
let components = URLComponents(string: location) {
let items = components.queryItems ?? []
let accessToken = items.first(where: { $0.name == "accessToken" })?.value
single(.success(LoginVO(accessToken: accessToken)))
} else {
let error = NSError(domain: "DefaultLoginRepository",
code: -1,
userInfo: [NSLocalizedDescriptionKey: "Missing Location header or invalid redirect URL"])
single(.failure(error))
}
}

return Disposables.create { request.cancel() }
}
}

private func request<T: Decodable>(
endpoint: String,
method: HTTPMethod = .post,
parameters: Parameters? = nil,
encoding: ParameterEncoding = URLEncoding.queryString,
headers: HTTPHeaders? = nil,
responseType: T.Type
) -> Single<T> {
return Single.create { single in
let url = "\(NetworkConfiguration.baseUrl)\(endpoint)"
let request = AF.request(url,
method: .get,
method: method,
parameters: parameters,
encoding: URLEncoding.queryString)
encoding: encoding,
headers: headers)
.validate()
.responseDecodable(of: responseType) { response in
switch response.result {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ import RxSwift

public protocol LoginRepository {
func postGoogleLogin() -> Single<LoginVO>
func postAppleLogin(userName: String?, identityToken: String) -> Single<LoginVO>
}
5 changes: 5 additions & 0 deletions Projects/Domain/Sources/UseCase/LoginUseCase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import RxSwift

public protocol LoginUseCase {
func postGoogleLogin() -> Single<LoginVO>
func postAppleLogin(userName: String?, identityToken: String) -> Single<LoginVO>
}

public final class DefaultLoginUseCase: LoginUseCase {
Expand All @@ -21,4 +22,8 @@ public final class DefaultLoginUseCase: LoginUseCase {
public func postGoogleLogin() -> Single<LoginVO> {
return repository.postGoogleLogin()
}

public func postAppleLogin(userName: String?, identityToken: String) -> Single<LoginVO> {
return repository.postAppleLogin(userName: userName, identityToken: identityToken)
}
}
40 changes: 11 additions & 29 deletions Projects/LearnMate/LearnMate.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 55;
objectVersion = 56;
objects = {

/* Begin PBXBuildFile section */
Expand Down Expand Up @@ -109,6 +109,7 @@
7C422FE2DDF9127C2F4B2DF0 /* RxSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; };
89F58ED76FC4C0973305766F /* DependencyInjector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DependencyInjector.swift; sourceTree = "<group>"; };
8A00D2BDD6B9A0B6E62CF3CC /* LearnMateTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = LearnMateTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
950A0D4D2E56D23300C07CF2 /* LearnMate.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = LearnMate.entitlements; path = LearnMate/LearnMate.entitlements; sourceTree = "<group>"; };
9ACFF0B3105009DD7646173E /* Domain.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Domain.framework; sourceTree = BUILT_PRODUCTS_DIR; };
9AEA88AC00680DC27F375FB6 /* HomeAssembly.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeAssembly.swift; sourceTree = "<group>"; };
A4DC536F09A4A8284AA20CE7 /* CommonUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CommonUI.framework; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -198,6 +199,7 @@
E290CD2FA6D313265F045E73 = {
isa = PBXGroup;
children = (
950A0D4D2E56D23300C07CF2 /* LearnMate.entitlements */,
B074F9F8E730729626040CA5 /* Project */,
ED0EE4EA4327EC4E4D07675E /* Products */,
);
Expand Down Expand Up @@ -378,6 +380,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = LearnMate/LearnMate.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
ENABLE_PREVIEWS = YES;
HEADER_SEARCH_PATHS = (
Expand All @@ -393,11 +396,7 @@
"$(inherited)",
"-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap",
);
OTHER_SWIFT_FLAGS = (
"$(inherited)",
"-Xcc",
"-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap",
);
OTHER_SWIFT_FLAGS = "$(inherited) -Xcc -fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap";
PRODUCT_BUNDLE_IDENTIFIER = io.tuist.LearnMate;
PRODUCT_NAME = LearnMate;
SDKROOT = iphoneos;
Expand All @@ -416,6 +415,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = LearnMate/LearnMate.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
ENABLE_PREVIEWS = YES;
HEADER_SEARCH_PATHS = (
Expand All @@ -431,21 +431,14 @@
"$(inherited)",
"-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap",
);
OTHER_SWIFT_FLAGS = (
"$(inherited)",
"-Xcc",
"-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap",
);
OTHER_SWIFT_FLAGS = "$(inherited) -Xcc -fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap";
PRODUCT_BUNDLE_IDENTIFIER = io.tuist.LearnMate;
PRODUCT_NAME = LearnMate;
SDKROOT = iphoneos;
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = (
"$(inherited)",
DEBUG,
);
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG";
SWIFT_COMPILATION_MODE = singlefile;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
Expand Down Expand Up @@ -531,21 +524,14 @@
"$(inherited)",
"-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap",
);
OTHER_SWIFT_FLAGS = (
"$(inherited)",
"-Xcc",
"-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap",
);
OTHER_SWIFT_FLAGS = "$(inherited) -Xcc -fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap";
PRODUCT_BUNDLE_IDENTIFIER = io.tuist.LearnMateTests;
PRODUCT_NAME = LearnMateTests;
SDKROOT = iphoneos;
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = (
"$(inherited)",
DEBUG,
);
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG";
SWIFT_COMPILATION_MODE = singlefile;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
Expand Down Expand Up @@ -574,11 +560,7 @@
"$(inherited)",
"-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap",
);
OTHER_SWIFT_FLAGS = (
"$(inherited)",
"-Xcc",
"-fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap",
);
OTHER_SWIFT_FLAGS = "$(inherited) -Xcc -fmodule-map-file=$(SRCROOT)/../../Tuist/.build/tuist-derived/RxCocoaRuntime/RxCocoaRuntime.modulemap";
PRODUCT_BUNDLE_IDENTIFIER = io.tuist.LearnMateTests;
PRODUCT_NAME = LearnMateTests;
SDKROOT = iphoneos;
Expand Down
10 changes: 10 additions & 0 deletions Projects/LearnMate/LearnMate/LearnMate.entitlements
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.developer.applesignin</key>
<array>
<string>Default</string>
</array>
</dict>
</plist>
53 changes: 51 additions & 2 deletions Projects/Login/Sources/View/LoginViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ import UIKit
import SnapKit
import RxSwift
import SafariServices
import AuthenticationServices

public class LoginViewController: BaseViewController, SFSafariViewControllerDelegate {
public class LoginViewController: BaseViewController, SFSafariViewControllerDelegate, ASAuthorizationControllerDelegate, ASAuthorizationControllerPresentationContextProviding {
let viewModel: LoginViewModel
let loginView = LoginView()

Expand Down Expand Up @@ -45,13 +46,61 @@ public class LoginViewController: BaseViewController, SFSafariViewControllerDele
self?.presentGoogleLogin()
}
.disposed(by: disposeBag)

loginView.appleLoginTapped
.bind { [weak self] in
self?.presentAppleLogin()
}
.disposed(by: disposeBag)
}

private func presentGoogleLogin() {
guard let url = URL(string: "https://dev-learnmate.store/oauth2/authorization/google") else { return }
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}


private func presentAppleLogin() {
let request = ASAuthorizationAppleIDProvider().createRequest()
request.requestedScopes = [.fullName, .email]

let authorizationController = ASAuthorizationController(authorizationRequests: [request])
authorizationController.delegate = self
authorizationController.presentationContextProvider = self
authorizationController.performRequests()
}

// MARK: - ASAuthorizationControllerDelegate
public func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
if let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential {
let userID = appleIDCredential.user
let email = appleIDCredential.email
let givenName = appleIDCredential.fullName?.givenName ?? ""
let familyName = appleIDCredential.fullName?.familyName ?? ""
let userName = givenName + familyName

if let tokenData = appleIDCredential.identityToken,
let tokenString = String(data: tokenData, encoding: .utf8) {
print("1๏ธโƒฃ Identity Token: \(tokenString)")
viewModel.postAppleLogin(userName: userName, identityToken: tokenString)
} else {
print("Failed to decode identity token")
}

print("2๏ธโƒฃ UserID: \(userID)")
print("3๏ธโƒฃ Email: \(email ?? "Not provided")")
print("4๏ธโƒฃ User Name: \(userName)")
}
}

public func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) {
print("Apple Login Error: \(error.localizedDescription)")
}

// MARK: - ASAuthorizationControllerPresentationContextProviding
public func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
return view.window!
}

private func bindTransition() {
// homeQuizView.onStartButtonTapped = { [weak self] indexPath in
// let quizViewController = QuizViewController()
Expand Down
15 changes: 12 additions & 3 deletions Projects/Login/Sources/ViewModel/LoginViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,31 @@ import RxSwift

protocol LoginViewModelProtocol {
func postGoogleLogin()
func postAppleLogin(userName: String?, identityToken: String)
}

public class LoginViewModel: LoginViewModelProtocol {
private let disposeBag = DisposeBag()
private let loginUseCase: LoginUseCase
public init(loginUseCase: LoginUseCase) {
self.loginUseCase = loginUseCase
postGoogleLogin()
}

func postGoogleLogin() {
loginUseCase.postGoogleLogin()
.subscribe(onSuccess: { response in
print(response)
}, onFailure: { _ in


}).disposed(by: disposeBag)
}

func postAppleLogin(userName: String?, identityToken: String) {
loginUseCase.postAppleLogin(userName: userName, identityToken: identityToken)
.subscribe(onSuccess: { response in
print("Apple Login Response: \(response)")
}, onFailure: { error in
print("Apple Login Error: \(error)")
}).disposed(by: disposeBag)
}
}
Loading