Last active
August 29, 2015 14:22
-
-
Save KingOfBrian/d0e91c3f63c94398f349 to your computer and use it in GitHub Desktop.
Code Generation of .xcasset resources
This file contains 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 | |
/** | |
* Build script to generate a type safe wrapper around your projects .xcasset file. | |
* This will fail your build if you reference an image in your .xcasset file that has | |
* changed or been removed, as well as provide code completion help for all your images. | |
* | |
* Copy this file into a new `Run Phase` in your project, with `/usr/bin/env xcrun swift` | |
* specified for `Shell`. | |
* | |
* Configure the variables below: | |
* | |
* - outputPath: The location to write the generated file. This file must be manually | |
* added to your .xcodeproj. | |
* - path: The location to look for .imageset files (Which reside inside the .xcasset directory) | |
* - objcCompatible: A flag to use static methods or an enum. | |
*/ | |
private struct Parameters { | |
static let outputFile = "./Assets.swift" | |
static let inputPath = "." | |
static let objcCompatible = false | |
static let separationCharacters = NSCharacterSet(charactersInString: "-_ ") | |
static let indentWidth = 4 | |
} | |
// Transform image names in the xcasset to Tokens for use in code | |
func stringToCamelCase(input:String) -> String { | |
let parts = input.componentsSeparatedByCharactersInSet(Parameters.separationCharacters) | |
return parts.reduce("") { methodName, part in | |
methodName + (methodName == "" ? part : part.capitalizedString) | |
} | |
} | |
func stringToUpperCase(input:String) -> String { | |
let parts = input.componentsSeparatedByCharactersInSet(Parameters.separationCharacters) | |
return parts.reduce("") { methodName, part in | |
methodName + part.capitalizedString | |
} | |
} | |
// Formatting helper | |
func indent(lines:[String], level:Int = 1) -> [String] { | |
let space = "".join((0..<Parameters.indentWidth * level).map { (x:Int) in return " "}) | |
return lines.map { "\(space)\($0)" } | |
} | |
// Functions to transform image names to lines of code | |
func imageNameToSwiftStaticFunc(imageName:String) -> String { | |
let methodName = stringToCamelCase(imageName) | |
return "static func \(methodName)() -> UIImage { return UIImage(named: \"\(imageName)\")! }" | |
} | |
func imageNameToSwiftEnum(imageName:String) -> String { | |
let methodName = stringToUpperCase(imageName) | |
return "case \(methodName) = \"\(imageName)\"" | |
} | |
// Higher level transforms from image names to compilable code | |
let generated = ["/*", | |
" * WARNING: This file is automatically generated based on the contents", | |
" * of your .xcasset files. Do not modify.", | |
" */"] | |
func swiftImageExtension(imageNames:[String]) -> String { | |
let functions = indent(imageNames.map(imageNameToSwiftStaticFunc)) | |
var lines = ["import UIKit", "extension UIImage {", "}"] | |
lines.splice(functions, atIndex: 2) | |
lines.splice(generated, atIndex: 0) | |
return "\n".join(lines) | |
} | |
func swiftImageEnum(imageNames:[String]) -> String { | |
let functions = indent(imageNames.map(imageNameToSwiftEnum), level:2) | |
var lines = ["import UIKit", | |
"extension UIImage {", | |
" enum AssetName : String {", | |
" }", | |
" convenience init!(asset:AssetName) {", | |
" self.init(named:asset.rawValue)", | |
" }", | |
"}"] | |
lines.splice(functions, atIndex: 3) | |
lines.splice(generated, atIndex: 0) | |
return "\n".join(lines) | |
} | |
// Obtain image names and pass them to a higher level transform function | |
if let paths = NSFileManager.defaultManager().subpathsAtPath(Parameters.inputPath) { | |
let imageNames = paths.filter { | |
$0.pathExtension == "imageset" | |
}.map { (x:String) -> String in | |
x.lastPathComponent.stringByDeletingPathExtension | |
} | |
let body = Parameters.objcCompatible ? swiftImageExtension(imageNames) : swiftImageEnum(imageNames) | |
try! body.writeToFile(Parameters.outputFile, atomically:true, encoding: NSUTF8StringEncoding) | |
} | |
else { | |
print(" warning: Unable to find any imageset files in \(Parameters.inputPath)") | |
} |
Hi @KingOfBrian,
You might be interested to know that I developed a similar tool to generate enums for assets — which actually can also generate enums for various other stuff like UIStoryboards and Localizable.strings, etc…
Anyway, nice to see that we had a similar idea, great minds think alike 😉
Don't hesitate to make suggestions or PR to my code if you see anything that could be improved)
Hey @AliSoftware -- That looks awesome! Will definitely point people in that direction instead.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks @jwatson! I updated indent to use map + join instead too. Pretty sure it's slightly worse, but it doesn't have any
var
's! Also, I dropped return from the map functions. Not sure if it's correct or better, but thought i'd give it a shot.