Last active
July 11, 2016 21:36
-
-
Save fpg1503/cd25efb3ddf280811f914a78c4aebe1b to your computer and use it in GitHub Desktop.
ViewSpitter: Programmatic UIView Boilerplate generator
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
extension String { | |
mutating func appendLine(line: String = "") { | |
self += "\n\(line)" | |
} | |
mutating func appendMark(mark: String) { | |
self.appendLine() | |
self.appendLine("//MARK - \(mark)") | |
self.appendLine() | |
} | |
} | |
struct ViewModel: ArrayLiteralConvertible { | |
typealias Element = String | |
let name: String | |
let type: String | |
let style: String | |
let color: String | |
init(arrayLiteral elements: ViewModel.Element...) { | |
name = elements[0] | |
type = elements[1] | |
style = elements[2] | |
color = elements [3] | |
} | |
} | |
struct ClassModel { | |
let className: String | |
let superClass: String | |
let views: [ViewModel] | |
let viewRelations: [(String, String)] | |
} | |
extension ClassModel { | |
func generateBoilerplate() -> String { | |
// Header | |
var result = "" | |
result.appendLine("import UIKit") | |
result.appendLine("import Cartography") | |
result.appendLine() | |
result.appendLine("final class \(className): \(superClass) {") | |
//View declaration | |
for viewModel in views { | |
result.appendLine("private var \(viewModel.name): \(viewModel.type)? {") | |
result.appendLine("didSet {") | |
result.appendLine("guard let \(viewModel.name) = \(viewModel.name) where \(viewModel.name) != oldValue else { return }") | |
result.appendLine() | |
result.appendLine("\(viewModel.name).setStyle(.\(viewModel.style))") | |
result.appendLine("\(viewModel.name).textColor = .\(viewModel.color)Color()") | |
result.appendLine("addSubview(\(viewModel.name))") | |
result.appendLine("}") | |
result.appendLine("}") | |
result.appendLine() | |
} | |
//Designated initializer | |
result.appendMark("Designated initializer") | |
result.appendLine("required init?(coder aDecoder: NSCoder) {") | |
result.appendLine("super.init(coder: aDecoder)") | |
result.appendLine("setupViews()") | |
result.appendLine("}") | |
result.appendLine("}") | |
//Configuration | |
result.appendMark("Configuration") | |
result.appendLine("extension \(className) {") | |
result.appendLine("func configureWith(viewModel viewModel: Any) {") | |
result.appendLine("//FIXME: Replace me with ViewModel and implement me") | |
result.appendLine("}") | |
result.appendLine("}") | |
//Layout | |
result.appendMark("Layout") | |
result.appendLine("private extension \(className) {") | |
result.appendLine("private func setupViews() {") | |
for viewModel in views { | |
result.appendLine("\(viewModel.name) = \(viewModel.type)()") | |
} | |
result.appendLine("setupConstraints()") | |
result.appendLine("}") | |
result.appendLine() | |
result.appendLine("private func setupConstraints() {") | |
if let first = views.first { | |
var line = "guard let \(first.name) = \(first.name)" | |
let otherViews = Array(views.dropFirst()) | |
if otherViews.count == 0 { | |
line += " else {" | |
result.appendLine(line) | |
} else { | |
line += "," | |
result.appendLine(line) | |
for (index, view) in otherViews.enumerate() { | |
var shadowing = "\(view.name) = \(view.name)" | |
shadowing += index == otherViews.count - 1 ? " else {" : "," | |
result.appendLine(shadowing) | |
} | |
} | |
result.appendLine("print(\"[WARNING]: Setting subviews failed\")") | |
result.appendLine("return") | |
result.appendLine("}") | |
} | |
result.appendLine() | |
if viewRelations.count > 0 { | |
result.appendLine("//TODO: Layout constraints") | |
result.appendLine() | |
for (first, second) in viewRelations { | |
let translatedFirst = first == "self" ? "view" : first | |
let translatedSecond = second == "self" ? "view" : second | |
result.appendLine("constrain(\(first), \(second)) { \(translatedFirst), \(translatedSecond) in") | |
result.appendLine("//FIXME: Add constraints") | |
result.appendLine("}") | |
result.appendLine() | |
} | |
} else if let first = views.first { | |
result.appendLine("/*") | |
result.appendLine("constrain(\(first.name), self) { \(first.name), view in") | |
result.appendLine("\(first.name).leading == view.leading + 20") | |
result.appendLine("\(first.name).top == view.top + 20") | |
result.appendLine("}") | |
result.appendLine("*/") | |
} | |
result.appendLine("}") | |
result.appendLine("}") | |
return result | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment