44

我想使用 Apple 的 Swift 包管理器来发送我的库。但是,我的 lib 包含一个 .bundle 文件,其中包含多个翻译成不同语言的字符串。使用 cocoapods,我可以使用 spec.resource 包含它。但是在 SwiftPM 中,我做不到。有什么解决办法吗?

4

6 回答 6

43

包管理器还没有任何关于如何将资源与目标捆绑的定义。我们知道这样做的必要性,但还没有具体的建议。我提交了https://bugs.swift.org/browse/SR-2866以确保我们有跟踪此问题的错误。

于 2016-10-05T15:39:23.260 回答
14

使用 Swift 5.3 终于可以添加本地化资源了

Package 初始化程序现在有一个defaultLocalization可用于本地化资源的参数。

public init(
    name: String,
    defaultLocalization: LocalizationTag = nil, // New defaultLocalization parameter.
    pkgConfig: String? = nil,
    providers: [SystemPackageProvider]? = nil,
    products: [Product] = [],
    dependencies: [Dependency] = [],
    targets: [Target] = [],
    swiftLanguageVersions: [Int]? = nil,
    cLanguageStandard: CLanguageStandard? = nil,
    cxxLanguageStandard: CXXLanguageStandard? = nil
)

假设您有一个Icon.png想要为讲英语和德语的人本地化的内容。

图像应包含在Resources/en.lproj/Icon.png&中Resources/de.lproj/Icon.png

在你可以像这样在你的包中引用它们之后:

let package = Package(
    name: "BestPackage",
    defaultLocalization: "en",
    targets: [
        .target(name: "BestTarget", resources: [
            .process("Resources/Icon.png"),
        ])
    ]
)

请注意LocalizationTagIETF Language Tag的包装。

以下提案概述的学分和输入,请查看更多详细信息。

于 2020-05-01T17:24:04.350 回答
4

从 Swift 5.3 开始,感谢SE-0271,您可以通过添加声明来resources在swift 包管理器上添加捆绑资源。.target

例子:

.target(
   name: "HelloWorldProgram",
   dependencies: [], 
   resources: [.process(Images), .process("README.md")]
)

如果你想了解更多,我在medium上写了一篇文章,讨论这个话题

于 2020-05-22T17:35:08.713 回答
3

我使用的解决方案是将我需要的数据构建到一个 Swift 对象中。为此,我有一个 shell 脚本,它将读取一个输入文件,对其进行 base64 编码,然后编写一个 Swift 文件,将其呈现为一个 InputStream。然后,当我想向我的 Swift 包中添加数据项时,我运行脚本来读取文件并写入输出文件。当然,需要签入输出文件,以便使用该项目的人即使没有脚本也可以使用该资源。(通常我将输入文件放在一个Resources目录中并将输出写入该Sources目录,但脚本本身并不依赖于此。)

我认为这是一个不太理想的解决方案,并期待包管理器何时内置此功能。但与此同时,它是一个可行的解决方案。

下面的例子展示了它是如何使用的:

首先,这是脚本本身:

#!/usr/bin/env bash

# Read an input file, base64 encode it, then write an output swift file that will
# present it as an input stream.
#
# Usage: generate_resource_file.sh <inputfile> <outputfile> <streamName>
#
# The <streamName> is the name presented for the resulting InputStream. So, for example,
#   generate_resource_file.sh Resources/logo.png Sources/Logo.swift logoInputStream
# will generate a file Sources/Logo.swift that will contain a computed variable
# that will look like the following:
#   var logoInputStream: InputStream { ...blah...
#

set -e

if [ $# -ne 3 ]; then
    echo "Usage: generate_resource_file.sh <inputfile> <outputfile> <streamName>"
    exit -1
fi

inFile=$1
outFile=$2
streamName=$3

echo "Generating $outFile from $inFile"
echo "Stream name will be $streamName"

if [ ! -f "$inFile" ]; then
    echo "Could not read $inFile"
    exit -1
fi

echo "// This file is automatically generated by generate_resource_file.sh. DO NOT EDIT!" > "$outFile"
echo "" >> "$outFile"
echo "import Foundation" >> "$outFile"
echo "" >> "$outFile"
echo "fileprivate let encodedString = \"\"\"" >> "$outFile"
base64 -i "$inFile" >> "$outFile"
echo "\"\"\"" >> "$outFile"
echo "" >> "$outFile"
echo "var $streamName: InputStream {" >> "$outFile"
echo "    get {" >> "$outFile"
echo "        let decodedData = Data(base64Encoded: encodedString)!" >> "$outFile"
echo "        return InputStream(data: decodedData)" >> "$outFile"
echo "    }" >> "$outFile"
echo "}" >> "$outFile"

echo "Rebuilt $outFile"

然后,给定此处显示的输入文件t.dat

Hello World!

运行该命令generate_resource_file.sh t.dat HelloWorld.swift helloWorldInputStream会生成以下HelloWorld.swift文件:

// This file is automatically generated by generate_resource_file.sh. DO NOT EDIT!

import Foundation

fileprivate let encodedString = """
SGVsbG8gV29ybGQhCgo=
"""

var helloWorldInputStream: InputStream {
    get {
        let decodedData = Data(base64Encoded: encodedString)!
        return InputStream(data: decodedData)
    }
}
于 2020-03-12T17:50:36.480 回答
3

由于尚不支持框架包为包资产提供 SPM 目标的唯一方法是通过包。如果您在框架中实现代码以在主项目(支持资产包)中搜索特定包,则可以从该包中加载资源。

例子:

访问捆绑的资源:

extension Bundle {
    static func myResourceBundle() throws -> Bundle {
        let bundles = Bundle.allBundles
        let bundlePaths = bundles.compactMap { $0.resourceURL?.appendingPathComponent("MyAssetBundle", isDirectory: false).appendingPathExtension("bundle") }

        guard let bundle = bundlePaths.compactMap({ Bundle(url: $0) }).first else {
            throw NSError(domain: "com.myframework", code: 404, userInfo: [NSLocalizedDescriptionKey: "Missing resource bundle"])
        }
        return bundle
    }
}

利用捆绑资源:

        let bundle = try! Bundle.myResourceBundle()
        return UIColor(named: "myColor", in: bundle, compatibleWith: nil)!

您可以对所有资源文件应用相同的逻辑,包括但不限于故事板、xib、图像、颜色、数据块和各种扩展名(json、txt 等)的文件。

注意:有时这是有道理的,有时则没有。确定用途以拥有项目的自由裁量权。需要非常具体的场景来证明将 Storyboards/Xibs 分成捆绑资产的合理性。

于 2019-09-27T13:05:33.147 回答
-1

重要的提示:

资源似乎不包含在生成的 Xcode 项目中

swift package generate-xcodeproj

但它们是当您在 Xcode ( ) 上打开 Package 文件夹xed .,然后双击该包以解决依赖关系时。

我还包括一个很好的教程:https ://medium.com/better-programming/how-to-add-resources-in-swift-package-manager-c437d44ec593

于 2020-11-20T02:12:11.877 回答