Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
aaa6fde
[fix] 정류장 파싱 시 조건 완화
ela-lee Dec 18, 2025
50fb0ce
[fix] TAGO 보정시 프로그레스바 진행률도 반영되도록 수정
ela-lee Dec 18, 2025
b805755
[fix] 라액 UI수정
minjeongkx Dec 21, 2025
54e28c6
Merge branch 'fix/#268/missing-bus-stop-crash' into feat/#272/seoul-b…
ela-lee Dec 22, 2025
3580f2b
[feat]#272 버스도착정보 프로토콜
ela-lee Dec 22, 2025
451c1f4
[feat]#272 서울시 citycode추가
ela-lee Dec 22, 2025
ebf5c78
[fix] 라액 함수 수정
llj72 Dec 22, 2025
64ca3fb
[fix]라액 아이콘 수정
minjeongkx Dec 22, 2025
6ee9337
[chore] 코드 주석처리
ela-lee Dec 30, 2025
21166ce
[feat]#272 http 연결 허용
ela-lee Dec 30, 2025
a0b1d30
[feat]#272 서울버스정류장 CVS 파일 추가
ela-lee Dec 30, 2025
413867b
[feat]#272 도시코드에 따라 사용할 섭비스 분리, 앱 시작시 CVS 로딩
ela-lee Dec 30, 2025
961f6fd
[feat]#272 서울버스도착정보 모델 추가
ela-lee Dec 30, 2025
6992d3c
[feat]#272 이름 변경 및 타입 추가
ela-lee Dec 30, 2025
c26597a
[feat]#272 서울버스도착정보 호출
ela-lee Dec 30, 2025
ed2dd06
[feat]#272서울 버스 도착 조회를 위해 stId 기반 노선 매칭 로직 추가
ela-lee Dec 30, 2025
5ad0a24
[feat]#272 서울시 버스 도착정보 전용 처리 로직 추가
ela-lee Dec 30, 2025
e19b295
[fix] 라액 UI수정
minjeongkx Dec 21, 2025
52334a0
[fix]라액 아이콘 수정
minjeongkx Dec 22, 2025
c1ae026
Merge branch 'fix/#271/liveactivityUI' into fix/#273/LiveActivity-Sta…
llj72 Jan 1, 2026
405f364
[feat] 라액 버스 도착예정시간 함수 추가
llj72 Jan 1, 2026
9e3126e
💄 에러뷰 ui 구현 완료
alice0047 Jan 1, 2026
bb415b5
[fix] 지나갔어요 업데이트 (임시저장)
llj72 Jan 3, 2026
7209d76
[fix] #272 지나갔어요/곧 도착해요 업데이트
llj72 Jan 3, 2026
0af010d
좌/우회전 음성안내 기능 제거
nan-park Jan 4, 2026
91729e1
좌표 접근 판단 임계값 15m -> 3m
nan-park Jan 4, 2026
b09b6a2
Merge pull request #279 from DeveloperAcademy-POSTECH/feat/#278/stabi…
ela-lee Jan 5, 2026
4db33a5
Merge pull request #276 from DeveloperAcademy-POSTECH/fix/#273/LiveAc…
ela-lee Jan 5, 2026
24856a2
Merge pull request #275 from DeveloperAcademy-POSTECH/feat/#272/seoul…
ela-lee Jan 5, 2026
6e7b347
Merge pull request #274 from DeveloperAcademy-POSTECH/fix/#271/liveac…
ela-lee Jan 5, 2026
df79746
[fix] 중복된 코드 삭제
ela-lee Jan 5, 2026
9fa10eb
Merge branch 'dev' into feat/#280/sound-setting
ela-lee Jan 5, 2026
9a7cc8f
[fix] 라이브액티비티 함수 변수 추가
ela-lee Jan 5, 2026
0b79fe5
Merge branch 'dev' into feat/#280/sound-setting
ela-lee Jan 5, 2026
b12aeea
Add Secrets.plist to project resources
nan-park Jan 7, 2026
a08e38e
Merge pull request #282 from DeveloperAcademy-POSTECH/fix/#281/bug-ap…
nan-park Jan 7, 2026
5463495
✨ [feat] 도보 경로 전용 카드 추가 및 기능 구현
alice0047 Jan 8, 2026
dbebf93
텍스트 색상 지정
alice0047 Jan 9, 2026
c19f9f7
[fix] 텍스트 색상 지정
alice0047 Jan 9, 2026
3c9c113
[fix] 도보 경로일 때, '도보' 텍스트 나오도록 분기처리
alice0047 Jan 9, 2026
58c98be
Merge remote-tracking branch 'origin/fix/#277/errorViewFix' into fix/…
alice0047 Jan 9, 2026
08c1730
Merge branch 'dev' into feat/#280/sound-setting
ela-lee Jan 13, 2026
61e9a32
[feat]#280 사운드 설정탭
ela-lee Jan 14, 2026
195eade
도보 카드 패딩값 변경
alice0047 Jan 14, 2026
aa3ce63
글자 크기 변경
alice0047 Jan 14, 2026
26c4931
[feat]#280 툴팁추가
ela-lee Jan 15, 2026
109a299
[feat]#280 무음모드일때 소리 안나도록 수정
ela-lee Jan 15, 2026
d31538b
[fix] 툴팁 기본상태 false로 수정
ela-lee Jan 15, 2026
2d43f29
[feat] 뒤로가기 제스처, 내비게이션바 추가, 아이콘 패딩 수정
nan-park Jan 15, 2026
c247fc1
[feat]#280 텍스트 색상 설정 및 툴팁 위치 수정
ela-lee Jan 15, 2026
7add726
Merge remote-tracking branch 'origin/feat/#286/settingsview-navigatio…
ela-lee Jan 15, 2026
c04ed0b
[fix] 텍스트 색상 및 간격 수정
alice0047 Jan 15, 2026
728f049
[feat] 도보 좌표 도착 인식 3m->7m
nan-park Jan 15, 2026
4d68908
Merge pull request #287 from DeveloperAcademy-POSTECH/feat/#286/setti…
nan-park Jan 16, 2026
349363d
Merge pull request #284 from DeveloperAcademy-POSTECH/fix/#277/errorV…
nan-park Jan 16, 2026
4081a19
Merge pull request #285 from DeveloperAcademy-POSTECH/feat/#280/sound…
nan-park Jan 16, 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
13 changes: 9 additions & 4 deletions BusRoad/Application/BusRoadApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,19 @@ struct BusRoadApp: App {
@StateObject var coordinator = NavigationCoordinator()
@StateObject private var proximityManager = AlightProximityManager(
locationService: LocationService.shared,
journeyManager: JourneyManager.shared,
voiceManager: VoiceAnnouncementManager()
)
journeyManager: JourneyManager.shared,
voiceManager: VoiceAnnouncementManager()
)

