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
12 changes: 6 additions & 6 deletions Atcha-iOS.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -2516,7 +2516,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 4;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 23SCTLK482;
FRAMEWORK_SEARCH_PATHS = (
Expand All @@ -2539,7 +2539,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.9.5;
MARKETING_VERSION = 1.9.6;
PRODUCT_BUNDLE_IDENTIFIER = com.atcha.iOS;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
Expand All @@ -2563,7 +2563,7 @@
CODE_SIGN_ENTITLEMENTS = "Atcha-iOS/Atcha-iOS.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 4;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 23SCTLK482;
EXCLUDED_ARCHS = "";
FRAMEWORK_SEARCH_PATHS = (
Expand All @@ -2586,7 +2586,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.9.5;
MARKETING_VERSION = 1.9.6;
OTHER_SWIFT_FLAGS = "";
PRODUCT_BUNDLE_IDENTIFIER = com.atcha.iOS;
PRODUCT_NAME = "$(TARGET_NAME)";
Expand All @@ -2611,7 +2611,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 4;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 23SCTLK482;
FRAMEWORK_SEARCH_PATHS = (
Expand All @@ -2634,7 +2634,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.9.5;
MARKETING_VERSION = 1.9.6;
PRODUCT_BUNDLE_IDENTIFIER = com.atcha.iOS;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
Expand Down
11 changes: 7 additions & 4 deletions Atcha-iOS/App/AppFlowCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class AppFlowCoordinator {
self.container = container
}

func startApp() {
func startApp(launchType: LaunchType = .main) {
let navigationController = UINavigationController()
window.rootViewController = navigationController
window.makeKeyAndVisible()
Expand All @@ -35,11 +35,14 @@ class AppFlowCoordinator {
DispatchQueue.main.async {
AppDIContainer.shared.tokenStorage.clearAllTokens()
UserDefaultsWrapper.shared.set(false, forKey: UserDefaultsWrapper.Key.hasSeenIntro.rawValue)
self?.startApp()
self?.startApp(launchType: .fast)
}
}

let splashCoordinator = container.makeSplashCoordinator(navigationController: navigationController)
let splashCoordinator = container.makeSplashCoordinator(
navigationController: navigationController,
launchType: launchType
)
splashCoordinator.routerHandler = { [weak self] router in
guard let self = self else { return }
switch router {
Expand Down Expand Up @@ -114,7 +117,7 @@ class AppFlowCoordinator {
mainCoordinator.withdrawFinish = { [weak self] in
DispatchQueue.main.async {
// 앱 데이터를 다 지웠으니, 스플래시부터 앱을 아예 새로 시작(리부팅)합니다!
self?.startApp()
self?.startApp(launchType: .fast)
}
}

Expand Down
4 changes: 2 additions & 2 deletions Atcha-iOS/App/DIContainer/AppCompositionRoot.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ extension AppCompositionRoot: SplashCoordinatorFactory,
MainCoordinatorFactory,
LockScreenCoordinatorFactory,
IntroCoordinatorFactory {
func makeSplashCoordinator(navigationController: UINavigationController) -> SplashCoordinator {
return splashDIContainer.makeSplashCoordinator(navigationController: navigationController)
func makeSplashCoordinator(navigationController: UINavigationController, launchType: LaunchType) -> SplashCoordinator {
return splashDIContainer.makeSplashCoordinator(navigationController: navigationController, launchType: launchType)
}

func makeLoginCoordinator(navigationController: UINavigationController) -> LoginCoordinator {
Expand Down
6 changes: 3 additions & 3 deletions Atcha-iOS/App/DIContainer/CoordinatorFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import Foundation
/// to avoid service locator style lookups and to enable constructor injection of dependencies.

protocol SplashCoordinatorFactory {
func makeSplashCoordinator(navigationController: UINavigationController) -> SplashCoordinator
func makeSplashCoordinator(navigationController: UINavigationController, launchType: LaunchType) -> SplashCoordinator
}

protocol LoginCoordinatorFactory {
Expand Down Expand Up @@ -45,8 +45,8 @@ extension AppDIContainer: SplashCoordinatorFactory,
MainCoordinatorFactory,
LockScreenCoordinatorFactory,
IntroCoordinatorFactory {
func makeSplashCoordinator(navigationController: UINavigationController) -> SplashCoordinator {
return splashDIContainer.makeSplashCoordinator(navigationController: navigationController)
func makeSplashCoordinator(navigationController: UINavigationController, launchType: LaunchType) -> SplashCoordinator {
return splashDIContainer.makeSplashCoordinator(navigationController: navigationController, launchType: launchType)
}

func makeLoginCoordinator(navigationController: UINavigationController) -> LoginCoordinator {
Expand Down
10 changes: 6 additions & 4 deletions Atcha-iOS/App/DIContainer/Splash/SplashDIContainer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,21 +32,23 @@ final class SplashDIContainer {
return UpdateAppVersionUseCaseImpl(repository: repository)
}

func makeSplashViewModel() -> SplashViewModel {
func makeSplashViewModel(launchType: LaunchType) -> SplashViewModel {
return SplashViewModel(
fetchUserUseCase: makeFetchUserUseCase(),
checkAppVersionUseCase: makeCheckAppVersionUseCase(),
updateAppVersionUseCase: makeUpdateAppVersionUseCase(),
tokenStorage: tokenStorage
tokenStorage: tokenStorage,
launchType: launchType
)
}

func makeSplashViewController(viewModel: SplashViewModel) -> SplashViewController {
return SplashViewController(viewModel: viewModel)
}

func makeSplashCoordinator(navigationController: UINavigationController) -> SplashCoordinator {
func makeSplashCoordinator(navigationController: UINavigationController, launchType: LaunchType) -> SplashCoordinator {
SplashCoordinator(navigationController: navigationController,
diContainer: self)
diContainer: self,
launchType: launchType)
}
}
3 changes: 2 additions & 1 deletion Atcha-iOS/App/SceneDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
let window = UIWindow(windowScene: windowScene)
var launchType: LaunchType = .main

appFlowCoordinator = AppFlowCoordinator(window: window, container: diContainer)
appFlowCoordinator?.startApp()
appFlowCoordinator?.startApp(launchType: launchType)

self.window = window
}
Expand Down
4 changes: 2 additions & 2 deletions Atcha-iOS/Base.lproj/LaunchScreen.storyboard
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="23727" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="24506" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23721"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="24504"/>
<capability name="Named colors" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
Expand Down
2 changes: 2 additions & 0 deletions Atcha-iOS/Core/Network/Token/SessionController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ final class SessionController {
storage.clearRefreshToken()
UserDefaultsWrapper.shared.removeAll()
AppDIContainer.shared.locationStateHolder.clear()
UserDefaultsWrapper.shared.set(true, forKey: UserDefaultsWrapper.Key.isGuest.rawValue)
UserDefaultsWrapper.shared.set(false, forKey: UserDefaultsWrapper.Key.hasSeenIntro.rawValue)

// 로그인 화면으로
DispatchQueue.main.async { [weak self] in
Expand Down
21 changes: 1 addition & 20 deletions Atcha-iOS/Core/Network/Token/TokenInterceptor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,6 @@ final class TokenInterceptor: RequestInterceptor, @unchecked Sendable {
return
}

let actualHeaders = request.request?.allHTTPHeaderFields ?? [:]

guard let refreshToken = tokenStorage.refreshToken else {
SessionController.shared.expireAndRouteToLogin()
completion(.doNotRetry)
Expand Down Expand Up @@ -109,24 +107,7 @@ final class TokenInterceptor: RequestInterceptor, @unchecked Sendable {
waiters.forEach { $0(.doNotRetry) }
return
}

let successBody = [
"newAccessToken": p.accessToken,
"newRefreshToken": p.refreshToken
]

DiscordWebhookManager.shared.sendErrorLog(
baseURL: NetworkConstant.baseURL,
statusCode: 200,
method: "GET",
path: "/auth/reissue",
responseCode: "REISSUE_SUCCESS",
message: "토큰 재발급에 성공하여 새로운 토큰을 수신했습니다.",
requestHeaders: actualHeaders,
requestBody: successBody, // 여기서 받은 토큰 정보를 보냅니다.
requestParameters: nil
)


self.tokenStorage.accessToken = p.accessToken
self.tokenStorage.refreshToken = p.refreshToken

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"images" : [
{
"filename" : "setting_home_mark.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "setting_home_mark@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "setting_home_mark@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 0 additions & 5 deletions Atcha-iOS/Presentation/Intro/IntroViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -178,11 +178,6 @@ final class IntroViewController: BaseViewController<IntroViewModel> {
label.snp.makeConstraints { make in
make.center.equalToSuperview()
}

button.snp.makeConstraints { make in
make.horizontalEdges.equalToSuperview().inset(15)
make.height.equalTo(56)
}
}
}

Expand Down
69 changes: 68 additions & 1 deletion Atcha-iOS/Presentation/Location/MainViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ final class MainViewController: BaseViewController<MainViewModel>,
if viewModel.isGuest { return false }
return viewModel.isGuideActiveInSession
}
private var guestTapCount = 0

// MARK: - Life Cycle

Expand Down Expand Up @@ -152,6 +153,12 @@ final class MainViewController: BaseViewController<MainViewModel>,
}
}
}

self.guestTapCount = 0
if viewModel.isGuest {
ballonView.isHidden = true
ballonView.alpha = 0
}
}

override func viewDidAppear(_ animated: Bool) {
Expand Down Expand Up @@ -229,6 +236,8 @@ final class MainViewController: BaseViewController<MainViewModel>,
flagImageView.image = UIImage.settingLocationMark
atchaImageView.isUserInteractionEnabled = true
atchaImageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleBallonTap)))
ballonView.isUserInteractionEnabled = true
ballonView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleBallonTap)))

ballonView.isHidden = true
ballonView.alpha = 0
Expand Down Expand Up @@ -309,6 +318,7 @@ extension MainViewController {
observeArrival()
observeScheduledArrivalTimeout()
observeAlarmTimeout()
observeLoginDismissal()
}

private func bindPermissionAlert() {
Expand Down Expand Up @@ -956,6 +966,13 @@ extension MainViewController {
}

@objc private func handleBallonTap() {
safeStartJump()

if viewModel.isGuest {
handleGuestBallonTap()
return
}

// 알람 등록 후(departure 상태)일 때만 반응
guard viewModel.bottomType == .departure else { return }

Expand Down Expand Up @@ -1014,6 +1031,31 @@ extension MainViewController {
postAlarmTapIndex += 1
}
}

private func handleGuestBallonTap() {
if guestTapCount == 0 {
// 첫 번째 터치: "궁금하면 로그인 해봐요!"
guestTapCount = 1

ballonView.layer.removeAllAnimations()
ballonView.isHidden = false
ballonView.alpha = 1

ballonView.setupTitle(topMessage: nil, bottomMessage: "궁금하면 로그인 해봐요!")
ballonView.animateStaggered(secondaryDelay: 0, fade: 0.25)

} else {
// 두 번째 터치: 로그인 시트 노출
guestTapCount = 0 // 카운트 리셋

// [중요] 말풍선을 즉시 숨김 상태로 만들어야 시트가 내려간 뒤 다시 Persistent(???원) 메시지가 나타납니다.
ballonView.layer.removeAllAnimations()
ballonView.isHidden = true
ballonView.alpha = 0

presentLoginAlert()
}
}
}

// MARK: - Map Delegate & Gesture
Expand Down Expand Up @@ -1233,6 +1275,10 @@ extension MainViewController {
private func showOrUpdatePersistentBalloon(isFirstVisit: Bool, isServiceRegion: Bool?, fareStr: String?) {
guard !isShowingToast else { return }

if viewModel.isGuest && guestTapCount == 1 {
return
}

// [수정] 우리가 정의한 로그인 기반 가이드 로직 적용
let showGuideLine = shouldShowMapGuide
let topText: String? = showGuideLine ? "지도를 움직여 출발지를 설정해요" : nil
Expand All @@ -1242,7 +1288,7 @@ extension MainViewController {
} else {
if viewModel.isGuest {
// 비로그인: 가이드 없이 ???원만 노출
ballonView.separationTitle(grayMessage: "여기서 막차 놓치면 택시비 ", whiteMessage: "???원", showTopLine: false)
ballonView.separationTitle(grayMessage: "여기서 막차 놓치면 택시비 ", whiteMessage: "???원", showTopLine: false)
} else {
// 로그인 상태
if let fare = fareStr {
Expand Down Expand Up @@ -1315,4 +1361,25 @@ extension MainViewController {
balloonHideWorkItem = workItem
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0, execute: workItem)
}

private func observeLoginDismissal() {
NotificationCenter.default.publisher(for: NSNotification.Name("LoginSheetDismissed"))
.receive(on: RunLoop.main)
.sink { [weak self] _ in
guard let self = self else { return }

// 로그인 시트가 내려갔으니 guestTapCount도 초기화해주는 게 자연스러워요
self.guestTapCount = 0

// 현재 검색 모드라면 다시 고정 말풍선 노출
if self.viewModel.bottomType == .search || self.viewModel.bottomType == nil {
self.showOrUpdatePersistentBalloon(
isFirstVisit: self.isFirstVisit,
isServiceRegion: self.latestIsServiceRegion,
fareStr: self.latestFareString
)
}
}
.store(in: &cancellables)
}
}
1 change: 1 addition & 0 deletions Atcha-iOS/Presentation/Login/LoginViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ extension LoginViewController {
self.containerView.transform = CGAffineTransform(translationX: 0, y: self.sheetHeight)
}) { _ in
self.dismiss(animated: false) {
NotificationCenter.default.post(name: NSNotification.Name("LoginSheetDismissed"), object: nil)
self.viewModel.loginCancelled?()
}
}
Expand Down
Loading
Loading