RoutingManager

Swift Version

OS Platforms

Licence

RoutingManager 是一个 Swift 包,旨在简化和增强 SwiftUI 应用程序中的导航。它提供了一种结构化的方法来管理导航栈,支持状态持久化和多种存储选项。它还包括强大的错误处理和灵活的环境注入。

特性

安装

Swift Package Manager

要将 RoutingManager 添加到您的项目,请使用 Swift Package Manager。

  1. 在 Xcode 中打开您的项目。

  2. 转到 File > Add Packages

  3. 在搜索栏中,输入 RoutingManager 仓库的 URL

    https://github.com/markbattistella/RoutingManager
  4. 单击 Add Package

用法

开始使用

要开始使用 RoutingManager,请导入该包并在您的 SwiftUI 视图中创建一个 RoutingManager 实例。 这是一个基本的设置

import SwiftUI
import RoutingManager

typealias Routes = RoutingManager<Stack, Route>

@main
struct RouteTestApp: App {
  var body: some Scene {
    WindowGroup {
      NavigationWrapper(
        storage: .inMemory,
        identifier: "default",
        for: Route.self
      ) {
        // Your first view
      } environmentInjection: { route in
        // Environment injection
      }
    }
  }
}

enum Stack: String, NavigationStackRepresentable {
  var id: String { rawValue }
  case main
}

enum Route: String, NavigationRouteRepresentable {
  var id: String { String(describing: self) }

  case home
  case products
  case item(id: Int)

  var body: some View {
    switch self {
      case .home:
        HomeView()
      case .products:
        ProductsList()
      case .item(let id):
        ItemDetail(id: id)
    }
  }
}

初始化 NavigationWrapper

NavigationWrapperRoutingManager 包的一个关键组件,它提供了一种以声明方式设置和管理您的应用程序导航流程的方法。

初始化 NavigationWrapper 时,您配置了导航栈在应用程序中如何管理和存储。

NavigationWrapper(
  storage: .memory,
  stack: Stack.main,
  for: Route.self
) {
  // The initial view for the navigation stack
} environmentInjection: { route in
  // Inject dependencies or modify the environment for the route's view
  route.body
}

参数

  1. storage: NavigationStorageOption

    • 此参数定义了导航状态的存储方式。 NavigationWrapper 支持多种存储选项
      • .memory:导航状态存储在内存中。 它不会也不会在应用程序启动之间持久存在。
      • .json:导航状态存储在 JSON 文件中。 如果用户选择,它会在应用程序启动之间持久存在。
      • .custom(FileStorage<[Stack: [Route]]>):导航状态存储在自定义存储实现中。 用户选择如何处理跨应用程序启动的保存和加载。
  2. stack: NavigationStackRepresentable

    • 这是您希望更新路由的堆栈。 指定一个堆栈允许您在应用程序中初始化多个 NavigationWrapper,并使其影响特定的堆栈。 例如,用于导航的 .main 堆栈,以及用于 .sheetTabView 中不同标签的另一个堆栈。
  3. for: R.Type

    • 这指定了您的导航堆栈将处理的路由类型。 通常,这是符合 NavigationRouteRepresentable 的枚举,定义了应用程序中所有可能的路由。 通过提供路由类型,NavigationWrapper 可以根据您定义的路由来管理不同视图之间的导航。
  4. content: () -> View

    • 尾随闭包是您提供导航堆栈的初始视图或入口点的地方。 这是用户首次启动应用程序或重置导航堆栈时将看到的视图。 您可以从这个视图开始构建您的导航流程,并利用您已定义的路由。
  5. environmentInjection: (R) -> View

    • 此闭包接受一个 Route 实例作为其参数,该实例表示当前正在处理的路由。
    • 在此闭包中,您可以修改环境或注入与该路由关联的视图所需的依赖项。
    • 该闭包返回一个 View,它通常是在路由的 body 属性中定义的视图。 但是,您可以根据需要使用其他修饰符或环境对象来包装此视图。

何时使用 environmentInjection

示例场景

假设您有一个需要访问存储在环境中的用户个人资料对象的路由。 您可以直接通过 environmentInjection 闭包注入此依赖项

environmentInjection: { route in
  route.body
    .environmentObject(UserProfile())
}

导航路由方法

RoutingManager 提供了多种方法来管理导航路由

