AppDependency

AppDependency 是一个 Swift 包,它以线程安全、类型安全且 SwiftUI 友好的方式简化了应用程序依赖项的管理。AppDependency 具有用于管理依赖项的专用结构体类型,可轻松且协调地访问应用程序中的依赖项。此外,该软件包还包含内置的日志记录机制,以帮助调试和跟踪错误。 AppDependency 软件包还拥有一个基于缓存的系统,可以在任何给定时间持久存储和检索任何应用程序范围的数据。

AppDependency 只是 AppState 的一个依赖项镜像。

要求: iOS 15.0+ / watchOS 8.0+ / macOS 11.0+ / tvOS 15.0+ / visionOS 1.0+ | Swift 5.9+ / Xcode 15+

非 Apple 平台支持: Linux & Windows

主要特性

(🍎 仅限 Apple OS)

依赖项管理

细粒度控制

属性包装器

入门

要将 AppDependency 集成到您的 Swift 项目中,您需要使用 Swift Package Manager (SPM)。 SPM 可以轻松管理 Swift 包依赖项。这是你需要做的

  1. 将包依赖项添加到你的 Package.swift 文件中
dependencies: [
    .package(url: "https://github.com/0xLeif/AppDependency.git", from: "1.0.0")
]

如果您正在使用 App 项目,请在 Xcode 中打开您的项目。导航到 File > Swift Packages > Add Package Dependency... 并输入 https://github.com/0xLeif/AppDependency.git

  1. 接下来,不要忘记将 AppDependency 添加为项目的 target。对于 Xcode 和 SPM Package.swift,此步骤都是必要的。

成功添加 AppDependency 作为依赖项后,您需要将 AppDependency 导入到要使用的 Swift 文件中。这是一个代码示例

import AppDependency

用法

定义依赖项

Dependency 是 AppDependency 提供的一个功能,允许您在应用程序中定义共享资源或服务。

要定义依赖项,您应该扩展 Application 对象。这是一个定义 networkService 依赖项的示例

extension Application {
    var networkService: Dependency<NetworkServiceType> {
        dependency(NetworkService())
    }
}

在此示例中,Dependency<NetworkServiceType> 表示 NetworkService 的类型安全容器。

读取和使用依赖项

定义依赖项后,您可以在应用程序中的任何位置访问它

let networkService = Application.dependency(\.networkService)

这种方法允许您以类型安全的方式处理依赖项,避免手动处理与不正确类型相关的错误。

AppDependency 属性包装器

AppDependency 提供了 @AppDependency 属性包装器,可以简化对依赖项的访问。 当您使用 @AppDependency 注释属性时,它会直接从 Application 对象中获取相应的依赖项。

struct ContentView: View {
    @AppDependency(\.networkService) var networkService

    // Your view code
}

在这种情况下,ContentView 可以访问 networkService 依赖项,并且可以在其代码中使用它。

将依赖项与 ObservableObject 一起使用

当您的依赖项是一个 ObservableObject 时,对它的任何更改都会自动更新您的 SwiftUI 视图。 请确保您的服务符合 ObservableObject 协议。 为此,你不应该使用 @AppDependency 属性包装器,而应该使用 @ObservedDependency 属性包装器。

这是一个例子

class DataService: ObservableObject {
    @Published var data: [String]

    func fetchData() { ... }
}

extension Application {
    var dataService: Dependency<DataService> {
        dependency(DataService())
    }
}

struct ContentView: View {
    @ObservedDependency(\.dataService) private var dataService

    var body: some View {
        List(dataService.data, id: \.self) { item in
            Text(item)
        }
        .onAppear {
            dataService.fetchData()
        }
    }
}

在此示例中,每当 DataService 中的数据发生更改时,SwiftUI 都会自动更新 ContentView

使用 Mock 依赖项进行测试

在 AppDependency 中使用 Dependency 的一个巨大优势是能够在测试期间用 Mock 版本替换依赖项。 这对于隔离应用程序的各个部分以进行单元测试非常有用。

您可以通过调用 Application.override 函数来替换依赖项。 此函数返回一个 DependencyOverride,您需要保留此 token,只要您希望 Mock 依赖项有效。 当 token 被释放时,依赖项会恢复到其原始状态。

这是一个例子

// Real network service
extension Application {
    var networkService: Dependency<NetworkServiceType> {
        dependency(NetworkService())
    }
}

// Mock network service
class MockNetworkService: NetworkServiceType {
    // Your mock implementation
}

func testNetworkService() {
    // Keep hold of the `DependencyOverride` for the duration of your test.
    let networkOverride = Application.override(\.networkService, with: MockNetworkService())

    let mockNetworkService = Application.dependency(\.networkService)
    
    // Once done, you can allow the `DependencyOverrideen` to be deallocated 
    // or call `networkOverride.cancel()` to revert back to the original service.
}

SwiftUI 预览

要在 SwiftUI 预览中覆盖依赖项,必须使用 Environment.preview 函数并将依赖项覆盖传递给内容。

class Service {
    var title: String { "Live Service" }
}

class MockService: Service {
    override var title: String { "Mock Service" }
}

extension Application {
    var service: Dependency<Service> {
        dependency(Service())
    }
}

struct ContentView: View {
    @AppDependency(\.service) private var service

    var body: some View {
        Text(service.title)
    }
}

#Preview {
    Application.preview(
        Application.override(\.service, with: MockService()),
        Application.override(\.userDefaults, with: UserDefaults())
    ) {
        ContentView()
    }
}

许可证

AppDependency 在 MIT 许可证下发布。 有关更多信息,请参见LICENSE

沟通和贡献