Hug*_*l31 10 swift swift-package-manager
我正在尝试在单元测试中使用资源文件并使用它来访问它Bundle.path
,但它返回nil.
MyProjectTests.swift中的这个调用返回nil:
Bundle(for: type(of: self)).path(forResource: "TestAudio", ofType: "m4a")
Run Code Online (Sandbox Code Playgroud)
这是我的项目层次结构.我也尝试过移动TestAudio.m4a
到一个Resources
文件夹:
??? Package.swift
??? Sources
? ??? MyProject
? ??? ...
??? Tests
??? MyProjectTests
??? MyProjectTests.swift
??? TestAudio.m4a
Run Code Online (Sandbox Code Playgroud)
这是我的包装说明:
// swift-tools-version:4.0
import PackageDescription
let package = Package(
name: "MyProject",
products: [
.library(
name: "MyProject",
targets: ["MyProject"])
],
targets: [
.target(
name: "MyProject",
dependencies: []
),
.testTarget(
name: "MyProjectTests",
dependencies: ["MyProject"]
),
]
)
Run Code Online (Sandbox Code Playgroud)
我正在使用Swift 4和Swift Package Manager Description API版本4.
l -*_*c l 33
斯威夫特 5.3
请参阅 Apple 文档:“使用 Swift 包捆绑资源”
Swift 5.3 包含包管理器资源 SE-0271进化提案,其中包含“状态:已实施(Swift 5.3) ”。:-)
资源并不总是供包的客户使用;资源的一种用途可能包括仅单元测试需要的测试装置。此类资源不会与库代码一起合并到包的客户端中,而只会在运行包的测试时使用。
resources
在target
和testTarget
API 中添加一个新参数以允许显式声明资源文件。SwiftPM 使用文件系统约定来确定属于包中每个目标的源文件集:具体而言,目标的源文件是位于为目标指定的“目标目录”下的那些文件。默认情况下,这是一个与目标同名的目录,位于“Sources”(对于常规目标)或“Tests”(对于测试目标)中,但可以在包清单中自定义此位置。
Run Code Online (Sandbox Code Playgroud)// Get path to DefaultSettings.plist file. let path = Bundle.module.path(forResource: "DefaultSettings", ofType: "plist") // Load an image that can be in an asset archive in a bundle. let image = UIImage(named: "MyIcon", in: Bundle.module, compatibleWith: UITraitCollection(userInterfaceStyle: .dark)) // Find a vertex function in a compiled Metal shader library. let shader = try mtlDevice.makeDefaultLibrary(bundle: Bundle.module).makeFunction(name: "vertexShader") // Load a texture. let texture = MTKTextureLoader(device: mtlDevice).newTexture(name: "Grass", scaleFactor: 1.0, bundle: Bundle.module, options: options)
例子
// swift-tools-version:5.3
import PackageDescription
targets: [
.target(
name: "Example",
dependencies: [],
resources: [
// Apply platform-specific rules.
// For example, images might be optimized per specific platform rule.
// If path is a directory, the rule is applied recursively.
// By default, a file will be copied if no rule applies.
// Process file in Sources/Example/Resources/*
.process("Resources"),
]),
.testTarget(
name: "ExampleTests",
dependencies: [Example],
resources: [
// Copy Tests/ExampleTests/Resources directories as-is.
// Use to retain directory structure.
// Will be at top level in bundle.
.copy("Resources"),
]),
Run Code Online (Sandbox Code Playgroud)
报告的问题和可能的解决方法
Xcode
Bundle.module
由 SwiftPM 生成(请参阅Build/BuildPlan.swift SwiftTargetBuildDescription generateResourceAccessor()),因此在由 Xcode 构建时不存在于Foundation.Bundle 中。
Xcode 中的一种类似方法是手动将Resources
引用文件夹添加到 Xcode 项目,添加 Xcode 构建阶段copy
以将其Resource
放入某个*.bundle
目录,并#ifdef XCODE_BUILD
为 Xcode 构建添加一些自定义编译器指令以使用资源。
#if XCODE_BUILD
extension Foundation.Bundle {
/// Returns resource bundle as a `Bundle`.
/// Requires Xcode copy phase to locate files into `ExecutableName.bundle`;
/// or `ExecutableNameTests.bundle` for test resources
static var module: Bundle = {
var thisModuleName = "CLIQuickstartLib"
var url = Bundle.main.bundleURL
for bundle in Bundle.allBundles where bundle.bundlePath.hasSuffix(".xctest") {
url = bundle.bundleURL.deletingLastPathComponent()
thisModuleName = thisModuleName.appending("Tests")
}
url = url.appendingPathComponent("\(thisModuleName).bundle")
guard let bundle = Bundle(url: url) else {
fatalError("Foundation.Bundle.module could not load resource bundle: \(url.path)")
}
return bundle
}()
/// Directory containing resource bundle
static var moduleDir: URL = {
var url = Bundle.main.bundleURL
for bundle in Bundle.allBundles where bundle.bundlePath.hasSuffix(".xctest") {
// remove 'ExecutableNameTests.xctest' path component
url = bundle.bundleURL.deletingLastPathComponent()
}
return url
}()
}
#endif
Run Code Online (Sandbox Code Playgroud)
Ram*_*mis 32
在正确的文件结构和依赖项设置之后, Bundle.module开始为我工作。
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages this package depends on.
.target(
name: "Parser",
dependencies: []),
.testTarget(
name: "ParserTests",
dependencies: ["Parser"],
resources: [
.copy("Resources/test.txt")
]
),
]
Run Code Online (Sandbox Code Playgroud)
private var testData: Data {
let url = Bundle.module.url(forResource: "test", withExtension: "txt")!
let data = try! Data(contentsOf: url)
return data
}
Run Code Online (Sandbox Code Playgroud)
SwiftPM(5.1)不支持原生资源还,但是...
在运行单元测试时,可以期望该存储库可用,因此只需使用从派生的内容加载资源#file
。这适用于所有现有的SwiftPM版本。
let thisSourceFile = URL(fileURLWithPath: #file)
let thisDirectory = thisSourceFile.deletingLastPathComponent()
let resourceURL = thisDirectory.appendingPathComponent("TestAudio.m4a")
Run Code Online (Sandbox Code Playgroud)
在测试以外的情况下,运行时存储库将不存在,但仍然可以包括资源,尽管以二进制大小为代价。通过将任意文件表示为字符串文字中的base 64数据,可以将其嵌入到Swift源代码中。Workspace 是一个开源工具,可以使该过程自动化:$ workspace refresh resources
。(免责声明:我是它的作者。)
目前,swift包管理器(SPM)不处理资源,这是在SPM的错误跟踪系统https://bugs.swift.org/browse/SR-2866中打开的问题.
在SPM中实现对资源的支持之前,我会将资源复制到结果可执行文件在运行时期望资源的位置.您可以通过打印Bundle.resourcePath
属性了解这些位置.我会使用Makefile自动执行此复制.这样Makefile就成为了SPM之上的"构建协调器".
我写了一个例子来演示这种方法在MacOS和Linux上是如何工作的 - https://github.com/vadimeisenbergibm/SwiftResourceHandlingExample.
用户将运行make命令:make build
而make test
不是swift build
和swift test
.Make会将资源复制到预期的位置(在MacOS和Linux上,在运行期间和测试期间不同).
归档时间: |
|
查看次数: |
2241 次 |
最近记录: |