函数 说明 示例
push(to screens: Route...) -> NavigationResult 将一个或多个屏幕推入导航堆栈。 push(to: homeScreen, detailsScreen)
goBack(_ numberOfScreens: Int) -> NavigationResult 按指定的屏幕数量后退。 goBack(2)
goToOccurrence(of screen: Route, direction: OccurrenceDirection) -> NavigationResult 基于方向,导航到导航堆栈中特定出现的屏幕。 goToOccurrence(of: profileScreen, direction: .first)
replaceCurrentScreen(with screen: Route) -> NavigationResult 将当前屏幕替换为新屏幕。 replaceCurrentScreen(with: settingsScreen)
replace(stack: Stack, with routes: [Route]) -> NavigationResult 用新的屏幕序列替换特定的导航堆栈,同时保持其他堆栈不变。 replace(stack: .productStack, with: homeScreen, productsScreen, itemScreen)
replaceCurrentStack(with routes: [Route]) -> NavigationResult 用新的屏幕序列替换与此管理器关联的整个导航堆栈。 replaceCurrentStack(with: homeScreen, profileScreen, settingsScreen)
override(navigation: [Stack: [Route]]) -> NavigationResult 用新的堆栈和路由字典覆盖整个存储的导航状态。 这将完全替换所有堆栈和路由。 override(navigation: [.main: [homeScreen, cartScreen], .auth: [loginScreen]])
resetNavigation() -> NavigationResult 重置导航堆栈,删除所有屏幕。 resetNavigation()

导航堆栈方法

RoutingManager 提供了多种方法来管理导航堆栈

函数 说明 示例
listRoutes() -> [Stack: [Route]] 检索当前存在于导航堆栈中的所有路由的列表。 listRoutes()
save() -> NavigationResult 保存当前的导航状态。 save()
load() -> NavigationResult 加载先前保存的导航状态。 load()
delete() -> NavigationResult 删除保存的导航状态。 delete()

存储选项

RoutingManager 支持多种存储选项,用于保存和加载导航路径

内存存储

内存存储是最简单的存储形式。 它在应用程序会话期间临时存储导航路径,并且不会在应用程序启动之间持久存在。

@State private var routeManager: Routes = .init(
  storage: .memory,
  stack: Stack.main,
  for: Route.self
)

JSON 文件存储

JSON 文件存储将导航路径保存为设备上的 .json 文件,从而允许它们在应用程序启动之间持久存在。 这对于需要在重新启动后恢复导航状态的应用程序非常有用。

@State private var routeManager: Routes = .init(
  storage: .json,
  stack: Stack.main,
  for: Route.self
)

自定义存储

您可以通过遵循 FileStorageRepresentable 协议来实现您自己的存储。 例如,以下是如何实现 XML 存储的示例

// XMLFileStorage.swift
class XMLFileStorage<T: Codable>: FileStorageRepresentable {
  private let fileManager = FileManager.default
  private let fileURL: URL

  init(fileName: String = "NavigationState.xml") {
        let directory = FileManager.default
            .urls(for: .documentDirectory, in: .userDomainMask)
            .first!
        self.fileURL = directory.appendingPathComponent(fileName)
  }

  func save(_ object: T) throws {
    let encoder = PropertyListEncoder()
    encoder.outputFormat = .xml
    let data = try encoder.encode(object)
    try data.write(to: fileURL)
  }

  func load() throws -> T? {
    guard FileManager.default.fileExists(atPath: fileURL.path) else { return nil }
    let data = try Data(contentsOf: fileURL)
    do {
      let decoder = PropertyListDecoder()
      return try decoder.decode(T.self, from: data)
    } catch {
      throw NavigationError.load(error)
    }
  }

  func delete() throws {
    guard FileManager.default.fileExists(atPath: fileURL.path) else { return }
    try FileManager.default.removeItem(at: fileURL)
  }
}

// SwiftUI view
@State private var routeManager: Routes = .init(
  storage: .custom(FileStorage(XMLFileStorage())),
  stack: Stack.main,
  for: Route.self
)

错误处理

RoutingManager 通过 .navigationError 修饰符提供强大的错误处理。 此修饰符允许您处理在导航操作期间发生的错误。

routeManager.push(to: .home)
  .navigationError { error in
    print("An error occurred: \(error.localizedDescription)")
  }

贡献

欢迎贡献! 如果您有建议或改进,请 fork 存储库并提交 pull request。

许可证

RoutingManager 在 MIT 许可证下发布。 有关详细信息,请参阅 LICENCE。