Skip to content

Instantly share code, notes, and snippets.

@Jager-yoo
Created February 22, 2023 09:42
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Jager-yoo/572c1735cf2560299a22c6f6065914b5 to your computer and use it in GitHub Desktop.
Save Jager-yoo/572c1735cf2560299a22c6f6065914b5 to your computer and use it in GitHub Desktop.
LinkNavigator README in Korean

- Concept

✨ LinkNavigator 는 SwiftUI 에서 화면을 자유롭게 이동할 수 있도록 도와주는 라이브러리입니다.

  • URL path 형식의 표현 방법을 통해, 화면 이동에 대한 직관적인 syntax 를 제공합니다.
  • 딥링크 처리 방식으로 어떤 화면이든 간편하게 이동할 수 있습니다.
  • 화면 이동과 함께 매개변수를 주입할 수 있습니다.
  • MVI 디자인 패턴, 혹은 pointfreeco 의 The Composable Architecture 와 같은 Uni-directional Architecture 에서 사용할 목적으로 디자인 되었지만, 그 외의 Architecture 에서도 사용하기 좋습니다.

- Basic Usage

  • 하나 혹은 여러 개의 화면을 push 합니다.

    navigator.next(paths: ["page1", "page2"], items: [:], isAnimated: true)
  • 하나 혹은 여러 개의 화면을 pop 합니다.

    navigator.remove(paths: ["pageToRemove"])
  • 이전 화면으로 돌아가거나, modal 을 내립니다.

    navigator.back(isAnimated: true)
  • 원하는 화면으로 이동합니다. 그 화면이 이미 네비게이션 스택에 존재한다면, 원하는 화면까지 이동하면서 그 사이의 다른 화면들을 pop 합니다. 만약 네비게이션 스택에 없던 새로운 화면이라면, 새롭게 push 합니다.

    navigator.backOrNext(path: "targetPage", items: [:], isAnimated: true)
  • 현재의 네비게이션 스택을 교체합니다.

    navigator.replace(paths: ["main", "depth1", "depth2"], items: [:], isAnimated: true)
  • 원하는 화면을 sheet 또는 full screen cover modal 로 올립니다.

    navigator.sheet(paths: ["sheetPage"], items: [:], isAnimated: true)
    
    navigator.fullSheet(paths: ["page1", "page2"], items: [:], isAnimated: true)
  • modal 을 내린 후 completion 클로저를 실행합니다.

    navigator.close(isAnimated: true) { print("modal dismissed!") }
  • 시스템 Alert 를 보여줍니다.

    let alertModel = Alert(
      title: "Title",
      message: "message",
      buttons: [.init(title: "OK", style: .default, action: { print("OK tapped") })],
      flagType: .default)
    navigator.alert(target: .default, model: alertModel)

- Advanced Usage

  • 복잡한 경로를 편집해서 사용할 수 있습니다.

    // 현재 네비게이션 스택 == ["home", "depth1", "depth2", "depth3"]
    // 목표 스택 == ["home", "depth1", "newDepth"]
    
    var new = navigator.range(path: "depth1") + ["newDepth"]
    navigator.replace(paths: new, items: [:], isAnimated: true)
  • modal 뒤에 있는 화면을 조종할 수 있습니다.

    navigator.rootNext(paths: ["targetPage"], items: [:], isAnimated: true)
    
    navigator.rootBackOrNext(path: "targetPage", items: [:], isAnimated: true)
  • 아이폰, 아이패드 각각에 대한 modal presentation 스타일을 선택할 수 있습니다.

    navigator.customSheet(
      paths: ["sheetPage"],
      items: [:],
      isAnimated: true,
      iPhonePresentationStyle: .fullScreen,
      iPadPresentationStyle: .pageSheet)
  • modal 뒤의 최상단에 있는 화면을 강제로 reload 합니다. onAppear(perform:) 수정자를 다시 호출해야 할 때 유용합니다.

    navigator.rootReloadLast(items: [:], isAnimated: false)

- Example

LinkNavigator 는 2가지 예제를 제공합니다.


- Getting Started

Step 1

  • SwiftUI 프로젝트에 LinkNavigator 를 설치하기 위해서, 총 4개의 파일을 설정해야 합니다.

  • 파일, 타입의 이름은 자유롭게 수정해도 됩니다. 다음 예시에서는 이해를 돕기 위해 단순한 이름을 사용했습니다.

  • AppDependency -> AppRouterGroup -> AppDelegate -> AppMain 순서로 설명합니다.

    // AppDependency.swift
    // 외부 의존성을 관리하는 타입입니다.
    
    import LinkNavigator
    
    struct AppDependency: DependencyType { } // DependencyType 프로토콜을 채택해야 합니다.
    // AppRouterGroup.swift
    // LinkNavigator 를 통해 이동하고 싶은 화면들을 관리하는 타입입니다.
    
    import LinkNavigator
    
    struct AppRouterGroup {
      var routers: [RouteBuilder] {
        [
          HomeRouteBuilder(), // Step 3 에서 구현
          Page1RouteBuilder(),
          Page2RouteBuilder(),
          Page3RouteBuilder(),
          Page4RouteBuilder(),
        ]
      }
    }
    // AppDelegate.swift
    // 외부 의존성과 화면을 주입받은 navigator 를 관리하는 타입입니다.
    
    import SwiftUI
    import LinkNavigator
    
    final class AppDelegate: NSObject {
      var navigator: LinkNavigator {
        LinkNavigator(dependency: AppDependency(), builders: AppRouterGroup().routers)
      }
    }
    
    extension AppDelegate: UIApplicationDelegate {
      func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
        true
      }
    }
    // AppMain.swift
    // Application 의 시작 화면을 설정하는 타입입니다.
    
    import SwiftUI
    import LinkNavigator
    
    @main
    struct AppMain: App {
      @UIApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate
    
      var navigator: LinkNavigator {
        appDelegate.navigator
      }
    
      var body: some Scene {
        WindowGroup {
          navigator
            .launch(paths: ["home"], items: [:]) // 'paths' 파라미터의 인자가 시작 페이지로 설정됩니다.
            .onOpenURL { url in
            // 딥링크 네비게이션이 필요한 경우,
            // URL 편집은 여기서 수행합니다.
            }
        }
      }
    }

Step 2

  • 화면에 해당하는 구조체 내부에 navigator 프로퍼티를 추가하여, 이니셜라이징 될 때 주입되도록 합니다.

  • Architecture 특징에 따라, navigator 프로퍼티의 위치를 자유롭게 변경해서 사용하세요. 예를 들어, ViewModel 또는 Environment 에 넣어서 사용해도 좋습니다.

    struct HomePage: View {
      let navigator: LinkNavigatorType
    
      var body: some View {
        ...
      }
    }

Step 3

  • 모든 화면에 각각에 대해 RouteBuilder 프로토콜을 채택한 구조체를 만듭니다.

  • 이렇게 만든 RouteBuilder 구조체들은 AppRouterGroup 타입에서 모아두고 관리합니다.

    import LinkNavigator
    import SwiftUI
    
    struct HomeRouteBuilder: RouteBuilder {
      var matchPath: String { "home" }
    
      var build: (LinkNavigatorType, [String: String], DependencyType) -> UIViewController? {
        { navigator, items, dependency in
          return WrappingController(matchingKey: matchPath) {
            HomePage(navigator: navigator)
          }
        }
      }
    }

- Installation

LinkNavigator 는 Swift Package Manager 를 지원합니다.

  • Xcode 상단의 File 메뉴 -> Add Packages... 를 선택합니다.
  • Package URL 입력창에 "https://github.com/interactord/LinkNavigator.git" 를 입력해서 설치합니다.
  • 혹은 Package.swift 파일에 아래와 같이 입력합니다.
let package = Package(
  name: "MyPackage",
  products: [
    .library(
      name: "MyPackage",
      targets: ["MyPackage"]),
  ],
  dependencies: [
    .package(url: "https://github.com/interactord/LinkNavigator.git", .upToNextMajor(from: "0.5.4"))
  ],
  targets: [
    .target(
      name: "MyPackage",
      dependencies: ["LinkNavigator"])
  ]
)

- License

이 라이브러리는 MIT 라이선스를 따릅니다. 자세한 내용은 LICENSE 를 확인해주세요.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment