Upload
others
View
2
Download
0
Embed Size (px)
Citation preview
What Code Generation Can Do for You
Marc-Antoine Sauvé Hopper
Hopper Creates Trust from DataWe process 300 billion flight prices each month to build trust and optimize customer interactions.
Repetitive tasks are
Boring Error prone Counterproductive
Sourcery
Code generation No reflection Flexible
What is it?Standalone application
What is it?Standalone application
Scan the source code
What is it?Standalone application
Scan the source code
Uses templates
What is it?Standalone application
Scan the source code
Uses templates
Generate source code
What is it?Standalone application
Scan the source code
Uses templates
Generate source code
Can read code and comments
public class Foo {}
Foo.swift
{% for type in types.all %}extension {{ type.name }} {{% if type.accessLevel == "public" %}
public var name: String {return "{{ type.name }}"
}{% endif %}}{% endfor %}
AutoClassName.stencil
public class Foo {}
Foo.swift
{% for type in types.all %}extension {{ type.name }} {{% if type.accessLevel == "public" %}
public var name: String {return "{{ type.name }}"
}{% endif %}}{% endfor %}
AutoClassName.stencil
public class Foo {}
Foo.swift
{% for type in types.all %}extension {{ type.name }} {{% if type.accessLevel == "public" %}
public var name: String {return "{{ type.name }}"
}{% endif %}}{% endfor %}
AutoClassName.stencil
public class Foo {}
Foo.swift
{% for type in types.all %}extension {{ type.name }} {{% if type.accessLevel == "public" %}
public var name: String {return "{{ type.name }}"
}{% endif %}}{% endfor %}
AutoClassName.stencil
public class Foo {}
Foo.swift
{% for type in types.all %}extension {{ type.name }} {{% if type.accessLevel == "public" %}
public var name: String {return "{{ type.name }}"
}{% endif %}}{% endfor %}
AutoClassName.stencil
public class Foo {}
extension Foo {public var name: String {
return "Foo"}
}
Foo.swift
AutoClassName.generated.swift
Sourcery
Before build Watch files Manual
Real Swift problemListing enum
public enum SuperPower { case HeatVision case SuperSpeed case SuperStrength case XrayVision case SuperIntelligence}
models.swift
protocol AutoCases { }public enum SuperPower: AutoCases { case HeatVision case SuperSpeed case SuperStrength case XrayVision case SuperIntelligence}
models.swift
{% for enum in types.implementing.AutoCases|enum %}{{ enum.accessLevel }} extension {{ enum.name }} { static public let count: Int = {{ enum.cases.count }} {% if not enum.hasAssociatedValues %} static public let allCases: [{{ enum.name }}] = [ {% for case in enum.cases %} .{{ case.name }}{% if not forloop.last %},{% endif %} {% endfor %}] {% endif %}}{% endfor %}
AutoCases.stencil
{% for enum in types.implementing.AutoCases|enum %}{{ enum.accessLevel }} extension {{ enum.name }} { static public let count: Int = {{ enum.cases.count }} {% if not enum.hasAssociatedValues %} static public let allCases: [{{ enum.name }}] = [ {% for case in enum.cases %} .{{ case.name }}{% if not forloop.last %},{% endif %} {% endfor %}] {% endif %}}{% endfor %}
AutoCases.stencil
{% for enum in types.implementing.AutoCases|enum %}{{ enum.accessLevel }} extension {{ enum.name }} { static public let count: Int = {{ enum.cases.count }} {% if not enum.hasAssociatedValues %} static public let allCases: [{{ enum.name }}] = [ {% for case in enum.cases %} .{{ case.name }}{% if not forloop.last %},{% endif %} {% endfor %}] {% endif %}}{% endfor %}
AutoCases.stencil
public extension SuperPower { static let count: Int = 5 static public let allCases: [SuperPower] = [ .HeatVision, .SuperSpeed, .SuperStrength, .XrayVision, .SuperIntelligence ]}
AutoCases.generated.swift
Sourcery
Readable code Debuggable DRY
Equatable
// swiftlint:disable file_length// [...] Removed some boilerplate to enhance readability{% macro compareVariables variables %} {% for variable in variables where variable.readAccess != "private" and variable.readAccess != "fileprivate" %}{% if not variable.annotations.skipEquality %}guard {% if not variable.isOptional %}{% if not variable.annotations.arrayEquality %}lhs.{{ variable.name }} == rhs.{{ variable.name }}{% else %}compareArrays(lhs: lhs.{{ variable.name }}, rhs: rhs.{{ variable.name }}, compare: ==){% endif %}{% else %}compareOptionals(lhs: lhs.{{ variable.name }}, rhs: rhs.{{ variable.name }}, compare: ==){% endif %} else { return false }{% endif %} {% endfor %}{% endmacro %}
// MARK: - AutoEquatable for classes, protocols, structs{% for type in types.implementing.AutoEquatable|!enum %}
// MARK: - {{ type.name }} AutoEquatable{% if not type.kind == "protocol" and not type.based.NSObject %}extension {{ type.name }}: Equatable {}{% endif %}{% if type.supertype.based.Equatable or type.supertype.implements.AutoEquatable %}THIS WONT COMPILE, WE DONT SUPPORT INHERITANCE for AutoEquatable{% endif %}{{ type.accessLevel }} func == (lhs: {{ type.name }}, rhs: {{ type.name }}) -> Bool { {% if not type.kind == "protocol" %} {% call compareVariables type.storedVariables %} {% else %} {% call compareVariables type.allVariables %} {% endif %} return true}{% endfor %}
// Keeps going for a while adding some code for enums
AutoEquatable.stencil
// MARK: - SuperHero AutoEquatableextension SuperHero: Equatable {}public func == (lhs: SuperHero, rhs: SuperHero) -> Bool { guard lhs.name == rhs.name else { return false } guard lhs.superPowers == rhs.superPowers else { return false } return true}
AutoEquatable.generated.swift
// MARK: - SuperPower AutoEquatableextension SuperPower: Equatable {}public func == (lhs: SuperPower, rhs: SuperPower) -> Bool { switch (lhs, rhs) { case (.HeatVision, .HeatVision): return true case (.SuperSpeed(let lhs), .SuperSpeed(let rhs)): if lhs.speed != rhs.speed { return false } if lhs.duration != rhs.duration { return false } return true // [ Skipped some code for readability ] default: return false }}
AutoEquatable.generated.swift
public class SuperHero: AutoEquatable { public let name: String // sourcery: skipEquality public let sidekick: SuperHero? public let superPowers: [SuperPower]}
Annotations
Sourcery
Test once Avoid human mistakes Low maintenance cost
ClosureType
DictionaryType
ArrayType TupleElement
TupleType
TypeName
Subscript
MethodParameter
Method
Variable
AssociatedValue
Enum
EnumCase
ClassTypes
Protocol
Struct
GenericType
GenericTypeParameter
Attribute
What’s bad?Hard to read the template
Hard to read the template
Hard to write the template
What’s bad?
Hard to read the template
Hard to write the template
Poor support in the IDEs
What’s bad?
Hard to read the template
Hard to write the template
Poor support in the IDEs
Can be slow to compile
What’s bad?
Hard to read the template
Hard to write the template
Poor support in the IDEs
Can be slow to compile
Annotations are not type checked
What’s bad?
AppCode
Visual Studio Code
Still worth it!Saves a lot of time!
Questions?
We’re on the Lookout for Talented People
Check out current openings at hopper.com/jobs.
Slides available oncocoaheadsmtl.com.