init() {
_ = BusDataManager.shared
}

var body: some Scene {
WindowGroup {
AppNavigationView()
.environmentObject(coordinator)
.environmentObject(proximityManager)
.environmentObject(proximityManager)
}
}
}
11 changes: 11 additions & 0 deletions BusRoad/Component/Common/SettingKeys.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

enum SettingsKeys {
// 음성
static let busArrivalVoice = "busArrivalVoiceEnabled" // 버스 승차
static let busAlightVoice = "busAlightVoiceEnabled" // 버스 하차
static let walkingVoice = "walkingVoiceEnabled" // 도보 안내

// 진동
static let vibration = "vibrationEnabled"
}

14 changes: 9 additions & 5 deletions BusRoad/Component/RouteSuggestionView/ETA.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,18 @@ struct ETA: View {
var timeText: String {
let hours = journey.totalTime / 60
let minutes = journey.totalTime % 60


let base: String
if hours > 0 && minutes > 0 {
return "\(hours)시간 \(minutes)분"
} else if hours > 0 && minutes == 0 {
return "\(hours)시간"
base = "\(hours)시간 \(minutes)분"
} else if hours > 0 {
base = "\(hours)시간"
} else {
return "\(minutes)분"
base = "\(minutes)분"
}

// ✅ 도보-only면 "도보 " 붙임
return journey.isWalkingOnly ? "도보 \(base)" : base
}

var isMinimumTransfer: Bool {
Expand Down
92 changes: 46 additions & 46 deletions BusRoad/Component/RouteSuggestionView/RouteCard.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,49 +57,49 @@ struct RouteCard: View {



#Preview {
@Previewable var previewBusInfo: (busNo: String, arrivalText: String)? = nil

RouteCard(
viewModel: BusRouteViewModel(),
allJourneys: [
Journey(totalTime: 48, nodes: [
.walk(WalkRouteNode(
start: LocationInfo(name: "서울역", latitude: 37.55, longitude: 126.97),
end: LocationInfo(name: "강남역", latitude: 37.49, longitude: 127.02),
travelTime: 5
)),
.bus(BusRouteNode(
start: LocationInfo(name: "서울역", latitude: 37.55, longitude: 126.97),
end: LocationInfo(name: "강남역", latitude: 37.49, longitude: 127.02),
busNo: ["405", "472"],
busId: [1001, 1002],
stations: [
BusStation(index: 0, stationId: 111, stationName: "서울역", stationCityCode: 1100, localStationId: "LOCAL-SEOUL-001", nodeId: "1000001", latitude: 37.55, longitude: 126.97),
BusStation(index: 1, stationId: 222, stationName: "강남역", stationCityCode: 1100, localStationId: "LOCAL-GANGNAM-001", nodeId: "1000002", latitude: 37.49, longitude: 127.02)
],
travelTime: 35
))
])
],
journey: Journey(totalTime: 48, nodes: [
.walk(WalkRouteNode(
start: LocationInfo(name: "서울역", latitude: 37.55, longitude: 126.97),
end: LocationInfo(name: "강남역", latitude: 37.49, longitude: 127.02),
travelTime: 5
)),
.bus(BusRouteNode(
start: LocationInfo(name: "서울역", latitude: 37.55, longitude: 126.97),
end: LocationInfo(name: "강남역", latitude: 37.49, longitude: 127.02),
busNo: ["472"],
busId: [1001],
stations: [
BusStation(index: 0, stationId: 111, stationName: "서울역", stationCityCode: 1100, localStationId: "LOCAL-SEOUL-001", nodeId: "1000001", latitude: 37.55, longitude: 126.97),
BusStation(index: 1, stationId: 222, stationName: "강남역", stationCityCode: 1100, localStationId: "LOCAL-GANGNAM-001", nodeId: "1000002", latitude: 37.49, longitude: 127.02)
],
travelTime: 35
))
]),
index: 0
)
}
//#Preview {
// @Previewable var previewBusInfo: (busNo: String, arrivalText: String)? = nil
//
// RouteCard(
// viewModel: BusRouteViewModel(),
// allJourneys: [
// Journey(totalTime: 48, nodes: [
// .walk(WalkRouteNode(
// start: LocationInfo(name: "서울역", latitude: 37.55, longitude: 126.97),
// end: LocationInfo(name: "강남역", latitude: 37.49, longitude: 127.02),
// travelTime: 5
// )),
// .bus(BusRouteNode(
// start: LocationInfo(name: "서울역", latitude: 37.55, longitude: 126.97),
// end: LocationInfo(name: "강남역", latitude: 37.49, longitude: 127.02),
// busNo: ["405", "472"],
// busId: [1001, 1002],
// stations: [
// BusStation(index: 0, stationId: 111, stationName: "서울역", stationCityCode: 1100, localStationId: "LOCAL-SEOUL-001", nodeId: "1000001", latitude: 37.55, longitude: 126.97),
// BusStation(index: 1, stationId: 222, stationName: "강남역", stationCityCode: 1100, localStationId: "LOCAL-GANGNAM-001", nodeId: "1000002", latitude: 37.49, longitude: 127.02)
// ],
// travelTime: 35
// ))
// ])
// ],
// journey: Journey(totalTime: 48, nodes: [
// .walk(WalkRouteNode(
// start: LocationInfo(name: "서울역", latitude: 37.55, longitude: 126.97),
// end: LocationInfo(name: "강남역", latitude: 37.49, longitude: 127.02),
// travelTime: 5
// )),
// .bus(BusRouteNode(
// start: LocationInfo(name: "서울역", latitude: 37.55, longitude: 126.97),
// end: LocationInfo(name: "강남역", latitude: 37.49, longitude: 127.02),
// busNo: ["472"],
// busId: [1001],
// stations: [
// BusStation(index: 0, stationId: 111, stationName: "서울역", stationCityCode: 1100, localStationId: "LOCAL-SEOUL-001", nodeId: "1000001", latitude: 37.55, longitude: 126.97),
// BusStation(index: 1, stationId: 222, stationName: "강남역", stationCityCode: 1100, localStationId: "LOCAL-GANGNAM-001", nodeId: "1000002", latitude: 37.49, longitude: 127.02)
// ],
// travelTime: 35
// ))
// ]),
// index: 0
// )
//}
16 changes: 11 additions & 5 deletions BusRoad/Component/RouteSuggestionView/RouteCardSlide.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,18 @@ struct RouteCardSlide: View {
@Binding var currentIndex: Int
@Binding var routes: [Journey]?
@ObservedObject var viewModel: BusRouteViewModel

var body: some View {
VStack(spacing: 0) {
ZStack {
if viewModel.errorMessage != nil {
RouteErrorCard(viewModel: viewModel)
if viewModel.errorMessage == "출발지와 목적지가 너무 가깝습니다." {
if let journey = routes?.first {
RouteWalkCard(viewModel: viewModel, journey: journey)
}
} else {
RouteErrorCard(viewModel: viewModel)
}
} else if let routes {
ZStack {
ForEach(Array(routes.enumerated()), id: \.element.id) { index, item in
Expand Down Expand Up @@ -39,19 +45,19 @@ struct RouteCardSlide: View {
} else {
newIndex = min(routes.count - 1, currentIndex + 1)
}

if newIndex != currentIndex {
viewModel.arrivalText = nil
}

currentIndex = newIndex
}
)
} else {
ProgressRouteCard()
}
}

// 항상 28 높이의 공간 차지
if let routes = routes, routes.count > 1 {
HStack {
Expand Down
117 changes: 72 additions & 45 deletions BusRoad/Component/RouteSuggestionView/RouteErrorCard.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,53 +16,80 @@ struct RouteErrorCard: View {
.foregroundColor(Color.primarywhite)
.cornerRadius(20)
.shadow(color: .black.opacity(0.25), radius: 2, x: 0, y: 0)


VStack(spacing: 8) {
if viewModel.errorMessage == "출발지와 목적지가 너무 가깝습니다." {
Text("출발지와 도착지가\n가까이 있어요!")
.font(.presemi24)
.foregroundColor(Color.primaryHeavy)
.multilineTextAlignment(.center)
.lineSpacing(5)

Text("도보 경로로 안내할게요.")
.font(.premed20)
.foregroundColor(Color.primaryHeavy)

} else if viewModel.errorMessage == "지원하지 않는 교통수단이 포함되어 있습니다." {
Text("현재 경로는\n지원하지 않아요😵")
.font(.presemi24)
.foregroundColor(Color.primaryHeavy)
.multilineTextAlignment(.center)
.lineSpacing(5)

Text("다른 장소를 검색해주세요.")
.font(.premed20)
.foregroundColor(Color.primaryHeavy)

} else if viewModel.errorMessage == "출발지와 도착지가 같습니다." {
Text("출발지와 도착지가\n같은 곳이에요😵")
.font(.presemi24)
.foregroundColor(Color.primaryHeavy)
.multilineTextAlignment(.center)
.lineSpacing(5)

Text("다시 검색해주세요.")
.font(.premed20)
.foregroundColor(Color.primaryHeavy)

} else {
Text("앗, 문제가 발생했어요😵")
.font(.presemi24)
.foregroundColor(Color.primaryHeavy)
.multilineTextAlignment(.center)
.lineSpacing(5)

Text("경로를 새로고침 해주세요.")
.font(.premed20)
.foregroundColor(Color.primaryHeavy)
}
VStack(spacing: 28) {

Image(systemName: "exclamationmark.circle")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 36, height: 36)
.font(.system(size: 36, weight: .light))
.foregroundStyle(.greyDisable)

errorMessageText
}
}
}

//MARK: - 에러메시지 텍스트
private var errorMessageText: some View {
VStack(alignment: .center, spacing: 12, content: {
if viewModel.errorMessage == "지원하지 않는 교통수단이 포함되어 있습니다." {
Text("지원하지 않는 경로예요")
.font(.presemi24)
.foregroundStyle(Color.primaryHeavy)
.multilineTextAlignment(.center)
.lineSpacing(5)

Text("다른 장소를 검색해주세요")
.font(.premed16Scaled)
.foregroundStyle(.greyNormal)

} else if viewModel.errorMessage == "출발지와 도착지가 같습니다." {
Text("출발지와 도착지가\n동일해요")
.font(.presemi24)
.foregroundStyle(Color.primaryHeavy)
.multilineTextAlignment(.center)
.lineSpacing(5)

Text("다른 장소를 검색해주세요")
.font(.premed16Scaled)
.foregroundStyle(Color.greyNormal)

} else {
Text("오류가 발생했어요")
.font(.presemi24)
.foregroundStyle(Color.primaryHeavy)
.multilineTextAlignment(.center)
.lineSpacing(5)

Text("경로를 새로고침 해주세요")
.font(.premed16Scaled)
.foregroundStyle(Color.greyNormal)
}
})
}
}

// MARK: - 프리뷰
#Preview("지원하지 않는 교통수단") {
let vm = BusRouteViewModel()
vm.errorMessage = "지원하지 않는 교통수단이 포함되어 있습니다."
return RouteErrorCard(viewModel: vm)
.padding()
}

#Preview("출발지=도착지") {
let vm = BusRouteViewModel()
vm.errorMessage = "출발지와 도착지가 같습니다."
return RouteErrorCard(viewModel: vm)
.padding()
}

#Preview("기타 오류") {
let vm = BusRouteViewModel()
vm.errorMessage = "API 호출 실패"
return RouteErrorCard(viewModel: vm)
.padding()
}
14 changes: 7 additions & 7 deletions BusRoad/Component/RouteSuggestionView/RouteSelectButton.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ struct RouteSelectButton: View {
stage: RouteStage.walkingToBus.rawValue,
destination: walkNode.end.name, // 승차 정류장 이름
remainingBusStops: 0,
busTravelTime: 0
timeTillBusArrival: 0
)
}
print("[DEBUG] Live Activity 시작 - walkingToBus, destination: \(walkNode.end.name)")
Expand All @@ -44,7 +44,7 @@ struct RouteSelectButton: View {
stage: RouteStage.waitingForBus.rawValue,
destination: destinationName,
remainingBusStops: busNode.stations.count,
busTravelTime: busNode.travelTime
timeTillBusArrival: ArrivalInfoManager.shared.lastNearestArrTime ?? 0
)
print("[DEBUG] Live Activity 시작 - waitingForBus, destination: \(destinationName)")
}
Expand All @@ -55,7 +55,7 @@ struct RouteSelectButton: View {
print("[DEBUG] routes가 존재하지 않습니다.")
}
} label: {
Text("시작하기")
Text("안내 시작")
.foregroundColor(Color.subLight)
.font(.premed32)
.frame(width: 305.wScaled, height: 64)
Expand All @@ -68,7 +68,7 @@ struct RouteSelectButton: View {
Button {
retrySearch()
} label: {
Text("다시 검색하기")
Text("다시 검색")
.foregroundColor(Color.subLight)
.font(.premed32)
.frame(width: 305.wScaled, height: 64)
Expand All @@ -89,13 +89,13 @@ struct RouteSelectButton: View {
stage: RouteStage.walkingToDestination.rawValue,
destination: node.end.name,
remainingBusStops: 0,
busTravelTime: 0
timeTillBusArrival: 0
)
}
}

} label: {
Text("도보 이동하기")
Text("안내 시작")
.foregroundColor(Color.subLight)
.font(.premed32)
.frame(width: 305.wScaled, height: 64)
Expand All @@ -108,7 +108,7 @@ struct RouteSelectButton: View {
Button {
retrySearch()
} label: {
Text("다시 검색하기")
Text("다시 검색")
.foregroundColor(Color.subLight)
.font(.premed32)
.frame(width: 305.wScaled, height: 64)
Expand Down
1 change: 1 addition & 0 deletions BusRoad/Component/RouteSuggestionView/RouteSummary.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ struct RouteSummary: View {
.frame(width:12.wScaled.minimum(12), height:16.wScaled.minimum(16))
.foregroundColor(Color.subStrong)
}

Text("도보 \(journey.walkingTime)분")
.font(.prereg16Scaled)
.foregroundColor(Color.primaryHeavy)
Expand Down
Loading