SwiftUI NavigationStack Coordinator 적용후기

quokka·2024년 5월 9일

SwiftUI

목록 보기
6/7

git Repo: https://github.com/quokkaKyu/SwiftUINavigationStackCoordinator

뷰가 많아지면 관리하기가 힘들어집니다. 그래서 뷰의 이동만을 관리해줄 Coordinator가 필요했습니다. iOS16 이상 버전에서 사용할 SwiftUI NavigationStack에 Coordinator 패턴을 도입한 후기를 작성했습니다.

1. 목적지를 설정해줍니다.

enum Destination: Hashable {
    case firstView(number: Int)
    case secondView
    case thirdView(number: Int)
}

2. 뷰의 이동을 관리할 Coordinator를 생성해줍니다.

import Foundation
import Combine

final class Coordinator<T: Hashable>: ObservableObject {
    @Published var paths: [T] = []
    
    func push(_ path: T) {
        paths.append(path)
    }
    
    func pop() {
        paths.removeLast()
    }
    
    func pop(to: T) {
        guard let found = paths.firstIndex(where: { $0 == to }) else {
            return
        }
        
        let numToPop = (found..<paths.endIndex).count - 1
        paths.removeLast(numToPop)
    }
    
    func popToRoot() {
        paths.removeAll()
    }
}

3. ContentView에서 NavigationStack 초기화, navigationDestination에 이동할 뷰 설정, 하위계층 뷰에 Coordinator(=>ObservableObject) 전달

import SwiftUI

struct ContentView: View {
    @ObservedObject private var coordinator = Coordinator<Destination>()
    private let title = "ContentView"
    var body: some View {
        NavigationStack(path: $coordinator.paths) {
            VStack {
            }
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .background(Color.white)
            .navigationBarTitleDisplayMode(.large)
            .toolbar(content: {
                Button(action: {
                    coordinator.push(.firstView(number: 1))
                }, label: {
                    Text("FirstView")
                })
            })
            .navigationDestination(for: Destination.self) { destination in
                switch destination {
                case .firstView(let number): FirstView(number: number)
                case .secondView: SecondView()
                case .thirdView(let number): ThirdView(number: number)
                }
            }
        }
        .environmentObject(coordinator)
    }
}

4. 이동한 뷰에서 @EnvironmentObject로 전달받은 Coordinator 기능 구현

struct FirstView: View {
    @EnvironmentObject private var coordinator: Coordinator<Destination>
    private let title = "FirstView"
    let number: Int
    var body: some View {
        VStack {
            Text("\(number)")
            Spacer()
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .background(Color.secondary)
        .navigationTitle(title)
        .toolbar(content: {
            ToolbarItem(placement: .topBarTrailing, content: {
                Button(action: {
                    coordinator.push(.secondView)
                }, label: {
                    Text("SecondView")
                })
            })
        })
        .toolbar(content: {
            ToolbarItem(placement: .topBarLeading, content: {
                Button(action: {
                    coordinator.pop()
                }, label: {
                    Text("ContentView")
                })
            })
        })
        .navigationBarBackButtonHidden()
    }
}

이상으로 버전에서 SwiftUI NavigationStack에 Coordinator 패턴을 도입한 후기를 마치겠습니다. iOS16이상 SwiftUI 뷰의 이동을 구현하는데 도움이 되셨으면 좋겠습니다. 피드백은 언제든지 환영입니다.

Reference
https://www.curiousalgorithm.com/post/router-pattern-for-swiftui-navigation
https://medium.com/@zuxbcvf/exploring-scalable-swiftui-navigation-30f8438e9d6d

profile
iOS를 공부하는 개발자입니다~ㅎㅎ

0개의 댓글