Skip to content

[Feat] #501 - 탭바 화면 전환 구현 #503

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Feb 22, 2025
Merged

Conversation

dlwogus0128
Copy link
Contributor

@dlwogus0128 dlwogus0128 commented Feb 20, 2025

🌴 PR 요약

탭바 화면 전환 구현을 구현했습니다.

🌱 작업한 브랜치

🌱 PR Point

  • 구조가 잘 머릿속에 안 그려져서 대충이라도 다이어그램을 그려놨는데 이해에 도움이 되셨으면 좋겠습니다..
  • 요약하자면 아래 그림처럼 ApplicationCoor의 tabbarFlow 안에서 아이템에 대한 coordinator들을 생성하고, 해당 아이템들의 coordinator에 접근해서 VC들을 가져와, 탭바 아이템으로 등록해주고 있습니다.
  • 이렇게 하지 않고 그냥 VC를 생성해 등록해버리면, 하위 코디네이터들의 플로우 실행이 불가능해요. (적어도 제가 했을때는..)
  • 아이템 등록하는 거는 루트 피처에서 아이템 enum 만들어서 하면 좀 더 간결해질 것 같은데.. 지저분하지만 일단 이렇게 둘게요
  • 좀 더 자세히 보자면, 아래 runTabBarFlow 내부만 표현해보았는데요.
  • 계속 헷갈렸던 게 첨에는 그냥 탭바 switch문 안에서 다른 플로우들을 시도했거든요. (runSoptampFlow 등) 그게 안되는 이유가 각 아이템들의 coordinator로부터 요청을 받아야 해서였어요
  • 그래서 생성된 아이템 coordinator들에 대해서 각각 받고 있는데, 다시 봐도 코드가 아쉽네요..
  • 모든 로직을 ApplicationCoordinator가 아니라 TabbarCoor가 갖고 있는 것도 생각해봤는데요.. 흠 그럼 탭바 피처가 다른 피처들한테 강한 의존성이 생겨서 포기했습니다

📌 참고 사항

📸 스크린샷

기능 스크린샷
Tabbar
Simulator.Screen.Recording.-.iPhone.16.Pro.Max.-.2025-02-20.at.21.14.40.mp4

📮 관련 이슈

@dlwogus0128 dlwogus0128 added Feat 새로운 기능 구현 재현✦ labels Feb 20, 2025
@dlwogus0128 dlwogus0128 self-assigned this Feb 20, 2025
Copy link

height bot commented Feb 20, 2025

Link Height tasks by mentioning a task ID in the pull request title or commit messages, or description and comments with the keyword link (e.g. "Link T-123").

💡Tip: You can also use "Close T-X" to automatically close a task when the pull request is merged.

Copy link
Contributor

@yungu0010 yungu0010 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

어제 될 듯 말 듯 안돼서 마음이 안 좋았는데 너무 . . 감사하고 . .고생하셨고 .. . 또 고생하셨습니다!!! 😥
재현님이 그려주신 다이어그램과 코드를 보니 제가 어제 잘못 생각하고 구현했던 부분들이 어딘지 이해가 되어요(친절한 그림 최고 ,, )

글고 라우터가 들고 있는 함수들 종류가 많아서 엄청 혼란스러웠는데 저만 그런 거 아니겠죠? ! (하하)

저도 동의합니다 !! 코디네이터 리팩토링 하면서 불필요한 것들을 제거해보아요!

딥링크 사용하는 쪽에서 살펴봐야 할 부분들이 있어 보이는데, QA에서 백퍼센트 걸릴 것 같구요
(글고 푸시알림 테스트해봤는데 안되네요.. 꼭 다시 보겠습니다)

푸시알림 관련 기능이 안된다는걸까요 ? 맞다면 내일 개인일정 끝나고 이어 작업해볼게요! ! (해당 PR 에서 이어 작업하겠습니다!)

@@ -35,7 +34,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
configureAPNs()

window = UIWindow(windowScene: scene)
window?.rootViewController = TabBarController(tabList: [UINavigationController(rootViewController: UIViewController()), UINavigationController(rootViewController: UIViewController())])
window?.rootViewController = rootController
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

헷 감사해요

let userType = type ?? UserDefaultKeyList.Auth.getUserType()

let homeCoordinator = runHomeFlow(type: userType)
guard let homeVC = homeCoordinator.rootViewController else { return }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

허어ㅓ어 이렇게 ,, 코디네이터를 통해서 생성해주어야했던거군요 ,, ,
어제 논의했던 방식 중에 Builder로 VC를 생성해서 TabBarCoordinator에 주입하는 방식은 각 탭의 코디네이터 내부에서의 VC와 탭바 VC가 달라서였을까요?

Copy link
Contributor Author

