web-dev-qa-db-fra.com

Comment inclure des actifs / ressources dans une bibliothèque Swift Package Manager?

Je voudrais expédier ma bibliothèque en utilisant le Swift Gestionnaire de paquets d'Apple. Cependant, ma bibliothèque comprend un fichier .bundle avec plusieurs chaînes traduites dans différentes langues. En utilisant des cocoapods, je peux l'inclure en utilisant spec.resource. Mais dans SwiftPM, je ne peux pas le faire. Une solution?

39
danielemm

Le gestionnaire de packages n'a pas encore de définition de la façon dont les ressources seront regroupées avec les cibles. Nous en sommes conscients, mais nous n'avons pas encore de proposition concrète à ce sujet. J'ai déposé https://bugs.Swift.org/browse/SR-2866 pour nous assurer que nous avons un bug qui suit cela.

41
Daniel Dunbar

Étant donné que les bundles de framework ne sont pas pris en charge pour l'instant, la seule façon de fournir des actifs de bundle avec une cible SPM est via un bundle. Si vous implémentez du code dans votre framework pour rechercher un bundle particulier dans votre projet principal (support des bundles d'actifs), vous pouvez charger des ressources à partir de ce bundle.

Exemple:

Accédez aux ressources groupées:

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

Utilisez les ressources groupées:

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

Vous pouvez appliquer la même logique pour tous les fichiers de ressources, y compris, mais sans s'y limiter, les storyboards, les xibs, les images, les couleurs, les blobs de données et les fichiers de diverses extensions (json, txt, etc.).

Remarque: Parfois, cela a du sens, parfois non. Déterminez l'utilisation à la discrétion du projet. Il faudrait des scénarios très spécifiques pour justifier la séparation des Storyboards/Xibs en ressources groupées.

3
TheCodingArt

La solution que j'utilise pour cela est de construire les données dont j'ai besoin dans un objet Swift. À cette fin, j'ai un script Shell qui va lire un fichier d'entrée, le coder en base64, puis écrire un = Swift fichier qui le présente comme un InputStream. Ensuite, quand je veux ajouter un élément de données à mon Swift package, j'exécute le script pour lire le fichier et écrire le fichier de sortie. Bien sûr, le fichier de sortie doit être archivé pour que la ressource soit disponible pour ceux qui utilisent le projet même s'ils n'ont pas le script. (En général, je place mes fichiers d'entrée dans un Resources et écrivez la sortie dans le répertoire Sources, mais le script lui-même ne dépend pas de cela.)

Je considère que c'est une solution loin d'être idéale, et j'attends avec impatience le moment où le gestionnaire de paquets aura cette capacité intégrée. Mais en attendant, c'est une solution viable.

L'exemple suivant montre comment il est utilisé:

Tout d'abord, voici le script lui-même:

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

Puis, étant donné un fichier d'entrée t.dat montré ici:

Hello World!

Exécution de la commande generate_resource_file.sh t.dat HelloWorld.Swift helloWorldInputStream génère les éléments suivants HelloWorld.Swift fichier:

// 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)
    }
}
2
Steven W. Klassen