Want to offer native, in-app customer service to your users? Get going quickly with Zendesk’s Mobile SDK. Save $177 with coupon code LBOC.

Building great forms for users to enter information on iOS is tough. There's a lot of small details which are easy to get wrong. Today we'll begin looking at ways to improve this process.

First up, is navigation. 📍

Allowing for quick and simple navigation from one form field to the next can dramatically reduce friction for our users.

We'll use a great new library from Thanh Pham called UITextField-Navigation to pull this off. Let's get started.

UITextField-Navigation is built around the concept of telling each field which field should be "next" in the form.

Once we've done that for all of our fields, the library takes over and displays a navigation bar above the keyboard automatically:

We can set up these relationships super easily in in Interface Builder:

...or in code:

let nameTextField = UITextField()
let bioTextView = UITextView()

nameTextField.nextNavigationField = bioTextView

Now, whenever a user begins editing a UITextField or UITextView in our app, a navigation toolbar will automatically be shown above the keyboard:

We can use UIAppearance to completely customize the look and feel of the toolbar:

NavigationFieldToolbar.appearance().barStyle = .black
NavigationFieldToolbar.appearance().backgroundColor = UIColor.purple
NavigationFieldToolbarButtonItem.appearance().tintColor = UIColor.white

Last but not least, we aren't limited to a "standard" set of buttons/items in the toolbar.

We'll use the navigationFieldToolBar optional property that the library adds to UITextField and UITextView to first customize some individual properties directly:

guard let toolbar = nameTextField.navigationFieldToolbar else { return }

toolbar.barStyle = .default
toolbar.backgroundColor = UIColor.red
toolbar.previousButton.title = "Backward!"
toolbar.nextButton.title = "Onward!"
toolbar.doneButton.title = "Dismiss"

...then create our own array of toolbar items:

let expandButton = UIBarButtonItem(
  title: "Expand",
  style: .plain,
  target: self,
  action: #selector(expandForm)
)

let flexible = UIBarButtonItem(
  barButtonSystemItem: .flexibleSpace, 
  target: nil,
  action: nil
)

toolbar.items = [
  toolbar.previousButton,
  toolbar.nextButton,
  flexible,
  expandButton,
  flexible,
  toolbar.doneButton
]

Neat!

Learn more about UITextField-Navigation at git.io/textfieldnav.

Topics

#298: Changing Light Colors with HomeKit 💡🌈

Topics

In Bite #297, we learned about about how to simulate a light bulb accessory using Apple's HomeKit Accessory Simulator. Today we'll continue where we left off, and learn how to change the color of our light bulb.

Let's dive in.

Everything in HomeKit is built around the HMHomeManager type. We'll use this manager to interact with homes, accessories, and more:

let manager = HMHomeManager()
manager.delegate = self

We've set ourselves as the delegate here so we can implement one function:

extension ViewController : HMHomeManagerDelegate {
  func homeManagerDidUpdateHomes(_ manager: HMHomeManager) {
    // TODO
  }
}

Like all user data, we'll need to ask for permission before we can access the user's HomeKit data. Once the user has allowed us, the homeManagerDidUpdateHomes function will be called. This function will also be called anytime signficant changes happen in a home.

We can use this as a cue to update our app's UI to match. Neat.

Ok, let's have some fun and see what it would look like (in code) to change all of the lights in our house to a different color.

First let's get all the lights in the home:

func homeManagerDidUpdateHomes(_ manager: HMHomeManager) {
  guard let home = manager.homes.first else { return }

  let lights = home.accessories.filter { $0.category.categoryType == HMAccessoryCategoryTypeLightbulb }
}

Nice. We now have an array of all lights in our home as HMAccessory objects. To make changes, we'll need to get to the services and characteristics inside of each accessory.

Let's flatMap our way to success and get an array of just the "Hue" characteristics.

Note: We're just going to try this out by tinting our light blue. In a complete app, we'd need to make sure to update all 3 Hue, Brightness, and Saturation characteristics of the light.

