Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
096357f
[BUGFIX] 알람 울린 후 지도 드래그 시 위치 따라가기 중지
woolnd Mar 10, 2026
2ecb1ad
[BUGFIX] 대중교통 시간 표시 로직 수정 및 알람 종료 시 폴링 중단
woolnd Mar 10, 2026
f040df1
[FEAT] 이동 평균 필터 및 경로 스냅 로직 도입으로 위치 정확도 개선
woolnd Mar 11, 2026
7179839
[BUGFIX] 경로 근접 감지 로직 안정화
woolnd Mar 11, 2026
e3440b7
[FEAT] 알람 울리면 바텀시트 라이팅 변경
woolnd Mar 11, 2026
5f92597
[BUGFIX] 팝업창 애니메이션 삭제
woolnd Mar 11, 2026
c226458
[FEAT] 알람 2분 타임아웃 및 집 도착(50m) 자동 종료 기능 구현
woolnd Mar 11, 2026
e7fded3
[BUGFIX] 알람 타임아웃 종료 버그 수정 및 메인 복귀 UX 개선
woolnd Mar 11, 2026
933cb27
[BUGFIX] 도착 푸시 누락 이슈 수정
woolnd Mar 11, 2026
043992e
[BUGFIX] 실시간 데이터 폴링 통합 및 새로고침 애니메이션 추가
woolnd Mar 11, 2026
7357ad6
Merge pull request #302 from Atcha-Project/bugfix/#301
woolnd Mar 11, 2026
2408efc
[BUGFIX] 알람 종료 시 현 위치 1회 가지고 오기
woolnd Mar 11, 2026
2ee0eda
[BUGFIX] 2분 자동 타임아웃 중복 적용 수정
woolnd Mar 11, 2026
32d020a
[BUGFIX] 집주소 로직 수정
woolnd Mar 11, 2026
cc82bcb
[BUGFIX] 검색 좌표 로직 개선
woolnd Mar 11, 2026
f9e17fc
[BUGFIX] 가장 늦은 막차 키워드 갱신 로직 수정
woolnd Mar 11, 2026
6effe47
[BUGFIX] 상세 경로 화면 중복 push 방지 로직 추가
woolnd Mar 11, 2026
561af40
[SETTING] 빌드 번호 수정
woolnd Mar 11, 2026
006899f
Merge pull request #304 from Atcha-Project/bugfix/#303
woolnd Mar 11, 2026
44a53f9
[BUGFIX] 주소 전달 로직 수정
woolnd Mar 11, 2026
593e255
[BUGFIX] 유저 패치 구조 수정
woolnd Mar 12, 2026
e18ec53
[BUGFIX] 택시비 초기화 로직 추가
woolnd Mar 12, 2026
1af88e4
[FEAT] 메인에서 집 주소 수정 기능 구현
woolnd Mar 12, 2026
8b702a2
[BUGFIX] 택시비 초기화 로직 수정
woolnd Mar 12, 2026
b2930b7
[BUGFIX] 메인 화면 복귀 시 자식 코디네이터 참조 해제 로직 추가
woolnd Mar 12, 2026
45f71ea
[FEAT] 메인에서 집주소 변경 버튼 이미지 추가
woolnd Mar 13, 2026
4801a45
[FEAT] Time-Gap 기반 지하철 탈출 감지 및 위치 복구 로직 구현
woolnd Mar 13, 2026
2eba02f
[FEAT] 대중교통 탑승 완료(Boarding) 자동 감지 및 UI 고정 로직 구현
woolnd Mar 13, 2026
b970538
[BUGFIX] 막차 검색 도착지 설정 뷰 터치 영역 버그 수정
woolnd Mar 13, 2026
9401a55
Merge pull request #306 from Atcha-Project/feat/#305
woolnd Mar 13, 2026
cd27ce2
[FEAT] Intro, Main 화면 진입 및 유저 상태 추적 구현
woolnd Mar 13, 2026
9befb43
[FEAT] 유저 상태 추적 구현
woolnd Mar 13, 2026
fa98ccf
[FEAT] 토스트메세지 삭제 제츠처 구현
woolnd Mar 13, 2026
416961a
Merge pull request #308 from Atcha-Project/feat/#307
woolnd Mar 13, 2026
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
40 changes: 34 additions & 6 deletions Atcha-iOS.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,9 @@
6DC3BF5E2E07123F00831470 /* IntroCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DC3BF5D2E07123F00831470 /* IntroCell.swift */; };
6DC3BF602E071F0900831470 /* LoginUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DC3BF5F2E071F0900831470 /* LoginUseCase.swift */; };
6DC3BF682E0721F300831470 /* LoginDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DC3BF672E0721F300831470 /* LoginDTO.swift */; };
6DC617A72F60EB1A002DD641 /* LocationSmoother.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DC617A62F60EB1A002DD641 /* LocationSmoother.swift */; };
6DC617A92F610238002DD641 /* HomeArrivalManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DC617A82F610238002DD641 /* HomeArrivalManager.swift */; };
6DC617AD2F62EB59002DD641 /* HomeRegistrationCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DC617AC2F62EB59002DD641 /* HomeRegistrationCoordinator.swift */; };
6DD632B12E4F8A9F00C6A66E /* CheckServiceRegionRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DD632B02E4F8A9F00C6A66E /* CheckServiceRegionRequest.swift */; };
6DD632B62E52E23A00C6A66E /* ProximityManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DD632B52E52E23A00C6A66E /* ProximityManager.swift */; };
6DD632B92E52E8E300C6A66E /* ProximityViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DD632B82E52E8E300C6A66E /* ProximityViewController.swift */; };
Expand Down Expand Up @@ -472,6 +475,9 @@
6DC3BF5D2E07123F00831470 /* IntroCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntroCell.swift; sourceTree = "<group>"; };
6DC3BF5F2E071F0900831470 /* LoginUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginUseCase.swift; sourceTree = "<group>"; };
6DC3BF672E0721F300831470 /* LoginDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginDTO.swift; sourceTree = "<group>"; };
6DC617A62F60EB1A002DD641 /* LocationSmoother.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationSmoother.swift; sourceTree = "<group>"; };
6DC617A82F610238002DD641 /* HomeArrivalManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeArrivalManager.swift; sourceTree = "<group>"; };
6DC617AC2F62EB59002DD641 /* HomeRegistrationCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeRegistrationCoordinator.swift; sourceTree = "<group>"; };
6DD632B02E4F8A9F00C6A66E /* CheckServiceRegionRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckServiceRegionRequest.swift; sourceTree = "<group>"; };
6DD632B52E52E23A00C6A66E /* ProximityManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProximityManager.swift; sourceTree = "<group>"; };
6DD632B82E52E8E300C6A66E /* ProximityViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProximityViewController.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -687,6 +693,7 @@
6D1EE2D62E08E4AB00F7BBF1 /* HomeRegister */ = {
isa = PBXGroup;
children = (
6DC617AB2F62EB4B002DD641 /* Coordinator */,
6D1EE2D92E08E4E000F7BBF1 /* HomeRegisterViewController.swift */,
6D1EE2DB2E08E4EA00F7BBF1 /* HomeRegisterViewModel.swift */,
);
Expand Down Expand Up @@ -1109,6 +1116,21 @@
path = Login;
sourceTree = "<group>";
};
6DC617AA2F62E931002DD641 /* ChangeHome */ = {
isa = PBXGroup;
children = (
);
path = ChangeHome;
sourceTree = "<group>";
};
6DC617AB2F62EB4B002DD641 /* Coordinator */ = {
isa = PBXGroup;
children = (
6DC617AC2F62EB59002DD641 /* HomeRegistrationCoordinator.swift */,
);
path = Coordinator;
sourceTree = "<group>";
};
6DD632AE2E4F8A6600C6A66E /* SearchLocation */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -1181,6 +1203,8 @@
6DADA5952EA09B9500CA9BE2 /* Amplitude */,
B61C448D2E3F57B600285A4B /* AlarmManager.swift */,
6DD632B72E52E8D000C6A66E /* Proximity */,
6DC617A62F60EB1A002DD641 /* LocationSmoother.swift */,
6DC617A82F610238002DD641 /* HomeArrivalManager.swift */,
);
path = Manager;
sourceTree = "<group>";
Expand Down Expand Up @@ -1305,6 +1329,7 @@
B65C12D82E042A320016D2F0 /* DIContainer */ = {
isa = PBXGroup;
children = (
6DC617AA2F62E931002DD641 /* ChangeHome */,
6D61ABE62F57174500111C9B /* Intro */,
B61C44962E40340C00285A4B /* LockScreen */,
6D2B8CB62E39C87F00608104 /* BusInfo */,
Expand Down Expand Up @@ -2144,6 +2169,7 @@
6DB7636D2E45C69400D06A49 /* AlarmRepository.swift in Sources */,
6DB7636F2E45C6D100D06A49 /* AlarmRepositoryImpl.swift in Sources */,
B65C12E22E042D400016D2F0 /* FetchUserUseCase.swift in Sources */,
6DC617AD2F62EB59002DD641 /* HomeRegistrationCoordinator.swift in Sources */,
B6EDD73C2E0D87F2006170DF /* FileStorageImpl.swift in Sources */,
B6793D4B2E3493D6001BE9F5 /* AtcahaInsetLabel.swift in Sources */,
6D2B8CB52E39AC7200608104 /* BusPositionInfoResponse.swift in Sources */,
Expand Down Expand Up @@ -2199,6 +2225,7 @@
B664018B2E2277A900A397AE /* PushAlarmOption.swift in Sources */,
6D26E0072F3C197F005097A4 /* SubwayInfoRepository.swift in Sources */,
B65C13082E057C590016D2F0 /* APIService.swift in Sources */,
6DC617A92F610238002DD641 /* HomeArrivalManager.swift in Sources */,
6D6879D02E4211B800E59C55 /* HomePatchRequest.swift in Sources */,
6D9283742E3AFF6A0090889B /* BusRouteCell.swift in Sources */,
6D73EB5F2E16120200F8DF8B /* CourseSearchViewModel.swift in Sources */,
Expand Down Expand Up @@ -2281,6 +2308,7 @@
6DADA5972EA09BA400CA9BE2 /* AmplitudeManager.swift in Sources */,
6DD632C22E544DB500C6A66E /* PushAlarmBottomView.swift in Sources */,
6D5E03D02E28853E0065AFBE /* CourseSearchResponse.swift in Sources */,
6DC617A72F60EB1A002DD641 /* LocationSmoother.swift in Sources */,
6D91A8E62E29F5BC0081BAFC /* CourseSettingViewModel.swift in Sources */,
6D26E0002F3C17C3005097A4 /* SubwayRealTimeInfoRequest.swift in Sources */,
B673C4912E0424FD00EE4AD0 /* SplashViewModel.swift in Sources */,
Expand Down Expand Up @@ -2460,7 +2488,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 15;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 23SCTLK482;
FRAMEWORK_SEARCH_PATHS = (
Expand All @@ -2483,7 +2511,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.7;
MARKETING_VERSION = 1.9;
PRODUCT_BUNDLE_IDENTIFIER = com.atcha.iOS;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
Expand All @@ -2507,7 +2535,7 @@
CODE_SIGN_ENTITLEMENTS = "Atcha-iOS/Atcha-iOS.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 15;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 23SCTLK482;
EXCLUDED_ARCHS = "";
FRAMEWORK_SEARCH_PATHS = (
Expand All @@ -2530,7 +2558,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.7;
MARKETING_VERSION = 1.9;
OTHER_SWIFT_FLAGS = "";
PRODUCT_BUNDLE_IDENTIFIER = com.atcha.iOS;
PRODUCT_NAME = "$(TARGET_NAME)";
Expand All @@ -2555,7 +2583,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 15;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 23SCTLK482;
FRAMEWORK_SEARCH_PATHS = (
Expand All @@ -2578,7 +2606,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.7;
MARKETING_VERSION = 1.9;
PRODUCT_BUNDLE_IDENTIFIER = com.atcha.iOS;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
Expand Down
11 changes: 11 additions & 0 deletions Atcha-iOS/App/DIContainer/Main/MainDIContainer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ final class MainDIContainer {
LoginDIContainer(apiService: apiService)
}()

private lazy var homeRegisterDI: HomeRegisterDIContainer = {
HomeRegisterDIContainer(apiService: apiService, locationStateHolder: locationStateHolder)
}()

init(apiService: APIService, locationStateHolder: LocationStateHolder) {
self.apiService = apiService
self.locationStateHolder = locationStateHolder
Expand Down Expand Up @@ -128,3 +132,10 @@ extension MainDIContainer{
}
}


// MARK: - HomeRegister
extension MainDIContainer{
func makeHomeRegisterDIContainer() -> HomeRegisterDIContainer {
return homeRegisterDI
}
}
51 changes: 49 additions & 2 deletions Atcha-iOS/Core/Manager/AlarmManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ final class AlarmManager {
private var shouldKeepBackgroundAudio = false
private var isPreviewing = false
private var hapticEngine: CHHapticEngine?
private var autoStopWorkItem: DispatchWorkItem?

// MARK: - Init
private init() {
Expand Down Expand Up @@ -71,6 +72,8 @@ final class AlarmManager {
/// 서버에서 받은 출발 시각 기준으로 1분 전에 반복 푸시/사운드/진동을 시작
func startAlarm(title: String, body: String) {
// 기존 알람 상태만 정리 (silent는 유지 or 다시 켜기)
autoStopWorkItem?.cancel()
autoStopWorkItem = nil
stopAlarm(keepSilent: true)
ensureBackgroundSilentRunning()
applySavedVolumeForAlarmStart()
Expand Down Expand Up @@ -99,6 +102,9 @@ final class AlarmManager {

/// 완전 정지: 예약/타이머/진동/알림/오디오 모두 끊기
func stopAlarm(keepSilent: Bool = false) {
autoStopWorkItem?.cancel()
autoStopWorkItem = nil

shouldKeepBackgroundAudio = false
pendingStartWorkItem?.cancel()
pendingStartWorkItem = nil
Expand All @@ -121,6 +127,7 @@ final class AlarmManager {
}

print("알람 종료 (keepSilent = \(keepSilent))")
UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: [AlarmNotificationID.autoStopInfo])
}

func alarmInit() {
Expand Down Expand Up @@ -220,13 +227,19 @@ extension AlarmManager {

self.sendImmediateLocalPush(title: title, body: body)
}

scheduleAutoStop()
}

func sendImmediateLocalPush(title: String, body: String) {
func sendImmediateLocalPush(title: String, body: String, playSound: Bool = false) {
let content = UNMutableNotificationContent()
content.title = title
content.body = body
content.sound = nil // 사운드는 직접 재생 중
if playSound {
content.sound = .default
} else {
content.sound = nil
}

let request = UNNotificationRequest(
identifier: UUID().uuidString,
Expand Down Expand Up @@ -593,3 +606,37 @@ extension AlarmManager {
print("미리듣기 종료 (keep=\(shouldKeepBackgroundAudio))")
}
}

extension AlarmManager {
private func scheduleAutoStop() {
autoStopWorkItem?.cancel()

let content = UNMutableNotificationContent()
content.title = "출발 알람이 자동 종료되었어요"
content.body = "클릭해서 경로 재탐색하기"
content.sound = .default

let request = UNNotificationRequest(
identifier: AlarmNotificationID.autoStopInfo,
content: content,
trigger: UNTimeIntervalNotificationTrigger(timeInterval: 120.0, repeats: false)
)
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)

// 2. 앱 내부의 정지 로직 및 팝업 신호 (포그라운드일 때 즉시, 백그라운드면 켜질 때 실행됨)
let workItem = DispatchWorkItem { [weak self] in
guard let self = self else { return }

// 음악/진동 정지
self.stopAlarm(keepSilent: false) // 아예 무음까지 끄기

UserDefaults.standard.set(true, forKey: "isAlarmTimedOut")

// 메인 뷰에 타임아웃 팝업 띄우라고 신호
NotificationCenter.default.post(name: NSNotification.Name("alarmDidTimeout"), object: nil)
}

autoStopWorkItem = workItem
DispatchQueue.main.asyncAfter(deadline: .now() + 120.0, execute: workItem)
}
}
Loading
Loading