Call the script like this:
./palette-generator.swift example-input.json
It will create a folder Colors in the folder where this script is executed, containing output.
| { | |
| "paletteColors": { | |
| "blue": "#0000FF", | |
| "red": "#FF0000" | |
| }, | |
| "projectColorReferences": { | |
| "title": "blue", | |
| "subtitle": "red" | |
| } | |
| } |
| #!/usr/bin/swift | |
| import Foundation | |
| struct PaletteInput: Decodable, Encodable { | |
| let paletteColors: [String: String] | |
| let projectColorReferences: [String: String] | |
| } | |
| func hexStringToUInt32(_ hex: String) -> UInt32 { | |
| var cString:String = hex.trimmingCharacters(in: .whitespacesAndNewlines).uppercased() | |
| if (cString.hasPrefix("#")) { | |
| cString.remove(at: cString.startIndex) | |
| } | |
| if ((cString.count) != 6) { | |
| return 0 | |
| } | |
| var rgbValue:UInt32 = 0 | |
| Scanner(string: cString).scanHexInt32(&rgbValue) | |
| return rgbValue | |
| } | |
| func redFromHex(_ hexColor: String) -> CGFloat { | |
| return CGFloat((hexStringToUInt32(hexColor) & 0xFF0000) >> 16) / 255.0 | |
| } | |
| func greenFromHex(_ hexColor: String) -> CGFloat { | |
| return CGFloat((hexStringToUInt32(hexColor) & 0x00FF00) >> 8) / 255.0 | |
| } | |
| func blueFromHex(_ hexColor: String) -> CGFloat { | |
| return CGFloat(hexStringToUInt32(hexColor) & 0x0000FF) / 255.0 | |
| } | |
| func createColorAssetJson(hexColor: String) -> String { | |
| return """ | |
| { | |
| "info" : { | |
| "version" : 1, | |
| "author" : "xcode" | |
| }, | |
| "colors" : [ | |
| { | |
| "idiom" : "universal", | |
| "color" : { | |
| "color-space" : "srgb", | |
| "components" : { | |
| "red" : "\(redFromHex(hexColor))", | |
| "alpha" : "1.0", | |
| "blue" : "\(blueFromHex(hexColor))", | |
| "green" : "\(greenFromHex(hexColor))" | |
| } | |
| } | |
| } | |
| ] | |
| } | |
| """ | |
| } | |
| func createPaletteEnum(paletteColors: [String: String]) -> String { | |
| let colorLines = paletteColors.map { arg -> (String) in | |
| let (name, hex) = arg | |
| return "\t\tstatic let \(name): UIColor = UIColor(red: \(redFromHex(hex)), green: \(greenFromHex(hex)), blue: \(blueFromHex(hex)), alpha: 1.0)" | |
| }.joined(separator: "\n") | |
| return """ | |
| \tenum Palette { | |
| \(colorLines) | |
| \t} | |
| """ | |
| } | |
| func createColorsEnum(paletteInput: PaletteInput) -> String { | |
| let colorLines = paletteInput.projectColorReferences.map { arg -> (String) in | |
| let (name, paletteRef) = arg | |
| return "\tstatic let \(name): UIColor = Palette.\(paletteRef)" | |
| }.joined(separator: "\n") | |
| return """ | |
| import UIKit | |
| enum Colors { | |
| \(createPaletteEnum(paletteColors: paletteInput.paletteColors)) | |
| \(colorLines) | |
| } | |
| """ | |
| } | |
| func printHelp() { | |
| print("Script should be called like this: ./palette-generator path-to-palette-file") | |
| } | |
| func exitWithMessage(_ message: String = "") -> Never { | |
| print(message) | |
| printHelp() | |
| return exit(1) | |
| } | |
| if CommandLine.arguments.count < 2 { | |
| exitWithMessage() | |
| } | |
| let paletteFilePath = CommandLine.arguments[1] | |
| guard let paletteData = FileManager.default.contents(atPath: paletteFilePath) | |
| else { | |
| exitWithMessage("File at path '\(paletteFilePath)' not found") | |
| } | |
| guard let paletteInput = try? JSONDecoder().decode(PaletteInput.self, from: paletteData) | |
| else { | |
| exitWithMessage("Palette Input has wrong format") | |
| } | |
| // Color Asset Files | |
| let paletteColors = paletteInput.paletteColors.map { arg -> (String, String) in | |
| let (name, hexColor) = arg | |
| return (name.capitalized(with: nil), createColorAssetJson(hexColor: hexColor)) | |
| } | |
| let projectColors = paletteInput.projectColorReferences.map { arg -> (String, String) in | |
| let (name, paletteRef) = arg | |
| let hexColor = paletteInput.paletteColors[paletteRef] | |
| return (name.capitalized(with: nil), createColorAssetJson(hexColor: hexColor!)) | |
| } | |
| var hadError = false | |
| let colorsDir = "./Colors" | |
| (paletteColors + projectColors).forEach { (name, json) in | |
| do { | |
| let colorDir = colorsDir + "/\(name).colorset" | |
| try FileManager.default.createDirectory( | |
| atPath: colorDir, withIntermediateDirectories: true, attributes: nil | |
| ) | |
| FileManager.default.createFile( | |
| atPath: "\(colorDir)/Contents.json", | |
| contents: json.data(using: .utf8), | |
| attributes: nil | |
| ) | |
| } catch { | |
| print(error.localizedDescription) | |
| hadError = true | |
| } | |
| } | |
| if hadError { | |
| print("Some colors were not created") | |
| exit(1) | |
| } | |
| FileManager.default.createFile( | |
| atPath: "\(colorsDir)/Colors.swift", | |
| contents: createColorsEnum(paletteInput: paletteInput).data(using: .utf8), | |
| attributes: nil | |
| ) | |
| print("SUCCESS!") | |
| exit(0) |