let hueCharacteristics = lights
  .flatMap { $0.services }
  .flatMap { $0.characteristics }
  .filter { $0.characteristicType == HMCharacteristicTypeHue }

Nice, now we need a way to grab a hue value from a UIColor:

func hsba(from color: UIColor) -> [CGFloat] {
  var HSBA = [CGFloat](repeating: 0.0, count: 4)
  color.getHue(&HSBA[0], saturation: &HSBA[1], brightness: &HSBA[2], alpha: &HSBA[3])
  return HSBA
}

Not the prettiest code, but it'll do. Now let's use it:

let color = hsba(from: UIColor.blue)
let newHue = Float(color[0])

for hc in hueCharacteristics {
  let max = hc.metadata?.maximumValue?.floatValue ?? 1.0

  hc.writeValue(NSNumber(value: newHue * max), completionHandler: { if let error = $0 { print("Failed: \(error)") } })
}

Here we grab our newHue value for our UIColor.blue color, then iterate through all the hueCharacteristics from before, ensuring we multiply our newHue value by each characteristic's maximumValue property. This makes sure we're speaking the language of the accessory, and setting a value in a scale it understands.

Finally, we wrap up our value in an NSNumber and write it to the HMCharacteristic.

When we build and run, we're prompted for permission. After allowing, we can flip back over to the HomeKit Accessory Simulator app and see the hue of our light has changed. Success!

Try adding more simulated lights, setting them to random colors, then running the app again to see a greater effect.

That's all for now. We've got plenty more to explore in HomeKit, look out for more Bites soon.

Weekly Sponsor: PSPDFKit 📑

PDF files have been around a long time. Over that time they evolved to become an incredibly versatile and functional format. They're also wide-ranging in terms of how they're used. Comic books, Apartment leases, flight manuals, etc. All powered by PDFs.

Being able to take advantage of all this great functionality in our apps would be huge for our users.

Today, we're welcoming a brand new sponsor to LBOC, it's PSPDFKit! PSPDFKit delivers an intuitive & seamless SDK for integrating PDF functionality into apps on iOS, Android, and the Web.

With it, we can beautifully display PDFs, and allow our users to intuitively annotate documents, and much, much more.

Let's get started.

We'll head over to PSPDFKit's wonderful documentation area and follow the Getting Started steps to integrate the Framework directly, or through a system like CocoaPods.

Once we've got PSPDFKit in our app, we can start playing around.

PSPDFKit provides tons of great APIs for programmtically interacting with PDF files, as well as a bunch of polished, (and incredibly customizable) user interfaces to allow our users to read and manipulate PDF files themselves.

Let's try opening a PDF, and displaying it:

let document = PSPDFDocument(url: documentURL)

let controller = PSPDFViewController(document: document, configuration: PSPDFConfiguration { builder in
    builder.thumbnailBarMode = .scrollable
    builder.scrollDirection = .horizontal
})

We create a new PSPDFDocument, then a new view controller to show it. We use PSPDFKit's awesome PSPDFConfiguration type to neatly build up our settings.

From here we can simply present it like any other view controller. Neat!

PSPDFKit stands out with an awesome, well thought-out API. It's clear that they care deeply about API design, and developer experience.

Before we go, let's try out another couple features. First up, Annotations:

// Create a new PSPDFDocument
let document = PSPDFDocument(url: documentURL)

// Create a link annotation, and set its URL
let linkAnnotation = PSPDFLinkAnnotation(url: URL(string: "https://pspdfkit.com")!)

// Position the annotation in the document
let boundingBox = CGRect(x: 200, y: 400, width: 50, height: 300)
linkAnnotation.boundingBox = boundingBox

// Customize the link annotation's appearance
PSPDFLinkAnnotationView.appearance().borderColor = .blue

// Add the newly created annotation to the document
document.add([linkAnnotation])

Very cool, Next up, Forms. Our users can of course fill out any form fields in documents they open, but we can actually fill them out programmtically too:

