Skip to content

Commit

Permalink
Merge pull request #57 from YAPP-Github/TNT-208-appCoordinate
Browse files Browse the repository at this point in the history
[TNT-208] App Coordinator 로직 작성
  • Loading branch information
FpRaArNkK authored Feb 5, 2025
2 parents af7ea5e + e361e48 commit b733379
Show file tree
Hide file tree
Showing 9 changed files with 617 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
//
// AppFlowCoordinatorFeature.swift
// Presentation
//
// Created by 박민서 on 2/4/25.
// Copyright © 2025 yapp25thTeamTnT. All rights reserved.
//

import SwiftUI
import ComposableArchitecture

import Domain

@Reducer
public struct AppFlowCoordinatorFeature {
@ObservableState
public struct State: Equatable {
var userType: UserType?

// MARK: SubFeature state
var trainerMainState: TrainerMainFlowFeature.State?
var traineeMainState: TraineeMainFlowFeature.State?
var onboardingState: OnboardingFlowFeature.State?

public init(
userType: UserType? = nil,
onboardingState: OnboardingFlowFeature.State? = .init(),
trainerMainState: TrainerMainFlowFeature.State? = nil,
traineeMainState: TraineeMainFlowFeature.State? = nil
) {
self.userType = userType
self.onboardingState = onboardingState
self.trainerMainState = trainerMainState
self.traineeMainState = traineeMainState
}
}

public enum Action {
/// 하위 코디네이터에서 일어나는 액션을 처리합니다
case subFeature(SubFeatureAction)
case onAppear

@CasePathable
public enum SubFeatureAction: Sendable {
/// 온보딩 플로우 코디네이터에서 발생하는 액션 처리
case onboardingFlow(OnboardingFlowFeature.Action)
/// 트레이너 메인탭 플로우 코디네이터에서 발생하는 액션 처리
case trainerMainFlow(TrainerMainFlowFeature.Action)
/// 트레이니 메인탭 플로우 코디네이터에서 발생하는 액션 처리
case traineeMainFlow(TraineeMainFlowFeature.Action)
}
}

public init() {}

public var body: some Reducer<State, Action> {
Reduce { state, action in
switch action {
case .subFeature(let internalAction):
switch internalAction {
case .onboardingFlow:
return .none

case .trainerMainFlow:
return .none

case .traineeMainFlow:
return .none
}

case .onAppear:
return .none
}
}
.ifLet(\.onboardingState, action: \.subFeature.onboardingFlow) { OnboardingFlowFeature() }
.ifLet(\.trainerMainState, action: \.subFeature.trainerMainFlow) { TrainerMainFlowFeature() }
.ifLet(\.traineeMainState, action: \.subFeature.traineeMainFlow) { TraineeMainFlowFeature() }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//
// AppFlowCoordinatorView.swift
// Presentation
//
// Created by 박민서 on 2/4/25.
// Copyright © 2025 yapp25thTeamTnT. All rights reserved.
//

import SwiftUI
import ComposableArchitecture

public struct AppFlowCoordinatorView: View {
let store: StoreOf<AppFlowCoordinatorFeature>

public init(store: StoreOf<AppFlowCoordinatorFeature>) {
self.store = store
}

public var body: some View {
Group {
if let userType = store.userType {
switch userType {
case .trainee:
if let store = store.scope(state: \.traineeMainState, action: \.subFeature.traineeMainFlow) {
TraineeMainFlowView(store: store)
}
case .trainer:
if let store = store.scope(state: \.trainerMainState, action: \.subFeature.trainerMainFlow) {
TrainerMainFlowView(store: store)
}
}
} else {
if let store = store.scope(state: \.onboardingState, action: \.subFeature.onboardingFlow) {
OnboardingFlowView(store: store)
}
}
}
.onAppear {
store.send(.onAppear)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
//
// OnboardingNavigationFeature.swift
// Presentation
//
// Created by 박서연 on 1/24/25.
// Copyright © 2025 yapp25thTeamTnT. All rights reserved.
//

import SwiftUI
import ComposableArchitecture

import Domain

@Reducer
public struct OnboardingFlowFeature {
@ObservableState
public struct State: Equatable {
public var path: StackState<Path.State>

public init(path: StackState<Path.State> = .init([.snsLogin(.init())])) {
self.path = path
}
}

public enum Action: Sendable {
/// 현재 표시되고 있는 path 화면 내부에서 일어나는 액션을 처리합니다.
case path(StackActionOf<Path>)
case onAppear
}

public init() {}

public var body: some ReducerOf<Self> {
Reduce { state, action in
switch action {
case let .path(action):
switch action {

case .element(id: _, action: .snsLogin(.view(.tappedAppleLogin))):
state.path.append(.trainerSignUpComplete(.init()))
return .none

/// 트레이너 프로필 생성 완료 -> 다음 버튼 tapped
case .element(id: _, action: .trainerSignUpComplete(.setNavigating)):
state.path.append(.trainerMakeInvitationCode(MakeInvitationCodeFeature.State()))
return .none

/// 트레이너의 초대코드 화면 -> 건너뛰기 버튼 tapped
case .element(id: _, action: .trainerMakeInvitationCode(.setNavigation)):
// 추후에 홈과 연결
return .none

/// 약관 화면 -> 트레이너/트레이니 선택 화면 이동
case .element(id: _, action: .userTypeSelection):
return .none

default:
return .none
}

case .onAppear:
return .none
}
}
.forEach(\.path, action: \.path)
}
}

extension OnboardingFlowFeature {
@Reducer(state: .equatable, .sendable)
public enum Path {
// MARK: Common
/// SNS 로그인 뷰
case snsLogin(LoginFeature)
/// 약관동의뷰
case term(TermFeature)
/// 트레이너/트레이니 선택 뷰
case userTypeSelection(UserTypeSelectionFeature)
/// 트레이너/트레이니의 이름 입력 뷰
case createProfile(CreateProfileFeature)

// MARK: Trainer
/// 트레이너 회원 가입 완료 뷰
/// TODO: 트레이너/트레이니 회원 가입 완료 화면으로 통합 필요
case trainerSignUpComplete(TrainerSignUpCompleteFeature)
/// 트레이너의 초대코드 발급 뷰
case trainerMakeInvitationCode(MakeInvitationCodeFeature)
/// 트레이너의 트레이니 프로필 확인 뷰
case trainerConnectedTraineeProfile(ConnectedTraineeProfileFeature)

// MARK: Trainee
/// 트레이니 기본 정보 입력
case traineeBasicInfoInput(TraineeBasicInfoInputFeature)
/// 트레이니 PT 목적 입력
case traineeTrainingPurpose(TraineeTrainingPurposeFeature)
/// 트레이니 주의사항 입력
case traineePrecautionInput(TraineePrecautionInputFeature)
/// 트레이니 프로필 생성 완료
/// TODO: 트레이너/트레이니 회원 가입 완료 화면으로 통합 필요
case traineeProfileCompletion(TraineeProfileCompletionFeature)
/// 트레이니 초대 코드입력
case traineeInvitationCodeInput(TraineeInvitationCodeInputFeature)
/// 트레이니 수업 정보 입력
case traineeTrainingInfoInput(TraineeTrainingInfoInputFeature)
/// 트레이니 연결 완료
/// TODO: 트레이너/트레이니 연결 완료 화면으로 통합 필요
case traineeConnectionComplete(TraineeConnectionCompleteFeature)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
//
// OnboardingNavigationView.swift
// Presentation
//
// Created by 박민서 on 2/4/25.
// Copyright © 2025 yapp25thTeamTnT. All rights reserved.
//

import SwiftUI
import ComposableArchitecture

import DesignSystem

public struct OnboardingFlowView: View {
@Bindable var store: StoreOf<OnboardingFlowFeature>

public init(store: StoreOf<OnboardingFlowFeature>) {
self.store = store
}

public var body: some View {
NavigationStack(path: $store.scope(state: \.path, action: \.path)) {
EmptyView()
} destination: { store in
switch store.case {
// MARK: Common
case .snsLogin(let store):
LoginView(store: store)
case .term(let store):
TermView(store: store)
case .userTypeSelection(let store):
UserTypeSelectionView(store: store)
case .createProfile(let store):
CreateProfileView(store: store)

// MARK: Trainer
case .trainerSignUpComplete(let store):
TrainerSignUpCompleteView(store: store)
case .trainerMakeInvitationCode(let store):
MakeInvitationCodeView(store: store)
case .trainerConnectedTraineeProfile(let store):
ConnectedTraineeProfileView(store: store)

// MARK: Trainee
case .traineeBasicInfoInput(let store):
TraineeBasicInfoInputView(store: store)
case .traineeTrainingPurpose(let store):
TraineeTrainingPurposeView(store: store)
case .traineePrecautionInput(let store):
TraineePrecautionInputView(store: store)
case .traineeProfileCompletion(let store):
TraineeProfileCompletionView(store: store)
case .traineeInvitationCodeInput(let store):
TraineeInvitationCodeInputView(store: store)
case .traineeTrainingInfoInput(let store):
TraineeTrainingInfoInputView(store: store)
case .traineeConnectionComplete(let store):
TraineeConnectionCompleteView(store: store)
}
}
}
}
Loading

0 comments on commit b733379

Please sign in to comment.