⚠️ Work in progress
Sacoge is a swift package plugin that makes it easy to serve your Swift Server's assets with an immutable cache policy by generating static references to each asset and adding the asset content's hash to its file name.
By serving assets with an immutable cache policy you can considerably reduce the number of requests to your server.
If you're using hummingbird
, the swift-sacoge-hummingbird contains a middleware that works with sacoge's generated code.
- Add sacoge to the package dependencies:
dependencies: [
// ...
.package(url: "https://github.com/alephao/swift-sacoge.git", from: "0.1.0"),
]
- Configure your target with SacogePlugin and SacogeHummingbird.
.target(
name: "MyTarget",
plugins: [
.plugin(name: "SacogePlugin", package: "swift-sacoge"),
]
)
- Build
Now you can access the Asset
type in your target, containing references to the assets inside the public
folder.
To configure sacoge, create a file named .sacoge
in the root of your project, here is an example with the default values. If you omit any of the keys, it will use the default value:
{
"from": "/",
"to": "public",
"structName": "Asset",
"ignore": [],
"skipChecksum": []
}
from
: The base request path to map to the file system's public directory. If thefrom -> to
values are/static/immutable/ -> public
, then requests incoming tohttps://example.com/static/immutable/img.jpg
will look into the file system'spublic/img.jpg
. Default:"/"
to
: The path to the directory containing the static assets. If thefrom -> to
values are/static/immutable/ -> public
, then requests incoming tohttps://example.com/static/immutable/img.jpg
will look into the file system'spublic/img.jpg
. Default:"public"
structName
: The name of the generated type that contains references to all assets. By default, if you have an asset in the root namedimg.jpg
you can reference it in your swift code by usingAsset.img_jpg
. SettingstructName
will renameAsset
to something else so you would access it via{structName}.img_jpg
. Default"Asset"
.ignore
: An array with the name of files/dirs you want sacoge to ignore. It won't generate any references to the files/dirs specified here. It has to be the exact name of the file/dir likeimg.jpg
orimages
(there are no suppport for globs yet). Default:[]
.skipChecksum
: By default, every file will have its content's hash added to thefrom
value, soimg.png
would have afrom
value ofimg_abcd1234.jpg
.skipChecksum
works likeignore
, but instead not generating references, it won't add the checksum to thefrom
file name. Default:[]
.
Sacoge runs a swift package build plugin, it generate a bit of swift code to help handling static/immutable assets you want to serve. This is what it is generates:
- A type
public struct Asset { ... }
with information about asset locations in the local file syestem and it external access path. - Instance of the
Asset
type for each asset inside your public assets folder, accessed viaAsset.{dir}.{myFile_ext}
e.g.:Asset.img.my_img_png
. The first 4 bytes of a SHA-256 hash of the file contents is added to the external access path, so you can serve the asset with animmutable
cache policy. - A static constant in
Assets
containing a dictionary mapping external paths to internal paths of every asset.
When referencing assets in swift code, you would following change:
func myImg() -> String {
"""
- <img src="/img/my_img.jpg">
+ <img src="\(Asset.img.my_img_jpg)">
"""
}
If you get a request and want to know if there is an asset for that request, you can do the following:
// Some `Request` type that contains a URL with a PATH
let request: Request // you got this value from your server framework
guard let asset = Asset.externalToInternalMapping[request.uri.path] else {
// The asset does not exits, or was ignored by sacoge (you can configure which assets to ignore)
}
// Contains the local file system path to the asset relative to your public folder
// Example: If you have an asset at `./public/my_img.png`, the internalPath is `/my_img.png`
asset.internalPath
// The external path used in the request URL to access the asset. Contains the same value as `request.uri.path` that you used above.
asset.externalPath