Last active
May 27, 2025 21:02
-
-
Save daltonclaybrook/cf59431cd84ff602996ccd7a693440bd to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
#if canImport(DeveloperToolsSupport) | |
import DeveloperToolsSupport | |
#endif | |
#if SWIFT_PACKAGE | |
private let resourceBundle = Foundation.Bundle.module | |
#else | |
private class ResourceBundleClass {} | |
private let resourceBundle = Foundation.Bundle(for: ResourceBundleClass.self) | |
#endif | |
// MARK: - Color Symbols - | |
@available(iOS 17.0, macOS 14.0, tvOS 17.0, watchOS 10.0, *) | |
extension DeveloperToolsSupport.ColorResource { | |
{COLOR_CONSTANTS} | |
} | |
// MARK: - Image Symbols - | |
@available(iOS 17.0, macOS 14.0, tvOS 17.0, watchOS 10.0, *) | |
extension DeveloperToolsSupport.ImageResource { | |
{IMAGE_CONSTANTS} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
load("@build_bazel_rules_swift//swift:swift_library.bzl", "swift_library") | |
load("//bazel:xcassets.bzl", "xcassets_accessors") | |
swift_library( | |
name = "MyLibrary", | |
srcs = glob([ | |
"**/*.swift", | |
]) + [ | |
":asset_accessors", | |
], | |
visibility = ["//visibility:public"], | |
) | |
xcassets_accessors( | |
name = "asset_accessors", | |
srcs = glob([ | |
"Resources/*.xcassets/**", | |
]), | |
out = "GeneratedAssets.swift", | |
) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
Defines the `xcassets_accessors` rule for generating accessors for colors and images in an asset catalog | |
""" | |
def _xcassets_accessor_impl(ctx): | |
colors = [] | |
images = [] | |
output = ctx.outputs.out | |
for src in ctx.files.srcs: | |
(_, _, subpath) = src.path.partition(".xcassets/") | |
if subpath == '': | |
fail("All srcs must be under an '.xcassets' folder. Invalid file:", src.path) | |
components = subpath.split("/") | |
if len(components) < 2 or components[-1] != "Contents.json": | |
continue # File is not a 'Contents.json' for an asset | |
asset = components[-2] | |
if asset.endswith(".colorset"): | |
colors.append(asset.removesuffix(".colorset")) | |
elif components[-2].endswith(".imageset"): | |
images.append(asset.removesuffix(".imageset")) | |
else: | |
continue # File is not a color or image asset | |
color_contents = _generate_color_contents(colors) | |
image_contents = _generate_image_contents(images) | |
ctx.actions.expand_template( | |
template = ctx.file._template, | |
output = output, | |
substitutions = { | |
"{COLOR_CONSTANTS}": color_contents, | |
"{IMAGE_CONSTANTS}": image_contents, | |
} | |
) | |
return [ | |
DefaultInfo(files = depset([output])), | |
] | |
def _generate_color_contents(colors): | |
if len(colors) == 0: | |
return " // No colors to generate" | |
strings = [] | |
for color in colors: | |
field = _convert_to_camel_case(color) | |
string = " static let {} = DeveloperToolsSupport.ColorResource(name: \"{}\", bundle: resourceBundle)".format(field, color) | |
strings.append(string) | |
return "\n".join(strings) | |
def _generate_image_contents(images): | |
if len(images) == 0: | |
return " // No images to generate" | |
strings = [] | |
for image in images: | |
field = _convert_to_camel_case(image) | |
string = " static let {} = DeveloperToolsSupport.ImageResource(name: \"{}\", bundle: resourceBundle)".format(field, image) | |
strings.append(string) | |
return "\n".join(strings) | |
def _convert_to_camel_case(s): | |
parts = s.replace("-", " ").replace("_", " ").split(" ") | |
if not parts: | |
return s | |
return _lowercase_first(parts[0]) + "".join([_capitalize_first(word) for word in parts[1:]]) | |
def _lowercase_first(s): | |
return s[0].lower() + s[1:] if s else s | |
def _capitalize_first(s): | |
return s[0].capitalize() + s[1:] if s else s | |
xcassets_accessors = rule( | |
doc = "Generate a Swift file containing accessors for colors and images in the provided asset catalog", | |
attrs = { | |
"srcs": attr.label_list( | |
doc = "The list of files under the '.xcassets' folder", | |
allow_files = True, | |
allow_empty = False, | |
mandatory = True, | |
), | |
"out": attr.output( | |
doc = "The name of the Swift output file to create", | |
mandatory = True, | |
), | |
"_template": attr.label( | |
default = "//bazel:templates/AssetAccessors.swift.tmpl", | |
allow_single_file = True, | |
), | |
}, | |
implementation = _xcassets_accessor_impl, | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment