All Projects → pointfreeco → Swift Enum Properties

pointfreeco / Swift Enum Properties

Licence: mit
🤝 Struct and enum data access in harmony.

Programming Languages

swift
15916 projects

Projects that are alternatives of or similar to Swift Enum Properties

sum types.dart
A code generator enabling sum-types in Dart
Stars: ✭ 39 (-79.58%)
Mutual labels:  algebraic-data-types, code-generation
Activej
ActiveJ is an alternative Java platform built from the ground up. ActiveJ redefines web, high load, and cloud programming in Java, featuring ultimate performance and scalability!
Stars: ✭ 183 (-4.19%)
Mutual labels:  code-generation
Shortbread
Android library that creates app shortcuts from annotations
Stars: ✭ 1,803 (+843.98%)
Mutual labels:  code-generation
Go Jsonschema
A tool to generate Go data types from JSON Schema definitions.
Stars: ✭ 164 (-14.14%)
Mutual labels:  code-generation
Xcassetpacker
A command line tool for converting a folder of images into an .xcasset package for Xcode
Stars: ✭ 150 (-21.47%)
Mutual labels:  code-generation
Glad
Multi-Language Vulkan/GL/GLES/EGL/GLX/WGL Loader-Generator based on the official specs.
Stars: ✭ 2,296 (+1102.09%)
Mutual labels:  code-generation
Umple
Umple: Model-Oriented Programming - embed models in code and vice versa and generate complete systems
Stars: ✭ 147 (-23.04%)
Mutual labels:  code-generation
Yasha
A command-line tool to render Jinja templates for great good
Stars: ✭ 189 (-1.05%)
Mutual labels:  code-generation
Re2dfa
Transform regular expressions into finite state machines and output Go source code. This repository has migrated to https://gitlab.com/opennota/re2dfa
Stars: ✭ 182 (-4.71%)
Mutual labels:  code-generation
Folktale
[not actively maintained!] A standard library for functional programming in JavaScript
Stars: ✭ 1,995 (+944.5%)
Mutual labels:  algebraic-data-types
Fastapi Crudrouter
A dynamic FastAPI router that automatically creates CRUD routes for your models
Stars: ✭ 159 (-16.75%)
Mutual labels:  code-generation
Swiftcolorgen
A tool that generate code for Swift projects, designed to improve the maintainability of UIColors
Stars: ✭ 152 (-20.42%)
Mutual labels:  code-generation
Codelingo
CodeLingo // The Linter for Devs Who Hate Linters
Stars: ✭ 174 (-8.9%)
Mutual labels:  code-generation
Ts Morph
TypeScript Compiler API wrapper for static analysis and programmatic code changes.
Stars: ✭ 2,384 (+1148.17%)
Mutual labels:  code-generation
Remote Data Ts
RemoteData type
Stars: ✭ 185 (-3.14%)
Mutual labels:  algebraic-data-types
Cakelisp
Metaprogrammable, hot-reloadable, no-GC language for high perf programs (especially games), with seamless C/C++ interop
Stars: ✭ 149 (-21.99%)
Mutual labels:  code-generation
Jennifer
Jennifer is a code generator for Go
Stars: ✭ 2,257 (+1081.68%)
Mutual labels:  code-generation
Evolutility Ui Jquery
Model-driven Web UI for CRUD using REST or localStorage.
Stars: ✭ 164 (-14.14%)
Mutual labels:  code-generation
Testura.code
Testura.Code is a wrapper around the Roslyn API and used for generation, saving and compiling C# code. It provides methods and helpers to generate classes, methods, statements and expressions.
Stars: ✭ 189 (-1.05%)
Mutual labels:  code-generation
Xsd2php
Convert XSD into PHP classes and JMS serializer definitions
Stars: ✭ 190 (-0.52%)
Mutual labels:  code-generation

🤝 swift-enum-properties

Swift 5.1 CI @pointfreeco

Struct and enum data access in harmony.

Motivation

In Swift, struct data access is far more ergonomic than enum data access by default.

A struct field can be accessed in less than a single line using expressive dot-syntax:

user.name

An enum's associated value requires as many as seven lines to bring it into the current scope:

let optionalValue: String?
if case let .success(value) = result {
  optionalValue = value
} else {
  optionalValue = nil
}
optionalValue

That's a lot of boilerplate getting in the way of what we care about: getting at the value of a success.

This difference is also noticeable when working with higher-order functions like map and compactMap.

An array of struct values can be transformed succinctly in a single expression:

users.map { $0.name }

But transforming an array of enum values requires a version of the following incantation:

results.compactMap { result -> String? in
  guard case let .success(value) = result else { return nil }
  return value
}

The imperative nature of unwrapping an associated value spills over multiple lines, which requires us to give Swift an explicit return type, name our closure argument, and provide two explicit returns.

Solution

We can recover all of the ergonomics of struct data access for enums by defining "enum properties": computed properties that optionally return a value when the case matches:

extension Result {
  var success: Success? {
    guard case let .success(value) = self else { return nil }
    return value
  }
  
  var failure: Failure? {
    guard case let .failure(value) = self else { return nil }
    return value
  }
}

This is work we are used to doing in an ad hoc way throughout our code bases, but we can centralize it in a computed property and are free to access underlying data in a succinct fashion:

// Optionally-chain into a successful result.
result.success?.count

// Collect a bunch of successful values.
results.compactMap { $0.success }

By defining a computed property, we bridge another gap: our enums now have key paths!

\Result<String, Error>.success
// KeyPath<Result<String, Error>, String?>

Despite these benefits, defining enum properties from scratch is a tall ask. Instead, enter generate-enum-properties.

Usage

usage: generate-enum-properties [--help|-h] [--dry-run|-n] [<file>...]

    -h, --help
        Print this message.

    -n, --dry-run
        Don't update files in place. Print to stdout instead.

    --version
        Print the version.

Once installed, you can invoke generate-enum-properties from the command line and feed it any number of Swift source files:

# Insert enum properties into every enum declaration. 
$ generate-enum-properties **/*.swift

It will automatically generate and inline enum properties for every enum with associated values. Please note that it updates source files in place. Use version control to avoid accidental insertions! You can use the --dry-run flag to preview the updated source.

$ generate-enum-properties --dry-run **/*.swift

Without the --dry-run flag, the following source file as input:

enum Validated<Valid, Invalid> {
  case valid(Valid)
  case invalid(Invalid)
}

Will have its contents replaced with the following output:

enum Validated<Valid, Invalid> {
  case valid(Valid)
  case invalid(Invalid)

  var valid: Valid? {
    get {
      guard case let .valid(value) = self else { return nil }
      return value
    }
    set {
      guard case .valid = self, let newValue = newValue else { return }
      self = .valid(newValue)
    }
  }
}

Note that both a setter and getter are generated, which means you can also optionally dive into enum data and update a part of it.

validatedUser.valid?.name = "Blob"

Running generate-enum-properties is idempotent: it will only insert properties that aren't already defined in the enum declaration. One caveat:

⚠️ If you have defined an enum property of the same name in an extension, it will collide with the one generated by generate-enum-properties.

Now you may be wondering: why not generate extensions that can be hidden away in another file? Unfortunately, this is problematic for enums that depend on types that need to be imported and types that are nested. By inlining enum properties, we can ensure that every associated value's type is in scope.

Xcode Code Snippets

If you or your team are not yet ready to use code generation in your code base, don't let that stop you from using enum properties! They are too useful to give up. Instead you can use our Xcode code snippet with a little bit of manual work to allow easy creation of enum properties in your code base:

Sep-25-2019 09-45-56

To install just add all of the code snippets in the .xcode directory to the following directory:

~/Library/Developer/Xcode/UserData/CodeSnippets/

and restart Xcode.

Or run the following command from the root of the repository:

$ make snippets

For more information about Xcode code snippets check out this informative NSHipster article.

Installation

Homebrew

You can install generate-enum-properties using our custom tap:

$ brew install pointfreeco/swift/generate-enum-properties
$ generate-enum-properties

SwiftPM

As a dependency

If you want to use generate-enum-properties in a project that uses SwiftPM, it's as simple as adding a dependencies clause to your Package.swift:

dependencies: [
  .package(url: "https://github.com/pointfreeco/swift-enum-properties.git", from: "0.1.0")
]

And invoking swift run from the command line:

$ swift run generate-enum-properties

As a CLI

If you want to run generate-enum-properties using SwiftPM, it's as simple as cloning the repository and invoking swift run:

$ git clone https://github.com/pointfreeco/swift-enum-properties.git
$ cd swift-enum-properties
$ swift run generate-enum-properties

Make

If you want to build and install generate-enum-properties yourself:

$ git clone https://github.com/pointfreeco/swift-enum-properties.git
$ cd swift-enum-properties
$ make install

Mint

If you want to install with Mint:

$ mint install pointfreeco/swift-enum-properties

Interested in learning more?

These concepts (and more) are explored thoroughly in Point-Free, a video series exploring functional programming and Swift hosted by Brandon Williams and Stephen Celis.

The design of this library was explored in the following Point-Free episodes:

video poster image

License

All modules are released under the MIT license. See LICENSE for details.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].