@dlwogus0128 dlwogus0128 Feb 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵 맞습니다! 이렇게 Coordinator 내부의 Builder로 생성해 접근해야 해당 코디네이터에서의 이벤트를 받아올 수 있어요

// 각 코디네이터 실행
coordinator.requestCoordinating = { [weak self] destination in
switch destination {
case .home:
// self?.runHomeFlow(type: type)
break
homeCoordinator.requestCoordinating = { [weak self, weak coordinator] destination in
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

runHomeFlow의 requestCoordinating이 재정의되고 있어서 runHomeFlow 내부에서 정의된 클로저는 사용되지 않는 것 같아요.(제가 이해한게 맞나요?ㅎ.ㅎ)
runHomeFlow를 제거하고 함수 내부에서 HomeCoordinator를 생성, 지금처럼 홈코디네이터의 클로저도 이 함수에서 정의하는 방법,
또는 runHomeFlow에서 정의된 클로저를 가져다 쓰는 것으로 개선하면 좋을거같은데 내일 일정 끝나고 다시 생각해보겠습니다!!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

runHomeFlow의 requestCoordinating이 재정의되고 있어서 runHomeFlow 내부에서 정의된 클로저는 사용되지 않는 것 같아요.(제가 이해한게 맞나요?ㅎ.ㅎ)

저도 처음에는 그렇게 생각했는데요. runHomeFlow 내에 클로저가 없으면 인덱스를 바꾸기 전까지 화면 전환이 안된답니다..

// runTabBarFlow
coordinator.requestCoordinating = { [weak self] destination in
  switch destination {
  case .home:
  ...

여기 탭바 코디네이터가 들고 있는 클로저는 인덱스를 바꾸는 순간에 호출이 되어요
그래서 아이템 인덱스를 바꾸기 전까지 홈이나 솝트로그 플로우가 호출이 안 돼서 그 전에 탭바에서 들고 있는 플로우에서도 클로저가 필요합니다.

runHomeFlow에서 정의된 클로저를 가져다 쓰는 것으로 개선하면 좋을거같은데

이런 방법도 있고 충분히 개선 방안이 있을 것 같아요! 저도 더 고민해볼게요

Comment on lines 58 to 59
self.rootController = soptlog.vc.asNavigationController
self.rootViewController = soptlog.vc.viewController
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기서 rootController랑 rootViewController는 다른건가요 ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

달라요 ㅎㅋㅋㅋㅋ

self.rootViewController = soptlog.vc.viewController이건 ApplicationCoor의 탭바 플로우가 참조해야 하는 soptlogVC이구요
self.rootController = soptlog.vc.asNavigationController 얘는 원래부터 있었던 것 같은데 지금 솝트로그는 뎁스를 더 타지않아서 필요 없어보이기는 했어요

제가 테스트하면서 이름을 좀 헷갈리게 지은 것 같아요.. 바꿔보겠습니다..

viewModel.selectedIndex = selectedIndex
guard let index = tabBar.items?.firstIndex(of: item) else { return }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

옹엉 ,,, 급하게 코드를 복기하느라 selectedIndex를 가져다 썼는데 selectedIndex로 접근하면 인덱스가 나오지 않나보군여,, 감사합니다!!!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

요거 한참 헤맸네욯 ㅋ ㅋ selectedIndex로 하면 업데이트가 잘 안되어서 탭이 엉뚱하게 바뀌더라구요 발견해서 다행!

@@ -12,11 +12,11 @@ import DSKit

final class TabBarController: UITabBarController {

private let tabList: [UINavigationController]
private let tabList: [UIViewController]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

어제 말씀드렸던 것처럼 탭 바와 내비게이션 컨트롤러를 사용하려면 각 탭의 VC를 NavigationController가 감싼 형태여야한다고 생각했는데, 어차피 ApplicationCoordinator에서 각 코디네이터를 실행해주기 때문에 그냥 VC를 주입해도 화면전환이 가능한건가요 ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네네 맞아요!!
router.replaceRootWindow(tabbarController, withAnimation: true, hideBar: true)
요기에서 탭바 자체를 네비로 감싸주고 있어요

Copy link
Contributor Author

@dlwogus0128 dlwogus0128 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

리뷰 감사해요옹~

let userType = type ?? UserDefaultKeyList.Auth.getUserType()

let homeCoordinator = runHomeFlow(type: userType)
guard let homeVC = homeCoordinator.rootViewController else { return }
Copy link
Contributor Author

@dlwogus0128 dlwogus0128 Feb 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵 맞습니다! 이렇게 Coordinator 내부의 Builder로 생성해 접근해야 해당 코디네이터에서의 이벤트를 받아올 수 있어요

// 각 코디네이터 실행
coordinator.requestCoordinating = { [weak self] destination in
switch destination {
case .home:
// self?.runHomeFlow(type: type)
break
homeCoordinator.requestCoordinating = { [weak self, weak coordinator] destination in
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

runHomeFlow의 requestCoordinating이 재정의되고 있어서 runHomeFlow 내부에서 정의된 클로저는 사용되지 않는 것 같아요.(제가 이해한게 맞나요?ㅎ.ㅎ)

저도 처음에는 그렇게 생각했는데요. runHomeFlow 내에 클로저가 없으면 인덱스를 바꾸기 전까지 화면 전환이 안된답니다..

// runTabBarFlow
coordinator.requestCoordinating = { [weak self] destination in
  switch destination {
  case .home:
  ...

여기 탭바 코디네이터가 들고 있는 클로저는 인덱스를 바꾸는 순간에 호출이 되어요
그래서 아이템 인덱스를 바꾸기 전까지 홈이나 솝트로그 플로우가 호출이 안 돼서 그 전에 탭바에서 들고 있는 플로우에서도 클로저가 필요합니다.

runHomeFlow에서 정의된 클로저를 가져다 쓰는 것으로 개선하면 좋을거같은데

이런 방법도 있고 충분히 개선 방안이 있을 것 같아요! 저도 더 고민해볼게요

Comment on lines 58 to 59
self.rootController = soptlog.vc.asNavigationController
self.rootViewController = soptlog.vc.viewController
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

달라요 ㅎㅋㅋㅋㅋ

self.rootViewController = soptlog.vc.viewController이건 ApplicationCoor의 탭바 플로우가 참조해야 하는 soptlogVC이구요
self.rootController = soptlog.vc.asNavigationController 얘는 원래부터 있었던 것 같은데 지금 솝트로그는 뎁스를 더 타지않아서 필요 없어보이기는 했어요

제가 테스트하면서 이름을 좀 헷갈리게 지은 것 같아요.. 바꿔보겠습니다..

@@ -12,11 +12,11 @@ import DSKit

final class TabBarController: UITabBarController {

private let tabList: [UINavigationController]
private let tabList: [UIViewController]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네네 맞아요!!
router.replaceRootWindow(tabbarController, withAnimation: true, hideBar: true)
요기에서 탭바 자체를 네비로 감싸주고 있어요

viewModel.selectedIndex = selectedIndex
guard let index = tabBar.items?.firstIndex(of: item) else { return }
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

요거 한참 헤맸네욯 ㅋ ㅋ selectedIndex로 하면 업데이트가 잘 안되어서 탭이 엉뚱하게 바뀌더라구요 발견해서 다행!

Copy link
Contributor

@meltsplit meltsplit left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

고생하셨습니다 차차 더 좋은 구조로 같이 발전시켜보죠!!👍

@yungu0010 yungu0010 self-assigned this Feb 21, 2025
@dlwogus0128
Copy link
Contributor Author

dlwogus0128 commented Feb 22, 2025

딥링크, 웹링크 핸들링 시 띄우고자 하는 플로우가 바로 dismiss 되는 경우에 대하여

스크린샷 2025-02-22 오후 2 59 27

해당 이슈에 대하여 위와 같이 해결했습니다. @yungu0010 @meltsplit

현재 ApplicationCoor가 모든 플로우를 관리하고 있기 때문에 HomeCoor, SoptlogCoor의 상위 rootController가 ApplicationCoor와 같을 경우, ApplicationCoor에서 router.dismiss를 실행할 시 (딥링크, 웹링크 핸들링 시 기존 실행되고 있는 모듈을 dismiss하기 위함) 바로 플로우가 내려갑니다.

때문에 ApplicationCoor 또한 self.rootController = tabbarVC를 NavigationController로 감싼 것으로 설정함으로써, HomeCoor, SoptlogCoor가 해당 네비컨을 root controller로 바라보게 합니다. 이러면 router.dismiss를 하더라도 플로우가 내려가지 않습니다.

이에 따라 HomeCoor에서 뎁스를 한 번 더 들어가 push를 하고 있던 CalendarDetail, Soptlog에서의 웹뷰 또한 ApplicationCoor에서 push, present 해줘야 하기 때문에 destination enum을 만들어서 클로저로 ApplicationCoor에게 화면 전환을 요청하고 있습니다.

다만 해당 구조가 기존 코디네이터 설계와 어긋나진 않는지, 탭바 모듈이 다른 피처 모듈들과 같은 계층에 있는 것이 맞는지에 관해서는 다시 고민해야 할 것 같습니다.

@yungu0010 yungu0010 merged commit c81e693 into develop Feb 22, 2025
@yungu0010 yungu0010 deleted the feat/#501-tabbar-flow branch February 22, 2025 09:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Feat 새로운 기능 구현 size/L 재현✦
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Feat] Tabbar 화면 전환 구현
3 participants