How to include assets / resources in a Swift Package Manager library?

后端 未结 6 799
挽巷
挽巷 2020-12-15 15:35

I would like to ship my library using Apple\'s Swift Package Manager. However my lib includes a .bundle file with several strings translated in different languages. Using co

相关标签:
6条回答
  • 2020-12-15 15:46

    Important note:

    Resources doesn't seem to be included in the Xcode project generated by

    swift package generate-xcodeproj

    But they are when you open the Package folder on Xcode (xed .) and then double click on the package to resolve dependencies.

    I'm including a nice tutorial as well: https://medium.com/better-programming/how-to-add-resources-in-swift-package-manager-c437d44ec593

    0 讨论(0)
  • 2020-12-15 15:48

    The package manager does not yet have any definition for how resources will be bundled with targets. We are aware of the need for this, but don't yet have a concrete proposal for it. I filed https://bugs.swift.org/browse/SR-2866 to ensure we have a bug tracking this.

    0 讨论(0)
  • 2020-12-15 15:48

    Due to framework bundles not being supported yet, the only way to provide bundle assets with an SPM target is through a Bundle. If you implement code in your framework to search for a particular bundle in your main project (supporting asset bundles), you can load resources from said bundle.

    Example:

    Access the bundled resources:

    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
        }
    }
    

    Utilize the Bundled resources:

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

    You can apply the same logic for all resource files, including but not limited to storyboards, xibs, images, colors, data blobs, and files of various extensions (json, txt, etc).

    Note: Sometimes this makes sense, sometimes it doesn't. Determine use to own project's discretion. It would take very specific scenarios to justify separating Storyboards/Xibs into bundled assets.

    0 讨论(0)
  • 2020-12-15 15:51

    The solution I use for this is to build the data I need into a Swift object. To this end I have a shell script that will read an input file, base64 encode it, then write a Swift file that presents it as an InputStream. Then, when I want to add a data item to my Swift package, I run the script to read the file and write the output file. Of course the output file needs to be checked in so that the resource is available to those who use the project even if they do not have the script. (Typically I place my input files in a Resources directory and write the output to the Sources directory, but the script itself does not depend on that.)

    I consider this a less than ideal solution, and am looking forward to when the package manager has this ability built in. But in the meantime, it is a workable solution.

    The following example shows how it is used:

    First, here is the script itself:

    #!/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"
    

    Then, given an input file t.dat shown here:

    Hello World!
    
    

    Running the command generate_resource_file.sh t.dat HelloWorld.swift helloWorldInputStream generates the following HelloWorld.swift file:

    // 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)
        }
    }
    
    0 讨论(0)
  • 2020-12-15 16:00

    Using Swift 5.3 it's finally possible to add localized resources

    0 讨论(0)
  • 2020-12-15 16:00

    starting on Swift 5.3, thanks to SE-0271, you can add bundle resources on swift package manager by adding resources on your .target declaration.

    example:

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

    if you want to learn more, I have written an article on medium, discussing this topic

    0 讨论(0)
提交回复
热议问题