Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/alisoftware/codegen-workshop

This is the repository used for my SwiftAveiro'19 Workshop about Code Generation
https://github.com/alisoftware/codegen-workshop

Last synced: about 1 month ago
JSON representation

This is the repository used for my SwiftAveiro'19 Workshop about Code Generation

Awesome Lists containing this project

README

        

# Code Generation Workshop

Learn to use SwiftGen & Sourcery to avoid boilerplate and improve your productivity. CodeGen also allows you to provide more type safety & make your code maintenance!

## Requirements

* This workshop expects that you already **know how to write Swift 4+ code** and have written a few (ideally iOS) applications.
* We'll use Xcode 10.2, but older Xcodes should work too

## Resources

During the workshop, we'll use the following websites

* [SwiftGen](https://github.com/SwiftGen/SwiftGen) & [Sourcery](https://github.com/krzysztofzablocki/Sourcery) repositories for their README & documentation
* [Sourcery dedicated documentation's website](https://cdn.rawgit.com/krzysztofzablocki/Sourcery/master/docs/index.html)
* [Stencil documentation](http://stencil.fuller.li/en/latest/builtins.html)
* [StencilSwiftKit](https://github.com/SwiftGen/StencilSwiftKit) for additional Stencil tags and filters documentation

---

## Workshop Steps

### Stage 0: Discover the project

> _Note: This project is in no way intended to be a model of good architecture_ 😅 _It has been crafted specifically to feature as many possible use cases for Code Generation as possible while trying to keep the app as simple as possible._ 😉

* Browse the source code quickly
* Compile and launch the project in Xcode – ow, it crashes, what a good start!
* Identify the issues with hardcoded values and crashes:
* Localization: wrong keys, untranslated 🚫
* Images: wrong keys, crash 💥
* Fonts: wrong name, some missing in Info.plist, fallback to system font 🚫
* Storyboards: hardcoded Storyboard and Scene names, crash 💥
* Models: Lots to data to type by hand ⌨️
* Info.plist : hardcoded keys 🚫
* Lottie: hardcoded name, one animation not found 🚫
* Identify the annoying repetition of code
* Big switches just to identify a name property
* Image associated with Item should be derived from item case
* Equatable implementations for enums
* ...

### Stage 1: Discover SwiftGen and its Build-in templates

* Install SwiftGen via CocoaPods (†)
* Read the doc about the Config File format, play with `swiftgen --help` and `swiftgen templates list`
* Make your first `swiftgen.yml` and generate constants for xcassets via the command line
* Fix the code that is using images and colors
* Add the generated file to your project
* Start using the generated constants in the code, see that some are missing
* Install SwiftGen as a Build Phase in your project so that you get constant updates
* Add `InputData/[email protected]` and `InputData/[email protected]` to your Assets Catalog and rebuild to see new constants.
* Continue with all the other types of resources
* Fonts to `swiftgen.yml` – Don't forget to remove them from the Info.plist too!
* Storyboards and Scene names
* `NSLocalizedString`
* Hardcoded `CFBundleName` from `Info.plist`
* Bonus: Play a bit by renaming your image assets or Storyboard IDs and see how the changes are now warning you at compile time about where to change their references

_(†) Alternatively you can download the ZIP file for [SwiftGen](https://github.com/SwiftGen/SwiftGen/releases/latest) and unzip it in your repo._

### Stage 2: Custom SwiftGen Templates

* Discover Stencil doc
* Create a Lottie template to list all animations, add generated code to Xcode, replace call sites to fix the wrong animation name. In addition to generate the name of JSON files, also try to extract the width and height of the animations from the JSON content.
* Create templates for YAML model files (see `InputData/`), in order to generate all the model objects (replacing the content of files in `Generated/`

### Stage 3: Discover Sourcery

* Install Sourcery via CocoaPods (†)
* Install it as a Build Phase in your project
* Take a look at its README and online documentation
* Use the `AutoEquatable` template and make it work on the `Item` type.
_(You might want to duplicate and customize the template to remove the `public` access modifier)_ 😉
* Bonus: Use Sourcery Annotations `// sourcery:skipEquality` to exclude some fields from `AutoEquatable` on models (e.g. `id`)

_(†) Alternatively you can download the ZIP file for [Sourcery](https://github.com/krzysztofzablocki/Sourcery/releases/latest) and unzip it in your repo._

### Stage 4: Custom Sourcery Templates

* Create Custom template listing all `Model` types. Use the `--watch` mode to iterate
* Make the template evolve to re-generate the `enum Item` based on those `Model`-conforming types
* Add the generation of the `image` and `description` properties and the `Encodable` conformance
* Add a custom annotation to identify the name property and generate the `var name` from it
* Make the `func item() -> Item` in `extension ID`/`ID.swift` be generated by Sourcery instead

### Stage 5: Generate all fields for all items

* Make `Item+Fields.swift` be generated by Sourcery
* Use a custom annotation to exclude the `openingCrawl` property from the fields
* Make `ItemStore.swift` and its `subscript` and `static let filters` be generated by Sourcery

### Stage 6: Add and rename models and see the magic happen

* Mark the `Planet` to conform to `Model` and change `Person.homeworld: ID` and `Film.planets: [ID]`
* Check that rebuilding – and thus re-running Sourcery – should now update all the places where we depend on Item cases (`ItemStore.generated.swift`, `Item+Fields.generated.swift`, `Item.generated.swift`)
* Change your template for `Item+Fields.generated.swift` to generate `person.homeworld.displayName`

### Stage 7: Rename things, Ignore properties for Encodable

* Rename `struct Film` to `struct Movie` and every `ID` into `ID`, then ensure everything changes automatically in the rest of the code too.
_Notice that the compiler will prompt us to rename the corresponding image too. Rename it and see SwiftGen re-generate the proper constants_
* Custom Encodable for Models, mark some properties like `id` or `openingCrawl ` to be ignored

### Stage 8: Inline code generation

* Change your model types to be classes.
* Since classes require an explicit `init`, you'll have to generate one... and it has to be declared in the original type implementation (you can't declare it in an `extension`). So we're gonna need to make Sourcery generate code _inline_, inside our existing code
* Look at the Sourcery documentation about Writing Templates – see how to achieve that with special `// sourcery:inline:...` annotations, and make a template to generate that `init` code.
* Tip: when looping over all your `storedVariables`, you can check `forloop.last` to see if you're on the last element of your list and omit the comma in that case.

### Going Further

* [EnumValue.stencil](https://gist.github.com/AliSoftware/691eee22464b6f9e1796a85111a9c2ee)
* [Type Erasure Template](https://github.com/AliSoftware/SourceryTemplates#type-erasure)
* [Protocol Composition for Dependency Injection](https://github.com/AliSoftware/SourceryTemplates#autopropertiesprotocol)
* [Sourcery's Swift Templates](https://cdn.rawgit.com/krzysztofzablocki/Sourcery/master/docs/writing-templates.html#swift-templates)
* [Sourcery Inline Code Generation](https://cdn.rawgit.com/krzysztofzablocki/Sourcery/master/docs/writing-templates.html#inline-code-generation)