let document = PSPDFDocument(url: documentURL)

guard let annotations = document.annotationsForPage(at: 0, type: .widget) else { return }

for annotation in annotations {
    switch annotation {
    case let textFieldFormElement as PSPDFTextFieldFormElement:
        guard let fieldName = textFieldFormElement.fieldName else { return }
        textFieldFormElement.contents = String(format: "Test %@", arguments: [fieldName])

    case let buttonFormElement as PSPDFButtonFormElement:
        buttonFormElement.toggleButtonSelectionStateAndSendNotification(true)
    default:
        break
    }
}

Very cool. We iterated through the form fields and filled them with some example content, and toggled the selected state for any buttons in the form.

We have truly only scratched the surface of what's possible with PSPDFKit here.

PSPDFKit offers a ton of other great features like indexed search, digital signatures, and even document editing! They also provide wonderfully fast and responsive support.

Be sure to check out PSPDFKit's extensive Guides area to find complete documentation and tons of extensive guides.

Thanks to PSPDFKit for their support of LBOC!

Topics

#297: Simulating HomeKit Accessories 💡🤖

Topics

"Home automation" is perhaps one of the hottest topics in technology these days.

While still an emerging market, many iOS device owners now also own at least one or two "smart home" devices.

Today we'll begin looking at HomeKit, Apple's framework for communicating with and controllling these devices from inside our apps.

Before can dive in though, there's a bit of tooling we need to learn about first. Specifically, we need to learn how to simulate HomeKit devices.

For example, we might not own any HomeKit devices ourselves. Even if we do though, we'd rather not need to phsyically change things about our home to test our app.

Not to worry, Apple provides a great solution to this challenge in the form of a HomeKit Accessory Simulator app for macOS.

In it, we can setup and configure a "simulated" set of devices in any kind of Home setup we'd like.

Sadly though, it doesn't ship with Xcode.

We'll need to head over to Apple's "More Developer Downloads" page here and search for "Hardware IO Tools for Xcode". We'll download the latest release, then install the HomeKit Accessory Simulator app.

Now, let's open it up and simulate our first accessory.

We'll click the + button in the bottom left and select New Accessory...

We'll fill out the fields with some example information. The values aren't super important, they merely need to be unique and somewhat realistic.

Neat. We've now got a (pretend) lamp. 💡

Well, sort of. There's actually one more important step, and it's speaks to the heart of how HomeKit works.

So far, HomeKit doesn't know anything about our new lamp. "Lamp" is just the name we gave it.

For HomeKit to do something useful with our device, we'll need to add a HomeKit Service to it. HomeKit Services describe the capabilities and functionality of a device

We'll click the Add Service... button on our new device, and choose Lightbulb from the dropdown menu.

We can leave the rest of the fields alone.

Neat! Not only do we now have a fully-simluted, color changing light bulb, we're also provided with some nice sliders and controls to read from, and write to, the current state of the device.

That's all for today. We'll learn more about the HomeKit Accessory Simulator as we continue to explore HomeKit. Next time we'll learn how to change this light's color in code! 🌈💡

Topics

#296: Today Extension B-Sides 📼

Topics

We first covered Today Extensions way back in Bite #36. They're a great way to offer quick, glanceable information or entry-points to our app. Today we'll take a look at some lesser known features of Today Extensions, and how we can use them in our code. Let's begin.

3D Touch Shortcut Widgets

First up, is one of the newest additions to the Today Extension world: 3D Touch Homescreen Widgets.

The coolest bit is, we don't really need to do anything to "get" this. If our app has a Today Extension, the widget will automatically appear when a user 3D Touches our app's icon.

The B-Side comes into play when we have multiple Today Extensions in our app. We'll need a way to tell the system which one to display above our icon. We can do this by adding a new key to our app's Info.plist:

UIApplicationShortcutWidget

(We can also choose "Home Screen Widget" from the keys dropdown).

Neat!

Conditonal Display

Today Extensions Widgets don't have to always be visible in the Today view. We can actually tell the system whether or not our widget should be displayed with this one function:

