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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -114,4 +114,6 @@ StageConfig.xcconfig
LiveConfig.xcconfig
BaseConfig.xcconfig

.claude

Atcha-iOS/DesignSource/AtchaImage/Icon.xcassets/Onboarding/.DS_Store
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 = 11;
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.6;
MARKETING_VERSION = 1.9.7;
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 = 11;
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.6;
MARKETING_VERSION = 1.9.7;
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 = 11;
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.6;
MARKETING_VERSION = 1.9.7;
PRODUCT_BUNDLE_IDENTIFIER = com.atcha.iOS;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"images" : [
{
"filename" : "alarm_cancel.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "alarm_cancel@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "alarm_cancel@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.
46 changes: 45 additions & 1 deletion Atcha-iOS/Presentation/Lock/LockViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ final class LockViewController: BaseViewController<LockViewModel> {
private let titleLabel: UILabel = UILabel()
private let taxiFareLabel: UILabel = UILabel()
private let startButton: AtchaButton = AtchaButton(text: "출발하기", size: .h52, style: .filled(.primary))
private let cancelImageView: UIImageView = UIImageView()
private let detailRouteButton: AtchaButton = AtchaButton(text: "더 늦은 경로 확인하기", size: .h52, style: .filled(.opacity))
private let bottomStack: UIStackView = UIStackView()
private var lottieAnimationView: LottieAnimationView = LottieAnimationView(name: "Alarm")
Expand Down Expand Up @@ -71,7 +72,7 @@ final class LockViewController: BaseViewController<LockViewModel> {

// MARK: - Lock UI
private func setupUI() {
view.addSubViews(backgroundImageView, lottieAnimationView, gradientView, logoImageView, titleLabel, taxiFareLabel, bottomStack)
view.addSubViews(backgroundImageView, lottieAnimationView, gradientView, logoImageView, titleLabel, taxiFareLabel, bottomStack, cancelImageView)

backgroundImageView.image = UIImage.lockBackground
gradient.colors = [
Expand All @@ -96,6 +97,9 @@ final class LockViewController: BaseViewController<LockViewModel> {
startButton.addTarget(self,
action: #selector(startTapped),
for: .touchUpInside)
cancelImageView.image = UIImage.alarmCancel
cancelImageView.isUserInteractionEnabled = true
cancelImageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(cancelAlarmTapped)))

detailRouteButton.addTarget(self,
action: #selector(detailRouteTapped),
Expand Down Expand Up @@ -137,6 +141,12 @@ final class LockViewController: BaseViewController<LockViewModel> {
make.leading.equalToSuperview().offset(20)
make.trailing.equalToSuperview().inset(20)
}

cancelImageView.snp.makeConstraints { make in
make.top.equalTo(view.safeAreaLayoutGuide.snp.top).inset(18)
make.trailing.equalToSuperview().inset(16)
make.size.equalTo(24)
}
}

@objc private func startTapped() {
Expand Down Expand Up @@ -175,6 +185,10 @@ final class LockViewController: BaseViewController<LockViewModel> {
)
}

@objc private func cancelAlarmTapped() {
showAlarmCancelPopup()
}

private func observeAlarmTimeout() {
NotificationCenter.default.publisher(for: NSNotification.Name("alarmDidTimeout"))
.receive(on: RunLoop.main)
Expand All @@ -184,4 +198,34 @@ final class LockViewController: BaseViewController<LockViewModel> {
}
.store(in: &cancellables)
}

private func showAlarmCancelPopup() {
AlarmManager.shared.stopAlarm()

let popupVM = AtchaPopupViewModel(info: .alarm_cancel)
let popupVC = AtchaPopupViewController(viewModel: popupVM)

popupVC.cancelButton.addAction(UIAction { [weak popupVC] _ in
popupVC?.dismiss(animated: false)
}, for: .touchUpInside)

popupVC.confirmButton.addAction(UIAction { [weak self, weak popupVC] _ in
guard let self else { return }
popupVC?.dismiss(animated: false)

self.viewModel.cancelLockScreenTimer()
AlarmManager.shared.stopAlarm()
AlarmManager.shared.removeAllAlarmNotificationsExceptAutoStop()

UserDefaultsWrapper.shared.set(
false,
forKey: UserDefaultsWrapper.Key.departureAlarmDidFire.rawValue
)

viewModel.routerHandler?(.dismissLockScreen)
}, for: .touchUpInside)

popupVC.modalPresentationStyle = .overFullScreen
present(popupVC, animated: false)
}
}
7 changes: 6 additions & 1 deletion Atcha-iOS/Presentation/Main/MainCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,12 @@ final class MainCoordinator: NSObject {
context: .afterReigster))
}
case .dismissLockScreen:
self?.navigationController.dismiss(animated: true)
self?.mainViewModel?.stopAlarmTimeoutTimer()
self?.navigationController.dismiss(animated: true) { [weak self] in
self?.mainViewModel?.alarmDelete()
self?.mainViewModel?.bottomType = .search
self?.mainViewModel?.removeLegInfoAndAddress()
}
default: do {}
}
}
Expand Down
7 changes: 5 additions & 2 deletions Atcha-iOS/Presentation/Popup/AtchaPopupInfo.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ enum AtcahPopuInfo {
case serverError
case update_essential
case update_recommended
case alarm_cancel

var title: String {
switch self {
Expand All @@ -35,6 +36,7 @@ enum AtcahPopuInfo {
case .serverError: return "잠시 후 다시 시도해주세요\n앗차팀에서 확인 및 대응 중입니다"
case .update_essential: return "더 좋아진 앗차를 사용하기 위해\n업데이트가 필요해요"
case .update_recommended: return "더 좋아진 앗차를 사용하기 위해\n업데이트를 권장해요"
case .alarm_cancel: return "알람을 종료할까요?"
}
}

Expand All @@ -52,12 +54,13 @@ enum AtcahPopuInfo {
case .serverError: return "확인"
case .update_essential: return "업데이트"
case .update_recommended: return "업데이트"
case .alarm_cancel: return "종료하기"
}
}

var confrimBackgroundColor: UIColor {
switch self {
case .alarm, .re_register, .course, .arrive: return .main
case .alarm, .re_register, .course, .arrive, .alarm_cancel: return .main
case .alarmTimeout, .serverError, .scheduledArrive: return .gray910
default: return .white
}
Expand All @@ -73,7 +76,7 @@ enum AtcahPopuInfo {
var cancelTitle: String {
switch self {
case .alarm, .re_register: return "돌아가기"
case .course: return "돌아가기"
case .course, .alarm_cancel: return "돌아가기"
case .update_recommended: return "나중에"
default: return "취소"
}
Expand Down
98 changes: 57 additions & 41 deletions Atcha-iOS/Presentation/Splash/SplashViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,27 +46,20 @@ final class SplashViewController: BaseViewController<SplashViewModel> {
}

private func setupBindings() {
viewModel.$appVersionInfo
.compactMap { $0 }
.receive(on: DispatchQueue.main)
.sink { [weak self] versionInfo in
guard let self else { return }
updateAppVersion(versionInfo)
}
.store(in: &cancellables)
fetchAppStoreVersion()
}

private func updateAppVersion(_ version: String) {
let serverVersion = version
let appVersion = AppInfoProvider.versionWithV
// 서버 버전이 앱 버전보다 높다면 업데이트가 필요한 상황
if isVersion(appVersion, lessThan: serverVersion) {
showUpdatePopup(isEssential: false)
} else {
viewModel.makeInitialFlow()
}
}
// private func updateAppVersion(_ version: String) {
// let serverVersion = version
// let appVersion = AppInfoProvider.versionWithV
//
// // 서버 버전이 앱 버전보다 높다면 업데이트가 필요한 상황
// if isVersion(appVersion, lessThan: serverVersion) {
// showUpdatePopup(isEssential: false)
// } else {
// viewModel.makeInitialFlow()
// }
// }

/// lhs < rhs 인지 비교 (서버 버전 < 앱 버전?)
private func isVersion(_ lhs: String, lessThan rhs: String) -> Bool {
Expand All @@ -84,31 +77,54 @@ final class SplashViewController: BaseViewController<SplashViewModel> {
}

private func showUpdatePopup(isEssential: Bool) {
// 1. 팝업 뷰모델 생성 (info 타입은 프로젝트 정의에 맞게 조절하세요)
let popupVM = AtchaPopupViewModel(info: isEssential ? .update_essential : .update_recommended)
let popupVC = AtchaPopupViewController(viewModel: popupVM)
// 1. 팝업 뷰모델 생성 (info 타입은 프로젝트 정의에 맞게 조절하세요)
let popupVM = AtchaPopupViewModel(info: isEssential ? .update_essential : .update_recommended)
let popupVC = AtchaPopupViewController(viewModel: popupVM)

// 2. [업데이트하기] 버튼 로직
popupVC.confirmButton.addAction(UIAction { _ in
let appID = "6747877903"
if let url = URL(string: "itms-apps://itunes.apple.com/app/id\(appID)"),
UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url)
}
}, for: .touchUpInside)

// 3. [닫기/취소] 버튼 로직
popupVC.cancelButton.addAction(UIAction { [weak self, weak popupVC] _ in
popupVC?.dismiss(animated: false)

// 2. [업데이트하기] 버튼 로직
popupVC.confirmButton.addAction(UIAction { _ in
let appID = "6747877903"
if let url = URL(string: "itms-apps://itunes.apple.com/app/id\(appID)"),
UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url)
}
}, for: .touchUpInside)
if isEssential {
print("필수 업데이트입니다. 진행할 수 없습니다.")
} else {
self?.viewModel.makeInitialFlow()
}
}, for: .touchUpInside)

popupVC.modalPresentationStyle = .overFullScreen
present(popupVC, animated: false)
}

private func fetchAppStoreVersion() {
guard let url = URL(string: "https://itunes.apple.com/lookup?id=6747877903&country=kr") else { return }

URLSession.shared.dataTask(with: url) { [weak self] data, _, _ in
guard let self,
let data,
let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
let results = json["results"] as? [[String: Any]],
let appStoreVersion = results.first?["version"] as? String else { return }

// 3. [닫기/취소] 버튼 로직
popupVC.cancelButton.addAction(UIAction { [weak self, weak popupVC] _ in
popupVC?.dismiss(animated: false)
DispatchQueue.main.async {
let appVersion = AppInfoProvider.versionWithV
let storeVersion = "v\(appStoreVersion)"

if isEssential {
print("필수 업데이트입니다. 진행할 수 없습니다.")
if self.isVersion(appVersion, lessThan: storeVersion) {
self.showUpdatePopup(isEssential: false)
} else {
self?.viewModel.makeInitialFlow()
self.viewModel.makeInitialFlow()
}
}, for: .touchUpInside)

popupVC.modalPresentationStyle = .overFullScreen
present(popupVC, animated: false)
}
}
}.resume()
}
}
Loading