Skip to content

Commit 41ff16a

Browse files
authored
[#399] 위젯 탭 내비게이션을 연결한다 (#404)
* feat: Today 위젯을 탭했을 시 TodayTodoView로 내비게이션 되도록 구현 * feat: 히트맵 위젯을 탭했을 시 ProfileView로 내비게이션 되도록 구현 * fix: 로그아웃 상태에서 위젯을 탭해 앱에 들어간 후 로그인 하면 해당 해당 탭으로 들어가지는 현상 제거 * refactor: 강제 옵셔널 언래핑 제거
1 parent 6b37a8a commit 41ff16a

6 files changed

Lines changed: 85 additions & 4 deletions

File tree

DevLog/App/RootView.swift

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,19 @@ struct RootView: View {
1111
@Environment(\.diContainer) var container: DIContainer
1212
@State var viewModel: RootViewModel
1313
@State private var selectedRoute: AppRoute?
14+
@State private var selectedMainTab = MainTab.home
1415

1516
var body: some View {
1617
ZStack {
1718
Color(UIColor.systemGroupedBackground).ignoresSafeArea()
1819
if let signIn = viewModel.state.signIn {
1920
if signIn {
20-
MainView(viewModel: MainViewModel(
21-
unreadPushCountUseCase: container.resolve(ObserveUnreadPushCountUseCase.self)
22-
))
21+
MainView(
22+
viewModel: MainViewModel(
23+
unreadPushCountUseCase: container.resolve(ObserveUnreadPushCountUseCase.self)
24+
),
25+
selectedTab: $selectedMainTab
26+
)
2327
} else {
2428
LoginView(viewModel: LoginViewModel(
2529
signInUseCase: container.resolve(SignInUseCase.self))
@@ -29,6 +33,19 @@ struct RootView: View {
2933
}
3034
.preferredColorScheme(viewModel.state.theme.colorScheme)
3135
.onAppear { viewModel.send(.onAppear) }
36+
.onChange(of: viewModel.state.signIn) { _, value in
37+
guard value == false else { return }
38+
selectedMainTab = .home
39+
}
40+
.onOpenURL { url in
41+
guard let mainTab = MainTab(widgetURL: url) else { return }
42+
switch viewModel.state.signIn {
43+
case .some(false):
44+
selectedMainTab = .home
45+
case .some(true), .none:
46+
selectedMainTab = mainTab
47+
}
48+
}
3249
.alert(viewModel.state.alertTitle, isPresented: Binding(
3350
get: { viewModel.state.showAlert },
3451
set: { viewModel.send(.setAlert($0)) }

DevLog/App/Routing/MainTab.swift

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//
2+
// MainTab.swift
3+
// DevLog
4+
//
5+
// Created by opfic on 4/30/26.
6+
//
7+
8+
import Foundation
9+
10+
enum MainTab: Hashable {
11+
case home
12+
case today
13+
case notification
14+
case profile
15+
16+
init?(widgetURL: URL) {
17+
guard widgetURL.scheme?.lowercased() == WidgetDeepLink.scheme.lowercased() else { return nil }
18+
19+
switch widgetURL.host {
20+
case WidgetDeepLink.todayTodoHost:
21+
self = .today
22+
case WidgetDeepLink.heatmapHost:
23+
self = .profile
24+
default:
25+
return nil
26+
}
27+
}
28+
}

DevLog/UI/Common/MainView.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@ import SwiftUI
1010
struct MainView: View {
1111
@Environment(\.diContainer) var container: DIContainer
1212
@State var viewModel: MainViewModel
13+
@Binding var selectedTab: MainTab
1314

1415
var body: some View {
15-
TabView {
16+
TabView(selection: $selectedTab) {
1617
HomeView(viewModel: HomeViewModel(
1718
fetchPreferencesUseCase: container.resolve(FetchTodoCategoryPreferencesUseCase.self),
1819
updatePreferencesUseCase: container.resolve(UpdateTodoCategoryPreferencesUseCase.self),
@@ -28,6 +29,7 @@ struct MainView: View {
2829
Image(systemName: "house.fill")
2930
Text(String(localized: "nav_home"))
3031
}
32+
.tag(MainTab.home)
3133
TodayView(viewModel: TodayViewModel(
3234
fetchTodosUseCase: container.resolve(FetchTodosUseCase.self),
3335
fetchTodoByIdUseCase: container.resolve(FetchTodoByIdUseCase.self),
@@ -39,6 +41,7 @@ struct MainView: View {
3941
Image(systemName: "sun.max.fill")
4042
Text(String(localized: "nav_today"))
4143
}
44+
.tag(MainTab.today)
4245
PushNotificationListView(viewModel: PushNotificationListViewModel(
4346
fetchUseCase: container.resolve(FetchPushNotificationsUseCase.self),
4447
deleteUseCase: container.resolve(DeletePushNotificationUseCase.self),
@@ -52,6 +55,7 @@ struct MainView: View {
5255
Text(String(localized: "nav_notifications"))
5356
}
5457
.badge(viewModel.state.unreadPushCount)
58+
.tag(MainTab.notification)
5559
ProfileView(viewModel: ProfileViewModel(
5660
fetchUserDataUseCase: container.resolve(FetchUserDataUseCase.self),
5761
fetchTodosUseCase: container.resolve(FetchTodosUseCase.self),
@@ -64,6 +68,7 @@ struct MainView: View {
6468
Image(systemName: "person.crop.circle.fill")
6569
Text(String(localized: "nav_profile"))
6670
}
71+
.tag(MainTab.profile)
6772
}
6873
.onAppear {
6974
viewModel.send(.onAppear)

DevLogWidget/Heatmap/HeatmapWidget.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ struct HeatmapWidget: Widget {
2020
) { entry in
2121
HeatmapWidgetEntryView(entry: entry)
2222
.containerBackground(.fill.tertiary, for: .widget)
23+
.widgetURL(WidgetDeepLink.heatmapURL)
2324
}
2425
.configurationDisplayName("Heatmap")
2526
.description("활동 히트맵을 표시합니다.")

DevLogWidget/Today/TodayTodoWidget.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ struct TodayTodoWidget: Widget {
2020
) { entry in
2121
TodayTodoWidgetEntryView(entry: entry)
2222
.containerBackground(.fill.tertiary, for: .widget)
23+
.widgetURL(WidgetDeepLink.todayTodoURL)
2324
}
2425
.description("오늘 기준 Todo 목록을 표시합니다.")
2526
.configurationDisplayName("Today")

WidgetShared/WidgetDeepLink.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//
2+
// WidgetDeepLink.swift
3+
// DevLog
4+
//
5+
// Created by opfic on 4/30/26.
6+
//
7+
8+
import Foundation
9+
10+
enum WidgetDeepLink {
11+
static let scheme = "DevLog"
12+
static let todayTodoHost = "today"
13+
static let heatmapHost = "profile"
14+
15+
static var todayTodoURL: URL? {
16+
url(host: todayTodoHost)
17+
}
18+
19+
static var heatmapURL: URL? {
20+
url(host: heatmapHost)
21+
}
22+
23+
private static func url(host: String) -> URL? {
24+
var urlComponents = URLComponents()
25+
urlComponents.scheme = scheme
26+
urlComponents.host = host
27+
return urlComponents.url
28+
}
29+
}

0 commit comments

Comments
 (0)