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