RoutingManager
是一个 Swift 包,旨在简化和增强 SwiftUI 应用程序中的导航。它提供了一种结构化的方法来管理导航栈,支持状态持久化和多种存储选项。它还包括强大的错误处理和灵活的环境注入。
FileStorageRepresentable
协议来实现您自己的存储解决方案。NavigationError
枚举和 navigationError
处理程序来处理导航失败。NavigationWrapper
将依赖项直接传递到视图中。要将 RoutingManager
添加到您的项目,请使用 Swift Package Manager。
在 Xcode 中打开您的项目。
转到 File > Add Packages
。
在搜索栏中,输入 RoutingManager
仓库的 URL
https://github.com/markbattistella/RoutingManager
单击 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
是 RoutingManager
包的一个关键组件,它提供了一种以声明方式设置和管理您的应用程序导航流程的方法。
初始化 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
}
storage: NavigationStorageOption
NavigationWrapper
支持多种存储选项.memory
:导航状态存储在内存中。 它不会也不会在应用程序启动之间持久存在。.json
:导航状态存储在 JSON 文件中。 如果用户选择,它会在应用程序启动之间持久存在。.custom(FileStorage<[Stack: [Route]]>)
:导航状态存储在自定义存储实现中。 用户选择如何处理跨应用程序启动的保存和加载。stack: NavigationStackRepresentable
NavigationWrapper
,并使其影响特定的堆栈。 例如,用于导航的 .main
堆栈,以及用于 .sheet
或 TabView
中不同标签的另一个堆栈。for: R.Type
NavigationRouteRepresentable
的枚举,定义了应用程序中所有可能的路由。 通过提供路由类型,NavigationWrapper
可以根据您定义的路由来管理不同视图之间的导航。content: () -> View
environmentInjection: (R) -> View
Route
实例作为其参数,该实例表示当前正在处理的路由。View
,它通常是在路由的 body
属性中定义的视图。 但是,您可以根据需要使用其他修饰符或环境对象来包装此视图。依赖项注入: 如果您的视图需要访问特定的环境对象,您可以在此处注入它们。 例如,您可以注入视图正常运行所需的数据模型或服务对象。
动态修改: 如果某些视图需要根据路由进行动态修改,您可以在此闭包中应用这些更改。 这可以提高视图的灵活性和重用性。
假设您有一个需要访问存储在环境中的用户个人资料对象的路由。 您可以直接通过 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
文件,从而允许它们在应用程序启动之间持久存在。 这对于需要在重新启动后恢复导航状态的应用程序非常有用。
@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。