LINE 로그인 지원 추가
LINE 로그인 요청과 토큰 처리 흐름을 추가함
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"originHash" : "23fbb8fc47a95510f46840139efb1770d536256f32ac2ba9f74cf03ee90a3979",
|
"originHash" : "9f35428c4c178ca4a8bfa4b72544585a9e4d5b119825b423e1d2166cbe03fe37",
|
||||||
"pins" : [
|
"pins" : [
|
||||||
{
|
{
|
||||||
"identity" : "abseil-cpp-binary",
|
"identity" : "abseil-cpp-binary",
|
||||||
@@ -154,6 +154,15 @@
|
|||||||
"version" : "1.22.5"
|
"version" : "1.22.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"identity" : "line-sdk-ios-swift",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/line/line-sdk-ios-swift.git",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "51ef2ebefb05db8f748e80208b3281ca723abcdb",
|
||||||
|
"version" : "5.14.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"identity" : "moya",
|
"identity" : "moya",
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import FBSDKCoreKit
|
|||||||
import FirebaseCore
|
import FirebaseCore
|
||||||
import FirebaseAnalytics
|
import FirebaseAnalytics
|
||||||
import FirebaseMessaging
|
import FirebaseMessaging
|
||||||
|
import LineSDK
|
||||||
|
|
||||||
class AppDelegate: UIResponder, UIApplicationDelegate {
|
class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||||
|
|
||||||
@@ -24,6 +25,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||||||
|
|
||||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
|
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
|
||||||
FirebaseApp.configure()
|
FirebaseApp.configure()
|
||||||
|
LoginManager.shared.setup(channelID: LINE_CHANNEL_ID, universalLinkURL: nil)
|
||||||
Notifly.initialize(projectId: NOTIFLY_PROJECT_ID, username: NOTIFLY_USERNAME, password: NOTIFLY_PASSWORD)
|
Notifly.initialize(projectId: NOTIFLY_PROJECT_ID, username: NOTIFLY_USERNAME, password: NOTIFLY_PASSWORD)
|
||||||
Messaging.messaging().delegate = self
|
Messaging.messaging().delegate = self
|
||||||
setupAppsFlyer()
|
setupAppsFlyer()
|
||||||
@@ -75,6 +77,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([any UIUserActivityRestoring]?) -> Void) -> Bool {
|
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([any UIUserActivityRestoring]?) -> Void) -> Bool {
|
||||||
|
_ = LoginManager.shared.application(application, open: userActivity.webpageURL)
|
||||||
AppsFlyerLib.shared().continue(userActivity, restorationHandler: nil)
|
AppsFlyerLib.shared().continue(userActivity, restorationHandler: nil)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import AppsFlyerLib
|
|||||||
import GoogleSignIn
|
import GoogleSignIn
|
||||||
import KakaoSDKCommon
|
import KakaoSDKCommon
|
||||||
import KakaoSDKAuth
|
import KakaoSDKAuth
|
||||||
|
import LineSDK
|
||||||
|
|
||||||
@main
|
@main
|
||||||
struct SodaLiveApp: App {
|
struct SodaLiveApp: App {
|
||||||
@@ -83,6 +84,7 @@ struct SodaLiveApp: App {
|
|||||||
comps.path.lowercased() == "/result" {
|
comps.path.lowercased() == "/result" {
|
||||||
canPgPaymentViewModel.handleVerifyOpenURL(url)
|
canPgPaymentViewModel.handleVerifyOpenURL(url)
|
||||||
} else {
|
} else {
|
||||||
|
_ = LoginManager.shared.application(UIApplication.shared, open: url, options: [:])
|
||||||
ApplicationDelegate.shared.application(UIApplication.shared, open: url, options: [:])
|
ApplicationDelegate.shared.application(UIApplication.shared, open: url, options: [:])
|
||||||
AppsFlyerLib.shared().handleOpen(url)
|
AppsFlyerLib.shared().handleOpen(url)
|
||||||
GIDSignIn.sharedInstance.handle(url)
|
GIDSignIn.sharedInstance.handle(url)
|
||||||
|
|||||||
@@ -27,3 +27,5 @@ let GID_CLIENT_ID = "758414412471-3cf403jb4s405eu17qrfrcbs9ofhq369.apps.googleus
|
|||||||
let GID_SERVER_CLIENT_ID = "758414412471-mosodbj2chno7l1j0iihldh6edmk0gk9.apps.googleusercontent.com"
|
let GID_SERVER_CLIENT_ID = "758414412471-mosodbj2chno7l1j0iihldh6edmk0gk9.apps.googleusercontent.com"
|
||||||
|
|
||||||
let KAKAO_APP_KEY = "20cf19413d63bfdfd30e8e6dff933d33"
|
let KAKAO_APP_KEY = "20cf19413d63bfdfd30e8e6dff933d33"
|
||||||
|
|
||||||
|
let LINE_CHANNEL_ID = "2008995582"
|
||||||
|
|||||||
@@ -1153,6 +1153,32 @@ enum I18n {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum Line {
|
||||||
|
static var openFailed: String {
|
||||||
|
pick(
|
||||||
|
ko: "라인 로그인 화면을 열 수 없습니다.\n다시 시도해 주세요.",
|
||||||
|
en: "Unable to open LINE sign-in.\nPlease try again.",
|
||||||
|
ja: "LINEログイン画面を開けません。\nもう一度お試しください。"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
static var signInFailed: String {
|
||||||
|
pick(
|
||||||
|
ko: "라인 로그인에 실패했습니다.\n다시 시도해 주세요.",
|
||||||
|
en: "LINE sign-in failed.\nPlease try again.",
|
||||||
|
ja: "LINEログインに失敗しました。\nもう一度お試しください。"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
static var tokenMissing: String {
|
||||||
|
pick(
|
||||||
|
ko: "라인 인증 토큰을 가져오지 못했습니다.",
|
||||||
|
en: "Failed to retrieve LINE token.",
|
||||||
|
ja: "LINE認証トークンを取得できませんでした。"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 문자 메시지(Text Message) 관련 문자열
|
// 문자 메시지(Text Message) 관련 문자열
|
||||||
|
|||||||
@@ -150,6 +150,7 @@ struct LoginView: View {
|
|||||||
Image("ic_login_line")
|
Image("ic_login_line")
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
hideKeyboard()
|
hideKeyboard()
|
||||||
|
viewModel.loginWithLine()
|
||||||
}
|
}
|
||||||
|
|
||||||
Image("ic_login_x")
|
Image("ic_login_x")
|
||||||
|
|||||||
@@ -15,12 +15,14 @@ import UIKit
|
|||||||
import GoogleSignIn
|
import GoogleSignIn
|
||||||
import KakaoSDKUser
|
import KakaoSDKUser
|
||||||
import KakaoSDKAuth
|
import KakaoSDKAuth
|
||||||
|
import LineSDK
|
||||||
|
|
||||||
final class LoginViewModel: NSObject, ObservableObject {
|
final class LoginViewModel: NSObject, ObservableObject {
|
||||||
private let appViewModel = AppViewModel()
|
private let appViewModel = AppViewModel()
|
||||||
private let repository = UserRepository()
|
private let repository = UserRepository()
|
||||||
private var subscription = Set<AnyCancellable>()
|
private var subscription = Set<AnyCancellable>()
|
||||||
private var currentNonce: String?
|
private var currentNonce: String?
|
||||||
|
private var currentLineNonce: String?
|
||||||
|
|
||||||
@Published var email = ""
|
@Published var email = ""
|
||||||
@Published var password = ""
|
@Published var password = ""
|
||||||
@@ -128,6 +130,49 @@ final class LoginViewModel: NSObject, ObservableObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func loginWithLine() {
|
||||||
|
guard let presentingViewController = presentingViewController() else {
|
||||||
|
self.errorMessage = I18n.Login.Line.openFailed
|
||||||
|
self.isShowPopup = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let nonce = randomNonceString()
|
||||||
|
currentLineNonce = nonce
|
||||||
|
|
||||||
|
var parameters = LoginManager.Parameters()
|
||||||
|
parameters.IDTokenNonce = nonce
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
LoginManager.shared.login(permissions: [.profile, .openID], in: presentingViewController, parameters: parameters) { result in
|
||||||
|
switch result {
|
||||||
|
case .success(let loginResult):
|
||||||
|
guard let identityToken = loginResult.accessToken.IDTokenRaw, !identityToken.isEmpty else {
|
||||||
|
self.currentLineNonce = nil
|
||||||
|
self.errorMessage = I18n.Login.Line.tokenMissing
|
||||||
|
self.isShowPopup = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let lineNonce = self.currentLineNonce else {
|
||||||
|
self.errorMessage = I18n.Common.commonError
|
||||||
|
self.isShowPopup = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
self.currentLineNonce = nil
|
||||||
|
self.loginWithLine(identityToken: identityToken, nonce: lineNonce)
|
||||||
|
|
||||||
|
case .failure(let error):
|
||||||
|
self.currentLineNonce = nil
|
||||||
|
ERROR_LOG(error.localizedDescription)
|
||||||
|
self.errorMessage = I18n.Login.Line.signInFailed
|
||||||
|
self.isShowPopup = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private func loginWithApple(identityToken: String, nonce: String) {
|
private func loginWithApple(identityToken: String, nonce: String) {
|
||||||
let pushToken = UserDefaults.string(forKey: .pushToken)
|
let pushToken = UserDefaults.string(forKey: .pushToken)
|
||||||
let marketingPid = UserDefaults.string(forKey: .marketingPid)
|
let marketingPid = UserDefaults.string(forKey: .marketingPid)
|
||||||
@@ -209,6 +254,33 @@ final class LoginViewModel: NSObject, ObservableObject {
|
|||||||
.store(in: &subscription)
|
.store(in: &subscription)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func loginWithLine(identityToken: String, nonce: String) {
|
||||||
|
let pushToken = UserDefaults.string(forKey: .pushToken)
|
||||||
|
let marketingPid = UserDefaults.string(forKey: .marketingPid)
|
||||||
|
let request = SocialLoginRequest(
|
||||||
|
container: "ios",
|
||||||
|
pushToken: pushToken.isEmpty ? nil : pushToken,
|
||||||
|
marketingPid: marketingPid.isEmpty ? nil : marketingPid,
|
||||||
|
identityToken: identityToken,
|
||||||
|
nonce: nonce
|
||||||
|
)
|
||||||
|
|
||||||
|
isLoading = true
|
||||||
|
repository.loginLine(request: request)
|
||||||
|
.sink { result in
|
||||||
|
switch result {
|
||||||
|
case .finished:
|
||||||
|
DEBUG_LOG("finish")
|
||||||
|
case .failure(let error):
|
||||||
|
self.isLoading = false
|
||||||
|
ERROR_LOG(error.localizedDescription)
|
||||||
|
}
|
||||||
|
} receiveValue: { response in
|
||||||
|
self.handleLoginResponse(response)
|
||||||
|
}
|
||||||
|
.store(in: &subscription)
|
||||||
|
}
|
||||||
|
|
||||||
private func presentingViewController() -> UIViewController? {
|
private func presentingViewController() -> UIViewController? {
|
||||||
return UIApplication.shared.connectedScenes
|
return UIApplication.shared.connectedScenes
|
||||||
.compactMap { $0 as? UIWindowScene }
|
.compactMap { $0 as? UIWindowScene }
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ enum UserApi {
|
|||||||
case loginApple(request: SocialLoginRequest)
|
case loginApple(request: SocialLoginRequest)
|
||||||
case loginGoogle(request: SocialLoginRequest, idToken: String)
|
case loginGoogle(request: SocialLoginRequest, idToken: String)
|
||||||
case loginKakao(request: SocialLoginRequest, accessToken: String)
|
case loginKakao(request: SocialLoginRequest, accessToken: String)
|
||||||
|
case loginLine(request: SocialLoginRequest)
|
||||||
case signUp(request: SignUpRequest)
|
case signUp(request: SignUpRequest)
|
||||||
case findPassword(request: ForgotPasswordRequest)
|
case findPassword(request: ForgotPasswordRequest)
|
||||||
case searchUser(nickname: String)
|
case searchUser(nickname: String)
|
||||||
@@ -59,6 +60,9 @@ extension UserApi: TargetType {
|
|||||||
case .loginKakao:
|
case .loginKakao:
|
||||||
return "/member/login/kakao"
|
return "/member/login/kakao"
|
||||||
|
|
||||||
|
case .loginLine:
|
||||||
|
return "/member/login/line"
|
||||||
|
|
||||||
case .signUp:
|
case .signUp:
|
||||||
return "/member/signup/v2"
|
return "/member/signup/v2"
|
||||||
|
|
||||||
@@ -132,7 +136,7 @@ extension UserApi: TargetType {
|
|||||||
|
|
||||||
var method: Moya.Method {
|
var method: Moya.Method {
|
||||||
switch self {
|
switch self {
|
||||||
case .login, .loginApple, .loginGoogle, .loginKakao, .signUp, .findPassword, .notification, .logout, .logoutAllDevice, .signOut, .creatorFollow, .creatorUnFollow, .memberBlock, .memberUnBlock,
|
case .login, .loginApple, .loginGoogle, .loginKakao, .loginLine, .signUp, .findPassword, .notification, .logout, .logoutAllDevice, .signOut, .creatorFollow, .creatorUnFollow, .memberBlock, .memberUnBlock,
|
||||||
.profileImageUpdate:
|
.profileImageUpdate:
|
||||||
return .post
|
return .post
|
||||||
|
|
||||||
@@ -158,6 +162,9 @@ extension UserApi: TargetType {
|
|||||||
case .loginKakao(let request, _):
|
case .loginKakao(let request, _):
|
||||||
return .requestJSONEncodable(request)
|
return .requestJSONEncodable(request)
|
||||||
|
|
||||||
|
case .loginLine(let request):
|
||||||
|
return .requestJSONEncodable(request)
|
||||||
|
|
||||||
case .signUp(let request):
|
case .signUp(let request):
|
||||||
return .requestJSONEncodable(request)
|
return .requestJSONEncodable(request)
|
||||||
|
|
||||||
@@ -220,7 +227,7 @@ extension UserApi: TargetType {
|
|||||||
|
|
||||||
var headers: [String : String]? {
|
var headers: [String : String]? {
|
||||||
switch self {
|
switch self {
|
||||||
case .login, .loginApple, .signUp, .findPassword:
|
case .login, .loginApple, .loginLine, .signUp, .findPassword:
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
case .loginGoogle(_, let idToken):
|
case .loginGoogle(_, let idToken):
|
||||||
|
|||||||
@@ -29,6 +29,10 @@ final class UserRepository {
|
|||||||
return api.requestPublisher(.loginKakao(request: request, accessToken: accessToken))
|
return api.requestPublisher(.loginKakao(request: request, accessToken: accessToken))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func loginLine(request: SocialLoginRequest) -> AnyPublisher<Response, MoyaError> {
|
||||||
|
return api.requestPublisher(.loginLine(request: request))
|
||||||
|
}
|
||||||
|
|
||||||
func signUp(request: SignUpRequest) -> AnyPublisher<Response, MoyaError> {
|
func signUp(request: SignUpRequest) -> AnyPublisher<Response, MoyaError> {
|
||||||
return api.requestPublisher(.signUp(request: request))
|
return api.requestPublisher(.signUp(request: request))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,3 +27,5 @@ let GID_CLIENT_ID = "983594297130-m6bv7lvc1lsetsvv3rk92etqc98uopqj.apps.googleus
|
|||||||
let GID_SERVER_CLIENT_ID = "983594297130-5hrmkh6vpskeq6v34350kmilf74574h2.apps.googleusercontent.com"
|
let GID_SERVER_CLIENT_ID = "983594297130-5hrmkh6vpskeq6v34350kmilf74574h2.apps.googleusercontent.com"
|
||||||
|
|
||||||
let KAKAO_APP_KEY = "231cf78acfa8252fca38b9eedf87c5cb"
|
let KAKAO_APP_KEY = "231cf78acfa8252fca38b9eedf87c5cb"
|
||||||
|
|
||||||
|
let LINE_CHANNEL_ID = "2008995539"
|
||||||
|
|||||||
Reference in New Issue
Block a user