NCWidgetController
  .widgetController()
  .setHasContent(true, 
    forWidgetWithBundleIdentifier: "com.littlebitesofcocoa.latest")

Calling this with false will hide the widget in the Today View until our app calls the function again with a true value.

Opening Our App

This one is a bit of a stretch to truly call a B-side, but it can easily be done incorrectly, so here we are.

It's quite common for a Today Extension Widget to need to open its containing app.

Apple has tightened the reigns in recent OS releases to "validate" when and how apps (and specifically Today Extensions) can open apps. Not to worry, we can use this special function on an NSExtensionContext to open a deep link into our app.

self.extensionContext?.openURL(NSURL("lboc://bites/296"), completionHandler: nil)

Pro Tip: Opening our own app (i.e. our app that contains the Today Extension) is just fine, but beware if we start trying to open other apps using this function, Apple may scrutinize our Today Extension further during App Review.

In Bite #294 we learned about annotations in Sourcery. They're a great way to make extra metadata available on our types when accessing them in Sourcery's .stencil templates.

Today we'll take a look at one of the most powerful features of annotations in Sourcery, Key/Value Annotations.

We'll explore these by building a fictional API client powered by a simple Swift Enum. Let's begin!

First, we'll need to create our enum. Then, we'll define some cases to represent each HTTP endpoint we want to be able to call.

For us, these are a classic set of "get all", "get one", "create", "change", and "delete":

enum SpaceshipsAPI {
    case ships
    case ship(shipID: Int)
    case createShip(shipID: Int, ship: Ship)
    case updateShip(shipID: Int, ship: Ship)
    case deleteShip(shipID: Int)
}

Long-time readers may recognize this enum technique from Bites such as #93 or #150.

Here's where the magic begins though. 🎩

Sourcery Key/Value Annotations are similar to regular Annotations. Instead of a simple tag though, they allow us to annotate types, enums, enum cases, etc with a set of names and a values.

Key/Value Annotations work everywhere that regular Annotations do. We can annotate types, enums, enum cases, and more.

They look like this:

/// sourcery: key = value
/// sourcery: anotherKey = someOtherValue

They can also be listed in a single line:

/// sourcery: key = value, anotherKey = someOtherValue

Values can contain some basic types:

/// sourcery: maxSpeed = 1500, hasHyperdrive = true
/// sourcery: codename = "blacksaber"

Ok, back to our API client. Now that we know we can add simple metadata like this above things in our code, let's use this technique to describe each API endpoint.

enum SpaceshipsAPI {
    /// sourcery: method = "GET", path = "/spaceships"
    /// sourcery: timeout = 5
    case ships
    ...
}

Nice! Normally, we might put things like method and path into essentially a big switch statement switching on self, and returning the correct method or path.

With Sourcery Key/Value Annotations though, we can let Sourcery generate all of these statements for us.

Let's create a SpaceshipsAPI+Properties.stencil template that extends our enum, generating the computed properties:

extension SpaceshipsAPI {
  public var path: String? {
    switch self {
      {% for c in type.SpaceshipsAPI.cases %}
      {% if c.annotations.path %}
      case .{{ c.name }}: return "{{c.annotations.path}}"
      {% endif %}
    {% endfor %}
    default: return nil
    }
  }

  // ... etc
}

Success! Now we can work at a much higher level. We can update our annotations and never need to manually edit tedious switch statements.

We'll run sourcery, which creates/updates SpaceshipsAPI+Properties.generated.swift, filling it with all those beautiful switches.

Finally, we can use our new properties like this:

  print(SpaceshipsAPI.ships.path)

The use cases can begin to boggle the mind a bit here.

We can now add rich metadata to just about anything in our Swift code. Then use it to metaprogram new Swift code that we can use in our project.

Neat!

Weekly Sponsor: Zendesk 💡

We're welcoming back one of our favorite sponsors this week, it's Zendesk!

None of us have ever built a flawless app.

