SwiftUI 页面导航最佳实践

Share
SwiftUI 页面导航最佳实践
Photo by Chris Lawton / Unsplash

通过全局 Router

  1. 定义一个全局 Router 对象,维护页面跳转类型和参数。
@Observable
final class Router {
    
    public enum Destination: Codable, Hashable {
        case pageA(models: [Model])
        case pageB
    }
    
    var navPath = NavigationPath()
    
    func navigate(to destination: Destination) {
        navPath.append(destination)
    }
    
    func navigateBack() {
        navPath.removeLast()
    }
    
    func navigateToRoot() {
        navPath.removeLast(navPath.count)
    }
}

枚举 Destination 可以指定要跳转到哪个页面以及携带的参数。

  1. 将 Router 和 NavigationStack 绑定在一起。
@Bindable var router = Router()    // 定义 router 变量   

NavigationStack(path: $router.navPath) {
    ContentView()
        .navigationDestination(for: Router.Destination.self) { destination in
            switch destination {
            case .pageA(let models):
                PageAView(models: models)
            case .pageB:
                PageBView()
            }
        }
}
.frame(width: CONTENT_VIEW_WIDTH, height: CONTENT_VIEW_HEIGHT)
.environment(router)

NavigationStack 的 path 传入的是 Router 的 path,这样 Router 就可以控制 NavigationStack 的页面跳转。在 navigationDestination 里处理各种页面的跳转。

.environment(router) 让 Router 传递给视图上的子视图,任意分支的子视图(包括子视图的子视图…)都可以拿到 Router 实例,控制页面跳转。

struct SubView: View {
    @Environment(Router.self) var router: Router
        
    private func gotoPageA() {
        router.navigate(to: .pageA(models: models))
    }
}

进阶版

利用泛型和协议,让路由支持自定义。

public protocol Destination: Hashable, Codable {
    
}

@Observable
final public class Router<Dest: Destination> {
    public var navPath = NavigationPath()
    
    public init() {
        
    }
}

public extension Router {
    func navigate(to destination: Dest) {
        navPath.append(destination)
    }
    
    func navigateBack() {
        navPath.removeLast()
    }
    
    func navigateToRoot() {
        navPath.removeLast(navPath.count)
    }
}

这样不同的业务就可以创建不同类别的页面枚举了。

enum BusinessDestination: Destination {
    case pageA(models: [Model])
    case pageB
}

struct ContainerView: View {
    @Bindable var router = Router<BusinessDestination>()
}

Read more

在 AI 时代,做 iOS 逆向分析有哪些高效手段

在 AI 时代,做 iOS 逆向分析有哪些高效手段

AI 发展如此迅猛,传统的代码手艺活已经被 AI 取代了很大一部分。除了生成代码外,AI 在其他的研发流程也能提升很多效率,例如这次要说的 AI 逆向分析。 在手工时代,我们逆向分析一个 iOS app,通常是使用 Hopper 或者 IDA 等软件查看解密的 Mach-O,然后利用软件提供的能力加上人工去分析逻辑流程。这个过程还要求我们懂一些基础的汇编语法。有时候还要去猜测哪些类哪些方法是负责某个逻辑,靠试验去验证。 现在来到了大模型时代,AI 不仅能自己通过一些简单的工具去分析 ipa 和 Mach-O ,也能使用更准更稳的 MCP、Skill 工具来做深度的分析。 Hopper MCP Hopper 在新版本支持了 MCP Server。 把 framework、Mach-O 或者解密后的 IPA 直接丢给它,

By Gray
Cinder:基于 Swift Macro 与 Mach-O Section 的解耦注册机制

Cinder:基于 Swift Macro 与 Mach-O Section 的解耦注册机制

Cinder(Github) 是一个无需逐模块启动注册、支持字符串、类型和函数调用的 Swift 编译期注册框架。 Cinder 是什么 在 iOS app 里借助 Mach-O 注册能力做一些二进制的插桩操作并不罕见。以往这些操作大都使用 objc runtime ,借助 __attribute__((used)) 和 __attribute__((section("name"))) 实现。在 Swift 上实现有一些难度,因为 Swift runtime 能力弱,以及早期的版本没有类似 objc 的 __attribute__ 能力。 * 在 Swift 5.10 版本,Apple 提供了 @_used 和

By Gray