Skip to content
19 changes: 9 additions & 10 deletions KkuMulKum.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
789D73AF2C46D99B00C7077D /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 789D73AE2C46D99B00C7077D /* GoogleService-Info.plist */; };
789D73B32C47CC6D00C7077D /* LocalNotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 789D73B22C47CC6D00C7077D /* LocalNotificationManager.swift */; };
789D73BE2C47FE0F00C7077D /* AuthInterceptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 789D73BD2C47FE0F00C7077D /* AuthInterceptor.swift */; };
78AA9F1C2D6CC667001F1BCE /* Pulse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78AA9F1B2D6CC667001F1BCE /* Pulse.swift */; };
78AA99CB2D670B67001F1BCE /* TokenRefreshManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78AA99CA2D670B67001F1BCE /* TokenRefreshManager.swift */; };
78AED1342C3D951F000AD80A /* NicknameViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78AED1332C3D951F000AD80A /* NicknameViewController.swift */; };
78AED1372C3D98D1000AD80A /* NicknameView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78AED1362C3D98D1000AD80A /* NicknameView.swift */; };
Expand Down Expand Up @@ -301,6 +302,7 @@
789D73B02C46DACD00C7077D /* KkuMulKum.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = KkuMulKum.entitlements; sourceTree = "<group>"; };
789D73B22C47CC6D00C7077D /* LocalNotificationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalNotificationManager.swift; sourceTree = "<group>"; };
789D73BD2C47FE0F00C7077D /* AuthInterceptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthInterceptor.swift; sourceTree = "<group>"; };
78AA9F1B2D6CC667001F1BCE /* Pulse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Pulse.swift; sourceTree = "<group>"; };
78AA99CA2D670B67001F1BCE /* TokenRefreshManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TokenRefreshManager.swift; sourceTree = "<group>"; };
78AED1332C3D951F000AD80A /* NicknameViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NicknameViewController.swift; sourceTree = "<group>"; };
78AED1362C3D98D1000AD80A /* NicknameView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NicknameView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1208,6 +1210,7 @@
789873372C3D1B4800435E96 /* ViewController */,
789873362C3D1B3900435E96 /* VIewModel */,
789873352C3D1B3000435E96 /* View */,
78AA9F1B2D6CC667001F1BCE /* Pulse.swift */,
);
path = Login;
sourceTree = "<group>";
Expand Down Expand Up @@ -1937,6 +1940,7 @@
DDD62FFB2C5FA4FE00174B57 /* MeetingService.swift in Sources */,
DD43937D2C412F4500EC1799 /* CheckInviteCodeViewController.swift in Sources */,
DEFBEBD92C46E4C200437188 /* AddPromiseCompleteView.swift in Sources */,
78AA9F1C2D6CC667001F1BCE /* Pulse.swift in Sources */,
DEFBEBDB2C46FD5700437188 /* AddPromiseResponseModel.swift in Sources */,
DD4393772C412F4500EC1799 /* CreateMeetingView.swift in Sources */,
789873342C3D1A7B00435E96 /* LoginView.swift in Sources */,
Expand Down Expand Up @@ -2232,11 +2236,9 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = KkuMulKum/KkuMulKumDebug.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Manual;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = D2DRA3F792;
DEVELOPMENT_TEAM = D2DRA3F792;
Comment on lines -2235 to +2241
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

어이구 이거 왜 이렇게 됐지요

GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = KkuMulKum/Resource/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "꾸물꿈";
Expand All @@ -2257,7 +2259,6 @@
PRODUCT_BUNDLE_IDENTIFIER = KkuMulKum.yizihn;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = KkumulkumDebug;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
Expand All @@ -2274,11 +2275,10 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = KkuMulKum/KkuMulKum.entitlements;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = D2DRA3F792;
DEVELOPMENT_TEAM = D2DRA3F792;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = KkuMulKum/Resource/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "꾸물꿈";
Expand All @@ -2300,7 +2300,6 @@
PRODUCT_BUNDLE_IDENTIFIER = KkuMulKum.yizihn;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = KkumulkumRelease1;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
Expand Down
35 changes: 30 additions & 5 deletions KkuMulKum/Application/SceneDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,41 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
handleNotification(userInfo: userInfo)
}