Chances are, no matter how good we think our app's user experience is, there's probably something users will need help with while they're in our app.

Yet in many apps, getting help is reduced to a "contact us" button that launches an compose email screen, or a just drops the user on a web page.

By forcing folks out of the app to get help, small problems can turn into big annoyances, and those will almost certainly turn into negative App Store reviews and poor ratings.

With Zendesk's Mobile SDKs, we can join the makers of Angry Birds, Venmo and Swiftkey in bringing native, in-app support functionality to our apps quickly and easily.

Our users can view help and support content, and even submit tickets to support without ever leaving our app. Neat!

Tickets go into Zendesk and can include technical details about the user and their device, history with our apps, and more.

Best of all, it's included with Zendesk at no extra charge.

We can use Zendesk's "out-of-the-box" iOS UI components to get up and running quickly, or we can build your own UI with SDK API Providers.

A huge thanks to Zendesk for sponsoring!

Topics

#294: Annotations with Sourcery 🔮

Topics

We first covered Sourcery back in Bite #292. It's a command-line tool that helps us generate Swift code from .stencil template files.

Today we'll check a specific feature of Sourcery, annotations. Let's dive in.

Nefore we begin, let's do a quick "refresher". The TLDR for Sourcery is Render template files into real Swift code. Full access to the type system using SourceKitten. Useful for simple things (automate tasks like adding counts to enums), or crazy complex things (automatically generate Swift test code). Essentially, metaprogramming. Whew. Ok, moving on.

Annotations are simply small bits of metadata that annotate our properties functions, enums, enum cases and other types.

Then, later we can access this metadata inside our .stencil template files.

Let's try it out.

Before we begin, let's add a new Swift Enum to our project. Nothing special here so far, just regular Swift.

enum SpaceshipKind {
    case cruiser
    case frieghter
    case destroyer
}

Next, we'll add a new SpaceshipKind+Count.stencil file for Sourcery to render. We'll use the "Empty" option in Xcode when creating a new file, then open the Inspector (right side) and change the file's type to Swift Source so it will read (slightly) nicer in Xcode.

Finally, let's run sourcery:

sourcery source/ templates/ /source/_generated/ --watch --verbose

Passing in the --watch tells Sourcery to run continuously and re-render our templates anytime our code or templates change. Neat.

The --verbose flag tells Sourcery to print a bunch of useful debug info to the console while it's working. Helpful at first, but once we're more comfortable with Sourcery, we can probably leave this off.

Whew! Now we can finally start using Annotations.

Let's add an annotation to our enum:

enum SpaceshipKind {
    case cruiser
    case frieghter
    case destroyer
}

Then in our template, let's generate a count property for all our enums:

{% for enum in types.enums %}
extension {{ enum.name }} {
  static var count: Int { return {{ enum.cases.count }} }
}
{% endfor %}

This works great:

extension SpaceshipKind {
  static var count: Int { return 3 }
}

But what if we were farther along in our project? What if we had many more Enums in our code base, and wanted to exempt a few of them from generating a count property.

We'll add another new enum:

enum EngineKind {
    case hyperdrive
    case sunsail
}

Since we added some code, Sourcery's --watch kicks in and re-generates our template:

extension SpaceshipKind {
  static var count: Int { return 3 }
}
extension EngineKind {
  static var count: Int { return 2 }
}

Let's pretend we don't need that second enum to get a count property.

There's a few ways we could approach this in Sourcery, but this gives us a nice way to learn about and understand how annotations work.

We'll add one above EngineKind in our code:

/// sourcery: skipCount
enum EngineKind {
    case hyperdrive
    case sunsail
}

Annotations are simply special comments that Sourcery parses, and makes available in our .stencil templates.

The cool part is it's location aware, so we can put annotations above types, and they'll be available on those types in our .stencil templates! Same goes for annotations on properties, enum cases and more!

Now, we can access this annotation in our template:

