Skip to content

Instantly share code, notes, and snippets.

@nathantannar4
Created September 30, 2018 22:04
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nathantannar4/c32c8d151999410d81507f0f78dd8728 to your computer and use it in GitHub Desktop.
Save nathantannar4/c32c8d151999410d81507f0f78dd8728 to your computer and use it in GitHub Desktop.
Autogenerate an Xcode TemplateInfo.plist file based on current project
/*
XCodeTemplateGenerator.swift
Created by Nathan Tannar
Usage:
Run `swift XCodeTemplateGenerator.swift $SOURCE_FOLDER` where SOURCE_FOLDER is the root directory of your
XCode projects source code. A TemplateInfo.plist will be generated in the SOURCE_FOLDER allowing you
to copy the folder to your XCodes project template folder.
*/
import Foundation
guard CommandLine.arguments.count > 1 else {
print("Usage: swift XCodeTemplateGenerator.swift $SOURCE_FOLDER")
exit(0)
}
let directoryURL = URL(fileURLWithPath: FileManager.default.currentDirectoryPath, isDirectory: true)
let sourceFolder = "/" + CommandLine.arguments[1].replacingOccurrences(of: "/", with: "")
let plistURL = directoryURL.appendingPathComponent(sourceFolder).appendingPathComponent("TemplateInfo.plist")
func savePropertyList(_ plist: Any) throws {
let plistData = try PropertyListSerialization.data(fromPropertyList: plist, format: .xml, options: 0)
try plistData.write(to: plistURL)
}
func loadPropertyList() throws -> [String: Any] {
let data = try Data(contentsOf: plistURL)
guard let plist = try PropertyListSerialization.propertyList(from: data, format: nil) as? [String: Any] else {
return [String: Any]()
}
return plist
}
func shouldCreatePropertyList() -> Bool {
do {
_ = try loadPropertyList()
return false
} catch {
return true
}
}
func createPropertyList() throws {
var plist = [String: Any]()
plist["Kind"] = "Xcode.Xcode3.ProjectTemplateUnitKind"
plist["Identifier"] = "com.xcprojecttemplategen.default"
plist["Ancestors"] = ["com.apple.dt.unit.coreDataCocoaTouchApplication"]
plist["Concrete"] = true
plist["Description"] = "Default Description"
plist["SortOrder"] = 1
setNodes(in: &plist, to: [])
plist["Definitions"] = [:]
plist["Template Author"] = "Nathan Tannar @nathantannar4"
try savePropertyList(plist)
}
func setNodes(in plist: inout [String: Any], to nodes: [String]) {
var options = [Any]()
var units = [String: Any]()
var files = [String: Any]()
files["Nodes"] = nodes
units["Swift"] = files
options.append(["Identifier": "languageChoice", "Units": units])
plist["Options"] = options
}
func setDefinition(in plist: inout [String: Any], key: String, to definition: [String: Any]) {
var definitions = (plist["Definitions"] as? [String: Any]) ?? [:]
definitions[key] = definition
plist["Definitions"] = definitions
}
func makeDefinition(path: String, relativeFilePath: String) -> [String: Any] {
var definition = [String: Any]()
definition["Path"] = path
definition["Group"] = relativeFilePath.components(separatedBy: "/").filter { return !$0.isEmpty }
return definition
}
if shouldCreatePropertyList() {
try! createPropertyList()
}
var plist = try! loadPropertyList()
setNodes(in: &plist, to: [])
plist["Definitions"] = [:]
let resourceKeys: [URLResourceKey] = [.isDirectoryKey, .nameKey]
let enumerator = FileManager.default.enumerator(at: directoryURL, includingPropertiesForKeys: resourceKeys, options: [.skipsHiddenFiles], errorHandler: { (url, error) -> Bool in
fatalError("directoryEnumerator error at \(url): \(error)")
})!
var nodes = [String]()
let keySet = Set(resourceKeys)
let basePath = directoryURL.path
let extensions = [".swift", ".plist", ".storyboard", ".xib", ".strings"]
for case let fileURL as URL in enumerator {
let resourceValues = try! fileURL.resourceValues(forKeys: keySet)
let name = resourceValues.name!
let pathFromBase = fileURL.path.replacingOccurrences(of: basePath, with: "")
if resourceValues.isDirectory == false {
if pathFromBase.hasPrefix(sourceFolder) && name != "TemplateInfo.plist" {
if extensions.first(where: { name.hasSuffix($0) }) != nil {
let key = pathFromBase.replacingOccurrences(of: sourceFolder + "/", with: "")
nodes.append(key)
let relativeFilePath = pathFromBase.replacingOccurrences(of: sourceFolder, with: "").replacingOccurrences(of: name, with: "")
let definition = makeDefinition(path: key, relativeFilePath: relativeFilePath)
setDefinition(in: &plist, key: key, to: definition)
}
} else if name == "Podfile" || name == "Cartfile" {
let key = "../" + pathFromBase.replacingOccurrences(of: "/", with: "")
nodes.append(key)
let definition = ["Path": name]
setDefinition(in: &plist, key: key, to: definition)
}
}
}
setNodes(in: &plist, to: nodes)
try! savePropertyList(plist)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment