This is a reference of all the useful links to follow my UIKonf'17 talk on Code Generation in Swift
- Slides on SpeakerDeck
- Keynote Live URL
- All the other files in this GIST contains the code snippets I used in my slides, referenced by slide number.
This is a reference of all the useful links to follow my UIKonf'17 talk on Code Generation in Swift
label.font = | |
FontFamily.Avenir.blackOblique.font(size: 18) | |
label.text = | |
L10n.homeGreetings("UIKonf", 1, "Berlin").string |
swiftgen fonts "…/Fonts" … | |
swiftgen strings "…/Localizable.strings" … | |
swiftgen images "…/Assets.xcassets" … | |
swiftgen storyboards "$PROJECT_DIR" … | |
swiftgen colors "…/colors.xml" … | |
… |
enum FontFamily { | |
enum Avenir: String { | |
case black = "Avenir-Black" | |
case blackOblique = "Avenir-BlackOblique" | |
case oblique = "Avenir-Oblique" | |
case roman = "Avenir-Roman" | |
func font(size: CGFloat) -> UIFont! { | |
return Font(font: self.rawValue, size: size) | |
} | |
} | |
} |
enum L10n { | |
/// Welcome to %@, the #%d iOS conference in %@! | |
case homeGreetings(String, Int, String) | |
/// Dismiss changes? | |
case editAlertTitle | |
/// Changes have been made to the image « %@ » | |
case editAlertMessage(String) | |
/// Save | |
case editAlertSave | |
/// Dismiss | |
case editAlertDismiss | |
} |
enum Assets { | |
static let apple = UIImage(named: "Apple") | |
static let banana = UIImage(named: "Banana") | |
static let orange = UIImage(named: "Orange") | |
static let somePears = UIImage(named: "Some-Pears") | |
} | |
// vs | |
enum Assets: String { | |
case apple = "Apple" | |
case banana = "Banana" | |
case orange = "Orange" | |
case somePears = "Some-Pears" | |
var image: UIImage { | |
return UIImage(named: self.rawValue) | |
} | |
} |
enum Assets { | |
{% for image in images %} | |
static let {{image|swiftIdentifier}} = UIImage(named: "{{image}}") | |
{% endfor %} | |
} |
enum Assets: String { | |
{% for image in images %} | |
case {{image|swiftIdentifier|lowerFirstWord}} = "{{image}}" | |
{% endfor %} | |
var image: UIImage { | |
return UIImage(asset: self) | |
} | |
} | |
extension UIImage { | |
convenience init!(asset: Assets) { | |
self.init(named: asset.rawValue) | |
} | |
} |
struct ImageInfo: Equatable { | |
let title: String | |
let author: String | |
let date: Date | |
} | |
func == (lhs: ImageInfo, rhs: ImageInfo) -> Bool { | |
guard lhs.title == rhs.title else { return false } | |
guard lhs.author == rhs.author else { return false } | |
guard lhs.date == rhs.date else { return false } | |
return true | |
} |
struct ImageInfo: Equatable { | |
let title: String | |
let author: String | |
let date: Date | |
let cameraModel: String | |
let kind: ImageKind | |
} | |
func == (lhs: ImageInfo, rhs: ImageInfo) -> Bool { | |
guard lhs.title == rhs.title else { return false } | |
guard lhs.author == rhs.author else { return false } | |
guard lhs.date == rhs.date else { return false } | |
return true | |
} |
// Template: | |
There are {{types.all.count}} types in this code, including: | |
- {{types.enums.count}} enums | |
- {{types.structs.count}} structs | |
- {{types.classes.count}} classes | |
// Generated Code: | |
There are 22 types in this code, including: | |
- 11 enums | |
- 1 structs | |
- 6 classes |
struct ImageInfo { | |
let title: String | |
let author: String | |
let date: Date | |
} |
extension ImageInfo: Equatable {} | |
func == (lhs: ImageInfo, rhs: ImageInfo) -> Bool { | |
guard lhs.title == rhs.title | |
else { return false } | |
guard lhs.author == rhs.author | |
else { return false } | |
guard lhs.date == rhs.date | |
else { return false } | |
return true | |
} |
{% for type in types.implementing.AutoEquatable %} | |
extension {{ type.name }}: Equatable {} | |
func == (lhs: {{ type.name }}, rhs: {{ type.name }}) -> Bool { | |
{% for variable in type.storedVariables %} guard lhs.{{ variable.name }} == rhs.{{ variable.name }} else { return false } | |
{% endfor %} | |
return true | |
} | |
{% endfor %} |
struct ImageInfo : AutoEquatable { | |
let title: String | |
let author: String | |
let date: Date | |
} | |
// + `sourcery/bin/sourcery` | |
// = Magic |
struct Contact { | |
let id: String | |
let firstName: String | |
let lastName: String | |
} |
extension Contact: JSONDeserializable { | |
init?(json: [String: Any]) { | |
self.id = json["id"] as? String | |
self.firstName = json["first_name"] as? String | |
self.lastName = json["last_name"] as? String | |
self.dateOfBirth = (json["dob"] as? String) | |
.flatMap(JSONDateFormatter.date(from:)) | |
self.avatar = (json["avatar"] as? [String: Any]) | |
.flatMap(Avatar.init(json:)) | |
} | |
} |
{% for type in types.implementing.AutoJSONDeserializable %} | |
extension {{ type.name }}: JSONDeserializable { | |
init?(json: [String: Any]) { | |
{% for prop in type.storedVariables %} | |
// ? | |
{% endfor %} | |
} | |
} | |
{% endfor %} |
// daemon mode to write your templates live: | |
$ sourcery/bin/sourcery --watch |
// Template (extract) | |
{% for prop in type.storedVariables %} | |
self.{{prop.name}} = json["{{prop.name}}"] as? {{prop.typeName}} | |
{% endfor %} | |
// Generated code | |
self.id = json["id"] as? String | |
self.firstName = json["firstName"] as? String | |
self.lastName = json["lastName"] as? String |
// Your code | |
struct Contact { | |
let id: String | |
// sourcery: JSONKey = "first_name" | |
let firstName: String | |
// sourcery: JSONKey = "last_name" | |
let lastName: String | |
} | |
// How to reference that in your template: | |
{% for prop in type.storedVariables %} | |
json["{{ prop.annotations.JSONKey |default:prop.name }}"] | |
{% endfor %} |
// Template | |
{% for prop in type.storedVariables %} | |
self.{{prop.name}} = json["{{ prop.annotations.JSONKey |default:prop.name }}"] as? {{prop.typeName}} | |
{% endfor %} | |
// Generated Code | |
self.id = json["id"] as? String | |
self.firstName = json["first_name"] as? String | |
self.lastName = json["last_name"] as? String |
struct Customer: AutoEquatable, AutoJSONDeserializable { | |
… // 15 properties | |
} | |
struct Product: AutoEquatable, AutoJSONDeserializable { | |
… // 28 properties | |
} | |
struct Cart: AutoEquatable, AutoJSONDeserializable { | |
… // 12 properties | |
} | |
struct Order: AutoEquatable, AutoJSONDeserializable { | |
… // 17 properties | |
} | |
enum ShippingOption: AutoCases { | |
… // 9 cases | |
} |
// sourcery: TypeErase = PokemonType | |
protocol Pokemon { | |
associatedtype PokemonType | |
func attack(move: PokemonType) | |
} | |
// + github.com/AliSoftware/SourceryTemplates | |
// = | |
let list: [AnyPokemon<Thunder>] = … |
Building an API Client using Sourcery Annotations
Generate JSON Parsing code
Automatic Type Erasure
Krzysztof’s talk at CraftConf about Sourcery
My Live Demo of SwiftGen at NSBudapest
Improve SwiftGen