{% for enum in types.enums %}
extension {{ enum.name }} {
  {% ifnot enum.annotations.skipCount %}
  static var count: Int { return {{ enum.cases.count }} }
  {% endif %}
}
{% endfor %}

Finally, our generated code is back to just the one enum:

extension SpaceshipKind {
  static var count: Int { return 3 }
}

Success!

We can also apply annotations to multiple things at once using :begin and :end tags:

/// sourcery:begin: skipCustomSetter
var captainID: Int
var maxSpeedInParsecs: Int
/// sourcery:end

This is just the beginning. Come back next time to learn about key/value pair annotations.

Learn more about Sourcery at git.io/sourcery (also in Bite #292)

AttributedStrings are a wonderful way for us to describe and add rich, styled text to our apps.

We've covered a few different approaches and solutions for composing AttributedStrings over the years.

Today we'll check out at a new library, Attributed by Nicholas Maccharoli, which stands out as a modern approach to the task. Let's take a look.

We'll start by looking at the "standard" way to use AttributedStrings in Foundation:

let font = UIFont(name: "AvenirNext", size: 18.0)!

NSAttributedString(string: "Han Solo", attributes: [
  NSForegroundColorAttributeName: UIColor.black,
  NSFontAttributeName: font
])

Not too bad, but we can do better. Let's try this same thing but with Attributed:

"Han Solo".attributed(with: Attributes { $0.foreground(color: .black).font(font) })

Neat!

"Why not just use the AttributedString API that ships with Foundation?"

This is a fair question.

Imagine building an app that had many different attributed string styles. With Attributed, we're given a strongly API, allowing us to omit a couple of types. We're also able to omit the long, verbose key names.

Finally, (and perhaps most importantly) we're using a sort of "closure composition" technique.

This involves accepting a Swift closure in an initializer, giving us the anonymous argument $0 to play with. Then, we can chain function calls on to $0 to add attributes.

All of this leads to a dramatic decrease in manual typing (even with autocomplete in Xcode, writing tons of attribute collections stops being fun, quickly).

It also allows us to work more effeciently, and accurately. We can lean on our syntactic shorthand and trust in the Swift's type system to get us to the finish line.

Inheritance

Before we go, let's talk about one of the most common hiccups we can run into when writing AttributedString-related code. Inheritance.

We've all been there. We're composing an AttributedString to go into a UILabel. We're pumped because we've got the style neatly translated from the original design into code. Then we see it. The design calls for one of the words in the label to be a different color, for emphasis.

Dun, dun dun.

No worries though, with Attributed we can solve this problem quickly:

let base = Attributes().font(UIFont(name: "AvenirNext", size: 18.0)!)

let highlighted = base.foreground(color: .red)

"Han Solo is the captain of the ".attributed(with: base)
    + "Millennium Falcon".attributed(with: highlighted)

Very cool. We're able to define a base set of attributes in Attributes(), then compose new sets that inherit all the attributes of our base set.

Then, we're leaning on Attributed's extension to String and String's support of the + operator to write some super clean code.

Pro Tip: This functionality also allows us to easily build up a re-usable set of Attributes(), keeping them in one spot, then sprinkling them throughout our code.

Learn more about Attributed at git.io/attributed.

Topics

#292: Metaprogramming with Sourcery 🔮

Topics

We cover plenty of libraries and developer tools here on LBOC. Many are useful not just on their surface, but also in terms of how they allow us to learn from our fellow developers.

Through this process, we collectively explore new approaches and techniques. New ideas emerge all the time.

Every now and then, one of these ideas stands out.

Sometimes, an idea makes too much sense, or is simply too useful to ignore.

In modern times, things like Fastlane, CocoaPods, and Carthage come to mind. Slightly more seasoned folks may remember the emergence of Pull to Refresh, or BWToolkit.

Sourcery from Krzysztof Zabłocki is the latest addition to this set.

It brings the concept of "meta-programming" to Swift, and it is definitely too useful to ignore.

Let's peer into our crystal ball, and see what it can do.

At its core, Sourcery generates Swift code from template files.

It elegantly brings together two other great developer tools: SourceKitten (for introspecting our code), and Stencil (for templates).

It aims to solve a few problems:

  • Reduce time spent writing so-called "boilerplate", or repetitive/obvious code.
  • Provide a way to reason about the types in our code, and their properties.
  • Reduce simple human errors, caused by things like typos.

Ok, enough introduction. Here's how Sourcery works:

  • First, we'll write some code that looks almost like regular Swift code into "template" (.stencil) files.
  • Then, we'll run the sourcery command-line tool. This will "render" our .stencil files into .swift files that we'll add to our project.
  • Finally, when we build our app, our "generated" .swift files will get compiled just like any other .swift files we might add.

Immediately some ideas of how to use this begin coming to mind. Here's a few specific tasks that might cause us to reach for Sourcery:

  • Conforming our types to NSCoding.
  • Conforming to Equatable or Hashable
  • Writing JSON de-serialization code

Maintaining each of these implementations is a never-ending task. Anytime we add, remove, or change a property we'll need to potentially revist each of these bits of code as well. Bummer.

Ok. Let's try this thing.

First we'll need to get the sourcery command-line tool. The simplest way to do this is to download the latest release binary here.

Let's cd into the root directory of the download and copy the tool over to somewhere permanent:

cp bin/sourcery /usr/local/bin

(Note: /usr/local/bin is a common place to put command line tools on macOS thanks largely to the fact that Homebrew puts things there, so it's likely already in our $PATH).

Neat. Now we can use it anywhere.

We could also have simply copied the tool into our project, and added it to source control. Any approach is fine, we just need to be able to run it in the root directory of our project somehow.

Now, let's head into that root directory of our project, and create a couple directories:

mkdir templates
mkdir generated

We're almost ready to try things out. First though, we'll need a template to generate from.

Let's add a new file in the templates directory called Enum+Count.stencil. Then, we'll write our first template code:

{% for enum in types.enums %}
extension {{ enum.name }} {
  static var count: Int { return {{ enum.cases.count }} }
}
{% endfor %}

The {{'s, }}'s, {%'s, and %}'s are Stencil template tags.

Stencil deserves a full Bite of it's own, but for now we just need to know that statements within these tags get evaluated by the sourcery command line tool, and iterated or replaced when generating Swift code.

The rest of the content is regular Swift code.

Anyone who has worked on a web app in recent years should feel right at home with this technique. Instead of generating HTML though, we're generating Swift code, neat!

Let's break down what's happening in our template:

First, we want to iterate through all the enums in our project's code:

{% for enum in types.enums %}

{% endfor %}

Then, for each enum we find, we want to extend it to have a new static property called count.

extension {{ enum.name }} {
  static var count: Int { return {{ enum.cases.count }} }
}

This property will return the number of cases in the enum. (Providing us a piece of functionality currently missing from Swift itself).

Finally, we can run sourcery.

./sourcery . templates generated --watch

We've passed in the --watch flag to make sourcery watch our template files, and re-generate them anytime it sees a change. Neat.

This will scan our source code for a bit, then produce a new file in the generated directory called Enum+Count.generated.swift.

It will look like this:

extension SpaceshipKind {
  static var count: Int { return 37 }
}

extension CrewRank {
  static var count: Int { return 10 }
}

extension HTTP.Method {
  static var count: Int { return 7 }
}

How cool is that?

Now, we just need to add this generated file to our Xcode project like we would any other file. Its contents will be replaced anytime sourcery runs.

Pro Tip: We can also optionally add a new "Run Script..." Build Phase to our Xcode project to run the sourcery command (without --watch of course) at the beginning of each build of our app. Very cool.

The Sourcery Github repo offers a some very useful example templates for adding things like Equatable and Hashable. These examples are a great way to learn more about what's possible.

We've of course only barely scratched the surface of what's possible with Sourcery. Look out for future Bites where we'll explore much more...

Learn more and find full documentation of Sourcery at git.io/sourcery

Page 1 of 35