TestsTested | ✗ |
LangLanguage | Obj-CObjective C |
License | MIT |
ReleasedLast Release | Dec 2016 |
Maintained by Krzysztof Zabłocki.
Doing the same thing over and over again and expecting different results.
Swift is a beautiful language that powers a lot of great iOS apps. Unfortunately it features very limited runtime and no meta-programming features.
This has led our projects to contain a lot of duplicated code patterns, they can be considered the same code, just with minimal variations.
Have you ever?
If you did then you probably found yourself writing a lot of repetitive code to deal with those scenarios, does this feel right?
Even worse, if you ever add a new property to a type all of those implementations have to be updated, or you'll end up with bugs. In those scenarios usually compiler won't generate the error for you, which leads to error prone code.
Insanity is a tool that scans your source code, applies your personal templates and generates Swift code for you, allowing you to use meta-programming techniques to save time and decrease potential mistakes.
There are multiple benefits in using Insanity approach:
Daemon mode in action:
How everything connects:
+--------------+
Scans code to build AST | | Generates new code
+----------------------------> INSANITY +--------------------------------+
| | | |
| +--^--------^--+ |
| | | |
| | | Reads templates |
| | | |
+-----+------+ +----------------+--+ +--+----------------+ +---------v---------+
| | | | | | | |
| Source | | Equality Template | | NSCoding Template | | Generated Swift |
| | | | | | | |
+-----^------+ +-------------------+ +-------------------+ +-------------------+
| |
| |
| |
+----------------------------------------------------------------------------+
Compiled into your project
I want to know how many elements are in each enum
Template:
{% for enum in types.enums %}
extension {{ enum.name }} {
static var count: Int { return {{ enum.cases.count }} }
}
{% endfor %}
Result:
extension AdType {
static var count: Int { return 2 }
}
Template:
{% for type in types.implementing.AutoEquatable %}
extension {{ type.name }}: Equatable {}
func == (lhs: {{ type.name }}, rhs: {{ type.name }}) -> Bool {
{% for variable in type.storedVariables %} if lhs.{{ variable.name }} != rhs.{{ variable.name }} { return false }
{% endfor %}
return true
}
{% endfor %}
Result:
extension AccountSectionConfiguration: Equatable {}
func == (lhs: AccountSectionConfiguration, rhs: AccountSectionConfiguration) -> Bool {
if lhs.status != rhs.status { return false }
if lhs.user != rhs.user { return false }
if lhs.entitlements != rhs.entitlements { return false }
return true
}
Template:
{% for variable in type.VideoViewModel.computedVariables %} {{ variable.name }}: {{ variable.type }}
{% endfor %}
Result:
attributedTitle: NSAttributedString
attributedKicker: NSAttributedString
attributedHeadline: NSAttributedString
attributedSummary: NSAttributedString
Insanity templates are powered by Stencil
Make sure you leverage Insanity built-in daemon to make writing templates a pleasure: you can open template side-by-side with generated code and see it change live.
There are multiple ways to access your types:
type.TypeName
=> access specific type by nametypes.all
=> all types, excluding protocolstypes.classes
types.structs
types.enums
types.protocols
=> lists all protocols (that were defined in the project)types.inheriting.BaseClassOrProtocol
=> lists all types inheriting from given BaseClass or implementing given Protocoltypes.implementing.BaseClassOrProtocol
=> convience alias that works exactly the same as .inheriting
For each type you can access following properties:
name
localName
<- name within parent scopestaticVariables
<- list of static variablesvariables
<- list of instance variablescomputedVariables
<- list of computed instance variablesstoredVariables
<- list of computed stored variablesinheritedTypes
<- list of type names that this type implements / inheritscontainedTypes
<- list of types contained within this typeparentName
<- list of parent type (for contained ones)Enum types builts on top of regular types and adds:
rawType
<- enum raw typecases
<- list of Enum.Case
Enum.Case provides:
name
<- namerawValue
<- raw valueassociatedValues
<- list of AssociatedValue
Enum.Case.AssociatedValue provides:
name
<- nametype
<- type of associated valueVia CocoaPods
If you're using CocoaPods, you can simply add pod 'Insanity' to your Podfile.
This will download the Insanity binaries and dependencies in Pods/
.
You just need to add $PODS_ROOT/Insanity/bin/Insanity
in your Script Build Phases.
Manually
You can clone it from the repo and just run Insanity.xcworkspace
.
Insanity is a command line tool Insanity
:
$ ./Insanity <source> <templates> <output>
Arguments:
Options:
--watch
[default: false] - Watch template for changes and regenerate as needed. Only works with specific template path (not directory).--verbose
[default: false] - Turn on verbose logging for ignored entitiesContributions to Insanity are welcomed and encouraged!
Please see the Contributing guide.
A list of contributors is available through GitHub.
To give clarity of what is expected of our community, Insanity has adopted the code of conduct defined by the Contributor Covenant. This document is used across many open source communities, and I think it articulates my values well. For more, see the Code of Conduct.
Insanity is available under the MIT license. See LICENSE for more information.
This tool is powered by
Olivier Halligon pointed me to few of his setup scripts for CLI tools, very helpful, thank you!
If you want to generate code for asset related logic, I highly recommend SwiftGen
Make sure to check my other libraries and tools, especially:
You can follow me on twitter for news / updates about other projects I'm creating.