print("Setting up navigation pulse subscription")
loginViewModel.navigationPulse.subscribe(with: self) { [weak self] (owner, navigation) in
print("🚀 Navigation pulse received: \(navigation)")
guard let self = self else { return }

DispatchQueue.main.async {
switch navigation {
case .toMain:
self.showMainScreen()
print("🏠 Navigating to main screen")
case .toOnboarding:
let nicknameViewModel = NicknameViewModel()
let nicknameViewController = NicknameViewController(viewModel: nicknameViewModel)
let navigationController = UINavigationController(
rootViewController: nicknameViewController,
isBorderNeeded: false
)
self.animateRootViewControllerChange(to: navigationController)
print("👤 Navigating to onboarding")

case .showError(let message):
print("Login error: \(message)")
self.showLoginScreen()
}
}
}

performAutoLogin()
}

private func performAutoLogin() {
print("Performing auto login")
loginViewModel.autoLogin { [weak self] success in
DispatchQueue.main.async {
if success {
self?.showMainScreen()
} else {
if !success {
self?.showLoginScreen()
}
}
Expand All @@ -61,7 +86,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
return false
}

private func showMainScreen() {
func showMainScreen() {
let mainTabBarController = MainTabBarController()
let navigationController = UINavigationController(
rootViewController: mainTabBarController,
Expand All @@ -82,7 +107,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
animateRootViewControllerChange(to: loginViewController)
}

private func animateRootViewControllerChange(to newRootViewController: UIViewController) {
func animateRootViewControllerChange(to newRootViewController: UIViewController) {
guard let window = self.window else { return }

UIView.transition(with: window,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ class MyPageViewController: BaseViewController, CustomActionSheetDelegate {

private func navigateToLoginScreen() {
let loginViewModel = LoginViewModel()
loginViewModel.logout()
let loginViewController = LoginViewController(viewModel: loginViewModel)
loginViewController.modalPresentationStyle = .fullScreen
self.present(loginViewController, animated: true, completion: nil)
Expand Down
81 changes: 81 additions & 0 deletions KkuMulKum/Source/Onboarding/Login/Pulse.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
//
// Pulse.swift
// KkuMulKum
//
// Created by 이지훈 on 2/24/25.
//

import Foundation

/// 일회성 이벤트를 관리하기 위한 제네릭 클래스
/// Pulse는 이벤트를 한 번만 전달하고, 이후 추가 구독자에게는 마지막 이벤트를 즉시 전달하는 메커니즘을 제공합니다.
class Pulse<T> {
/// 리스너 타입 정의 (이벤트 핸들러)
typealias Listener = (T) -> Void

/// 현재 저장된 값
private var value: T?

/// 등록된 리스너 목록
private var listeners: [Listener] = []

/// 이벤트가 이미 소비되었는지 추적하는 플래그
private var isConsumed = false

/// 새로운 Pulse 인스턴스 생성
init() {}

/// 이벤트 값을 전달하고 모든 리스너에게 알림
/// - Parameter value: 전달할 이벤트 값
/// - Note: 이벤트는 한 번만 전달되며, 이후 추가 emit은 무시됨
func emit(_ value: T) {
guard !isConsumed else { return }

self.value = value
isConsumed = true

// 모든 리스너에게 값 전달
listeners.forEach { $0(value) }

// 발신 후 리스너 비우기
listeners.removeAll()
}

/// 리스너를 등록하고 이미 발행된 이벤트가 있다면 즉시 전달
/// - Parameter listener: 이벤트를 처리할 클로저
func subscribe(_ listener: @escaping Listener) {
if let value = value, isConsumed {
// 이미 값이 발신되었다면 즉시 전달
listener(value)
} else {
// 아직 값이 없다면 리스너 등록
listeners.append(listener)
}
}

/// 약한 참조로 리스너를 등록하고 이미 발행된 이벤트가 있다면 즉시 전달
/// - Parameters:
/// - object: 리스너가 속한 약한 참조 객체
/// - listener: 이벤트를 처리할 클로저 (객체와 값을 받음)
func subscribe<O: AnyObject>(with object: O, listener: @escaping (O, T) -> Void) {
let wrappedListener: Listener = { [weak object] value in
guard let object = object else { return }
listener(object, value)
}

if let value = value, isConsumed {
// 이미 값이 발신되었다면 즉시 전달
wrappedListener(value)
} else {
// 아직 값이 없다면 리스너 등록
listeners.append(wrappedListener)
}
}

/// 모든 상태를 초기화하여 Pulse를 재사용 가능한 상태로 만듦
func reset() {
value = nil
isConsumed = false
listeners.removeAll()
}
}
Loading