Going to store stuff here.

This is inspired by Nikita’s knowledge repository.

The basic idea is, as I come across information - or otherwise generate my own - I write it down in here1. This, combined with tooling I’m writing, provides a single, searchable database for things I know. So that I don’t have to keep searching how I did something previously. Additionally, this serves to help reinforce things I’ve learned, both by forcing myself to write down things I learn, as well as write it in a way that should help my future self not have to spend so long figuring out how I did a thing.


Obviously, I’m going to exercise some discretion and not put things like my tax returns or whatever.


I love everything related to space, both human exploration of space, and the observation of objects in space.

How to align and use an Equatorial Mount

... Todo.


Taking pictures of the sky!

See Cafuego’s page on software for osx.

I use the following software:

  • AstroDSLR1 for capturing from my DSLR.
  • Nebulosity for stacking images.

Finding a site

Find a local dark sky site. In LA, I like Joshua Tree National Park. However, being able to easily access far-away dark sky sites is one of my primary reasons for learning to fly.


You can get away at a bare minimum with just a camera and a tripod. My equipment checklist is:

  • Camera
  • 50 mm lens, because wide field shots are fun.
  • Telescope
    • Telescope camera mount
      • (barlow lens, T-ring, etc.)
    • Bahtinov mask.
  • Equatorial Mount
    • Motors for said mount
    • Batteries for the motors
  • Computer (Strictly speaking, this isn’t necessary - my camera can be set to take a series of photos at once)
    • USB-A to Mini-USB-A (to talk to camera).
  • RED flashlight - white will ruin your night sight. You also want low-lumen, for the same reason.
  • Water
  • Coffee
  • Snacks
  • Camping chair
  • Sleeping pad/bag (even if you plan to stay up all night, bring these).
  • Pillow
  • Paper and Pen.
  • A book or something else to do while the computer does all the work.

Be sure to set the computer to “night shift” mode2 before it’s dark, as red as possible.

Go there, set up camp. Preferably be set up before dark.

Actually Taking Photos

Regardless of how you use it, be sure to write down what you’re taking a photo of when you do it. Even if you know what the constellation/body you’re photographing is anyway.

Also, for stacking3 reasons, the more photos you take, the better it is, but it does have diminishing returns4.

Using a computer

Use AstroDSLR from computer to control the camera. Keep the camera in bulb mode to allow the software to control exposure time.

Make a different folder for each different set of photos you take.

Without a computer

Put the camera in manual mode, and have it set to average.


Nebulosity doesn’t read the color information from your raw files. Convert them to jpeg, because that’s still better than grayscale images.

for i in *.cr2; do sips -s format jpeg "$i" --out "${i%.*}.jpg"; done

From Nebulosity, open batch -> align and combine images. Select “Translation + Rotation + Scale”, click “OK”, and select the images to stack. Now, select the same star in each photograph as it prompts you. You’re going to go through the sets 3 times (so that it can correct for transation/rotation/scale). Now, do some manual editing, and save the end result.

Post to instagram5 or whatever. Use it as your new desktop background.


They don’t have an up to date ssl cert. Site is at


or use f.lux to remove as much blue from your screen as possible.


It’s essentially an inverse square relation - to get 5x better quality, you need to take 25x more images.


flume seems to be a decent OSX client for instagramp. The pro version is worth it.


I should leave reviews on goodreads, but I don’t.

Some definition on genre:

I vastly prefer to read sci-fi and/or fantasy. Of that, I really enjoy hard sci-fi, but that’s not a requirement.

Here’s a list of books and other readings I enjoy:


Sorted by Author

Andy Weir

  • The Martian is a hard sci-fi book about someone left behind on one of the first missions to Mars, and his struggles to get back home.
  • Artemis is a heist novel set in the first city on the moon. Like The Martian, it’s also hard sci-fi.

Scott Meyer

I really enjoy his Magic 2.0 series, though it does have a significant drop-off in quality. The first two books are amazing, the third is pretty good, but not as good as the previous two. But the reviews for the fourth one have kept me from continuing.

Fletcher DeLancey

My partner turned me on to her. Her Chronicles of Alsea series is pretty great, though at times it reads like the fan fiction it grew out of. They’re still highly worth reading.

Other Readings

  • HFY is a subreddit where people share stories sci-fi/fantasy stories where humans are the badasses. Usually by picking one particular trait of humans and overexagerrating it to give them an advantage over other species.

Other DIY Projects

DIY Projects other people have done that inspire me.

DIY Smartwatch

Imgur gallery describing the project, with a reddit post, which links to this Github repository.

DIY Ebook Reader

This person published a DIY ebook reader.


Personal Finance

A lot of my views on personal finance come from Mr. Money Mustache.


I don’t practice anything formal like YNAB. I do keep track of my finances using ledger with ledger-autosync to automate syncing that, and I occasionally review the status of where I spend money to reduce expenses.

Overall, my system for spending follows this order:

  1. Rent & other debts (car payment, internet, phone, etc.)
  2. Food & other necesseties (clothing, etc.)
    • I prefer to spend on groceries vs. eating out. While the notion that a $5/day coffee habit keeps you poor is ridiculous, you generally end up with better food once you learn how to make it yourself. It’s better to reserve eating out as a special thing.
  3. Everything else.

In general, anything that falls under “everything else” is something I spend at least a day thinking about before I decide whether to get it or not. The more expensive it is, the longer I spend thinking on it.

For especially large purchases, I actually do set up budgeting. This works out as a using ledger’s virtual postings feature to place money in an account prefixed with “Budget” every time I get paid. That is, it’s envelope budgeting for a single large purchase.

Buying Used vs. New

I’m really bad at this. I should prefer used, but I often go for new just because it’s easier and faster. This is a habit I’m working on correcting.


Any money you invest, treat it as if it no longer exists. Especially for 401k or other retirement accounts that have a penalty if you access them before some age.


Always contribute at least the minimum to get your company to max out their matching. For example, if your company does matching up to 4%, then at least put in that 4%.

  • For 2019, the 401k contribution limit is $19,000.
  • If your company offers both 401k and Roth 401k, then do a pre-tax contribution and invest the tax savings.
    • If you don’t think you’ll invest the tax savings, then contribute to the post-tax 401k.
    • On the other hand, if you’re currently in a high tax bracket (and have low expenses), then put the money in the pre-tax 401k.
      • Because you should have low expenses, therefore being in a low tax bracket.
  • Just set it, and check on it every year as the contribution limit changes, or your company changes their matching policy.

Index Funds


Federal Income Tax Brackets

CA Tax income brackets


It’s fun.

Electric Plane

Lessons learned and consolidation for the electric plane I’m designing.

Base Plane

I’m thinking of basing this off either a Sling 2 or a Sling 4. My plan for this doesn’t need not require 4 seats, so the only reason to go with the 4 is for the larger carrying capacity for only slightly more power required.

Battery System

When I started this project, I thought I might use salvaged Tesla batteries. As I did more research I realized that the Tesla battery packs are severely over engineered for my needs.1. I can build a battery system that’ll be not as good as a Tesla system, but it’ll be good enough, and much lighter than a Tesla system.

Pack Design

I’m still working through this.

Current thought is to use LG MJ1 cells, which, as of early 2019, have the highest energy density (just under 260 watt-hours per kilogram) of any battery cell available. This might change by the time I get around to being ready to manufacture the battery packs.

Current thought is to build a small (1 to 4) number of 108s battery packs, with as few parallel strings as I can get away with. This is doable with off-the-shelf BMSs, and reduces the engineering challenges.

Mounting the Batteries

Initially, I thought I’d mount the batteries where the gas tanks would go - there’s plenty of space, the wing spar will handle the load, etc. But, I realized that I need to be able to access the battery packs easily, and for that it’s much easier to place them firewall forward or otherwise in/around the fuselage. (Having to take apart the wings, or build in a folding hatch, was not appealing to me).

I need to CAD this up, but the current thought is to place most of the batteries in front of the firewall, with the rest behind the main seat, as weight and balance dictates.

One of the super nice things about an electric plane is that the “fuel” doesn’t slosh around, or otherwise change the weight and balance. Which makes weight and balance calculations much easier, as well as allowing me to better optimize weight distribution. Of course, this nicety is countered by the fact that I’m always running at the heaviest fuel load.


I’m still figuring this out, and once I have this figured out, I’m sure my battery system will change to suit this.

I’m aware of the existence of a standard for electric airplane charging, but I’m unaware of it’s contents.

So, instead, I’m thinking of integrating an automotive EV charger. These are designed for ~400V battery systems, so I should be able to get one to work with mine.

I still haven’t ruled out working with actual electrical engineers to design/build my own.

Ideally, though, I’d be able to integrate an aircraft charger.


Current thought is two Emrax 228 motors in a stack configuration.

The stack configuration is for redundancy and power reasons.

  • if one motor (or motor controller) dies, then the other can pick up the slack, with a lower max-power.
  • this reduces the strain on each motor, which should improve their longevity
  • For my desired voltage (400V), it’s much easier to find motor controllers that are rated for the lower power each motor will require.

Motor Controllers

Still researching this. Ideally, these’ll be air-cooled controllers that are rated for 100 A continuous at ~400V.

Solar Charger

While I’m not going to slap solar cells on the plane, I do want to build a folding solar array that can be stored in the plane.

sunelec2 is a place where you can buy PALLETS of solar panels for fairly cheap.

Things that won’t be on the MVP

Out of scope things that won’t be on the plane, at least, not initially.

Motorized Wheels

For making ground operations much more efficient, I’ve considered placing ebike motors in main gear of the airplane. The thought was to aid in taxiing (don’t use the propeller to move), takeoff (use motors + propeller to get up to speed), and landing (regenerative breaking). However, for reasons of simplicity, I’m not going to do that.

I still might build motors into the wheels, but not hook them up to anything, though.

Solar Wings

TL;DR: It’s not worth it. Yet.

For the planes I’m considering, I have about 130 square feet total wing area. With the most efficient solar cells available on the market, I expect to get approximately 25 watts per square foot, or about 3 kilowatts for the entire wing. For reasons, I expect to only be able to utilize at most 2/3rds of the total wing area. Reducing this down to 2 kilowatts at most (realistically, closer to only 1). This is not useful whatsoever for extending the duration of flight (It would add on the order of 10 minutes total duration), which means that it’s only useful for charging, either to supplement grid power (that’ll be a fun challenge), or when grid power is not available.

There’s other things I can do to increase the amount of solar, e.g. covering most of the fuselage & tail, but that’s not really worth doing.

Additionally, just adding solar cells on top of the wings will affect the aerodynamics, potentially in a way I don’t want it to.

Instead, I’ll build a folding array that I can set up next to the plane and use to charge it. This’ll have an inconvenience and weight penalty compared to directly mounting the cells, but I’ll have much more surface area available, and it won’t interfere with the aerodynamics of the plane.

Other Electric Plane Builds


This plane is going to be based in LA. The batteries won’t overheat from use (air-cooled), though they do need some cooling to protect them while the plane sits outside in summer. Heating won’t be required (see: the model 3 lacks a battery heater), but even if it does, then I can utilize the same environmental cooling system to heat as well as cool.


This link breaks so often...

Battery System

Really, the pack design is going to be led by the BMS. Charging is the main unsolved problem in this. Once I figure out a solution to that, everything else should fall into place.


I really don’t want to have to design and build my own.

Henry has the Battery Murdering System that he uses in the Quick-E. I could probably ask him about it.

OrionBMS is a commercial off-the-shelf BMS with support for J1772 charging, meant for EVs. This is quite intriguing to me. They have extension public documentation, not just on use, but also actually putting the thing together. Currently leaning toward this route, with 2 to 4 (for space reasons, actually) parallel strings of 108s(18-37)p packs. Though this may change if I can figure out a good place for a single 108s74p pack. I’d much rather not deal with the engineering challenges of multiple parallel strings, but if space demands it, then sure.

Designing my own

There exist chips for making this slightly easier, I could go down this route. For reasons of charging (... is also rather just use as much off-the-shelf components as possible), I don’t think I’ll go down this route.

The main issue with designing my own system is managing charging and how that works out - specifically making sure each gets properly charged. Especially with the 51V packs I was originally designing for - e.g. can I feel 400V to all 7 packs in series and it’ll all charge properly, even up to 95%+ charged? That seems too good to be true, given what else I know about lithium-ion being such a finicky technology. From what I’ve discovered on Endless-Sphere, the answer is basically “don’t do this, it’s silly.” Which means that if I did decide to create my own BMS, then I’d need to build something for a 400V system - close to 100 cells in series. Which is a significant undertaking. And I’d still need to integrate charging.


There exists a working group to design aircraft chargers - they want to come up with a single charger standard. Last I checked, they don’t have anything public released.

Instead, I’m thinking of either placing an onboard J1772 charger, or potentially even a tesla-compatible charger. While it would be awesome to have compatibility with the supercharger network, there are precisely 0 superchargers on airport ramps - making this useless to me (Am I going to land on a highway and then taxi to a supercharger? I’d be floored if there’s even 1 supercharger station where that could work).

Noise Abatement

It’s loud, here’s the noise abatement procedures for a few of the airports I’ve flown out of.

I’m not a CFI. Hell, as of this writing, I’m not even a private pilot. Don’t take this as flight instruction.

Hawthorne Municipal - khhr

Available here, we have:

  • Takeoff at Vy (best rate of climb). (Normal takeoff is Vx)
  • Upwind to at least the end of the runway.
  • Turn crosswind at 500 ft above field elevation OR by hawthorne mall, whichever comes first. (Normal crosswind turn is at 800 ft above field)
  • Fly downwind over El Segundo BLVD. This means that your downwind will be much closer to the field than it otherwise would be.

Note that this is voluntary - but you should still follow it because aviation is already hated by the general public.

Santa Monica - ksmo

Available here. This is:

  • Takeoff runway 21, fly over the golf course (turn 10 degrees left at end of runway, then right to fly over the golf course).
  • Don’t turn crosswind until after you fly over Lincoln.
  • Turn base at/around I-405/When ATC tells you.

Unlike Hawthorne, there’s an actual ordinance behind the Santa Monica noise abatements. Meaning that violating them is something you really don’t want to do.


Food is good.

I follow a mostly vegetarian diet, but I do enjoy meat on occasion.


I’m not a great cook, but I try my best.

Recipes from other people



Grilled Cheese



  • 1 oz unsalted butter
  • cheese (american singles, nom!)
  • Bread


Put cheese between two slices of bread. Melt butter on small-ish pan over medium-low (favor low) heat. Once melted, put sandwich on pan Heat for 3-4 minutes Flip Heat for another 3-4 minutes.


For extra deliciousness, enjoy the sandwich with chili.

Mac and Cheese

Really, this is cheesy pasta, because you don’t have to use macaroni.

This is infinitely better than box mac and cheese, and just as simple.

(By the way, for box mac and cheese, the best is Annie’s white cheddar shells).


Fairly simple

  • Cheese (whatever you have is fine - it needs to be shredded before it goes in, or even better: grated.)
  • Milk (any kind, even the vegan milks)
  • Pasta (shells, rotini, though I guess any kind of pasta should be fine, I like having smaller noodles, though)
  • Butter (4 oz or so)


  • Fill a medium-sized pot or saucepan with water, remembering to salt it enough to taste like seawater.
  • Get it to a boil, then add the pasta.
  • Boil the pasta for however long the packaging says.
    • While this is going, prepair the rest of the ingredients.
      • get the right amount of butter.
      • shred or grate the cheese.
      • get the milk out.
  • Drain the pasta, put the butter in the same pan and get it to melt.
  • Add the cheese and milk. Cheese first.
    • I eyeball the milk - pouring for about half a second or a second is usually enough. You’re aiming for about 2 oz.
  • Stir until everything comes together. There’s a decent chance there’s not enough heat left to melt the cheese entirely, that’s fine.
  • Add the pasta back in and stir.


Roasted Potatoes


  • Potatoes. Not baking kinds, you want “harder” potatoes. You want enough to fill a “serving” bowl.
  • Olive Oil, about 2 to 4 oz.
  • Salt
  • Pepper
  • Other seasoning, if you want


Wash the potatoes. Of course.

You’ll need o bowl for mixing the oil and chopped potatoes, and a cookie sheet. Cover the cookie sheet in aluminum foil, and spray it with non-stick spray.

  • Preheat oven to 450 F
  • Pour the oil, and seasoning into the serving bowl, and mix them.
  • Chop the potatoes into cubes. About a quarter to half an inch on each side or so is fine. You’ll figure it out as you make these.
  • After every 2 potatoes, put them in the serving bowl, and mix them enough so that each cube is coated in the oil. Then put them on a cookie sheet. Potatoes should only be in a single layer.

Put the potatoes in the oven for 20 minutes or so. I set 3 timers at 18 minutes, 20 minutes, and 22 minutes. Check on the potatoes as each timer goes off (use the oven light, you don’t have to open the oven up). They will finish cooking after you pull them out, so if they look “done”, then it’s too late. They should look like they’re starting to finish.


Simple Soup

Soups are super easy. You can make a soup simply by tossing a bunch of vegetables into a pot and let them boil for 20 minutes. This is a simple vegetable (or beef) soup I like to make.

The only real way to screw up soup is to let it sit/cook for too long. Mushy soup isn’t good. I’ve learned that when I do make soup, I need to commit to finishing it by the next night, or else it’s not something I’m going to enjoy finishing.

Ingredients (Vegetarian)

  • Beans (Pinto, Kidney, or Red) ~1lb
  • Assorted vegetables, here’s what I enjoy:
    • potatoes (use the smaller red/yellow potatoes, don’t use russets/baking potatoes. You want a “starchy” potato)
    • carrots
    • celery
    • bell peppers
    • radishes
    • onions
  • Vegetable broth (I use 2 16oz containers)
  • Noodles/Pasta. Only do 1 package, here’s what I’ve used/liked
    • Egg noodles are good
    • shells (the smaller the better)
    • rotini
  • Seasoning to taste. I typically do pepper and italian seasoning.

Ingredients (Non-Vegetarian)

This is the same as the vegetarian, with the following replacements.

  • Ground Beef (~1lb) instead of beans
  • Beef Broth instead of vegetable broth


You’re going to use medium heat for most of this, unless otherwise specified.

If you’re making the non-vegetarian variant:

  • Season the meat, roll into balls. Cook these in the bottom of the pot with no liquid until they’re entirely brown on the outside.
  • Pour in the first container of broth.

If you’re making the vegetarian variant:

  • Pour in the beans + broth at the same time.


In order of density of the vegetables (denser vegetables take longer to cook), cut and add them to the pot.

Add seasoning as desired.

Cook, covered, for about 10 to 15 minutes, or until the vegetables are close to being banned.

Add the noodles, more seasoning, and the other broth container.

Cook, covered, for another 10 minutes or so.



My mobile OS of choice.


Spreadsheet program for iOS and macOS - the iOS version


This apple support page shows a list of current shortcuts available in the iOS version of numbers.


Renaming a Sheet

This is surprisingly non-obvious. You double-tap it to select the text, then you can edit it from there. I thought there would be something involving a long-press, but that only allows you to move the sheet around. Similarly, a single-tap brings up an edit menu that allows you to cut (remove and place in pasteboard), copy (place in pasteboard), duplicate, or delete the sheet.


The only desktop OS worth using. (iOS being the only mobile OS worth using).


BitBar is a really neat menubar app for macOS that lets you write simple command-line programs as separate menu bar apps.



How This is Setup

This is setup using mdBook. It’s hosted as a repository on github. I set up a pipeline in concourse to build, check that things work, and then push new versions once things are set up.

Repository Layout

This is a simple mdbook, the actual content files is under src/. is missing, because I have tooling to automatically generate one automatically.


The pipeline1 is relatively simple:

  • Check for new pushes to master
  • Generate a for the book.
  • Build the book (using this mdbook docker image)
  • Test that the generated book isn’t broken (mostly verify the links work) using html-proofer, via this docker image.
  • rsync the updated book to the server hosting the contents.

Server Setup

The server hosting this is a linode VPS. It gets deployed to/managed via an ansible playbook. The current setup is pretty bad/full of bad patterns, but needless to say that playbook manages setting up nginx, getting letsencrypt set up, and configuring nginx to serve the static files for this repository.

On Sol, the repository containing this playbook is located at ~/workspace/Apps.

Offline/Development Setup

For making changes and doing a local preview (or just simply running locally), the following setup is recommended/required:

  • Rust/Cargo: Install rustup
  • mdbook-generate-summary: cargo install mdbook-generate-summary will get you an out-of-date version. The CI uses a dockerimage for this, but that docker image is not yet set up for local usage. The “best” way to get an up-to-date version is to download the source, and run cargo install --path .. Which isn’t the best way to distribute software. 🤷🏻‍♀️
  • mdbook: cargo install mdbook


mdbook-generate-summary will build a file for you. This way, you don’t have to maintain one.

mdbook watch will build your sources, watch for any changes to the src/ directory, and serve up the book on localhost:3000.

I do this for my work repository, which I want to keep separate from my personal stuff.


The pipeline definition looks like this:

- name: rsync-resource
  type: docker-image
    repository: mrsixw/concourse-rsync-resource
    tag: latest

  # Knowledge Wiki
  - name: knowledge_source
    type: git
      uri: https:/
      branch: master
  # Task info
  - name: tasks
    type: git
      branch: master
  # Book Server
  - name: book_server
    type: rsync-resource
      server: {{book_server}}
      base_dir: /usr/local/var/www/knowledge/
      user: you
      disable_version_path: true
      private_key: {{BOOK_SERVER_PRIVATE_KEY}}
  - name: build_knowledge
      - aggregate:
        - get: knowledge_source
          trigger: true
        - get: tasks
      - task: generate_summary
          platform: linux
            type: docker-image
              repository: younata/mdbook-generate-summary
              tag: latest
            path: sh
            - -c
            - |
              cd knowledge_source
              mdbook-generate-summary src/ -v
              cp -r * ../generated/
            dir: ""
          - name: knowledge_source
          - name: generated
      - task: mdbook
        file: tasks/tasks/mdbook.yml
          code: generated
          concourse: tasks
          book: book
      - task: test
        file: tasks/tasks/html_proofer.yml
          code: book
          concourse: tasks
        params: {DOMAIN: ""}
      - put: book_server
        params: {sync_dir: book}

Client-Side Tooling

Some tools I wrote to help make my usage of this repo easier.

Mostly, this is the Second Brain iOS application I wrote. This simple tool stores a copy of this locally, and uses the spotlight hooks available on iOS to allow searching through the contents of it. There also exists a (very rudimentary) macOS version of the app, but most of my development effort has been on the iOS version.

Software Engineering

I’m a software engineer by trade. Most of what I know is related to that.

Continuous Integration

I have a lot of thoughts on CI/CD.

My preferred CI system is concourse. Notes on that are here.


Inline task definition

You shouldn’t make a habit of doing this, but here’s a link to a script that’ll inline task definitions, for the rare case when you want a one-off task definition.

Concourse on Linode

Some notes on running Concourse from a linode box:

  • You can run the web command and the worker command on the same machine. The web machine can be on a 1GB ram linode, it doesn’t take that much resources.
  • While doable on the 1GB ram plan, you should really run the workers on at least the 2GB ram plans. This is more for storage than anything else.
  • Using a linode is a better plan long term over getting a NUC so long as you stay under the 16 GB plan. Depending on your usage, the other benefits (not having to care about hardware issues) might even extend this to that.

As with the other services I maintain, the setup is managed inside of an ansible playbook.


I discovered the hard way that using the 1GB “nanode” plan was not a good plan. The disk very quickly filled up, in addition to everything being slow as molasses. Once I migrated the machine to the 2GB plan, I ran into issues with the volume space not being resized (concourse creates a worker volume logical volume with $TOTAL_DISK_SPACE - 10GB of space), then further issues with the system thinking that a volumes which were deleted in fact weren’t, etc.


See this issue

Remove $CONCOURSE_WORK_DIR/garden-properties.json before each time a worker starts.

Resizing the Worker Volume

See this issue.

# On a machine with fly
fly -t $TARGET land-worker -w $WORKER_NAME

# On the worker
sudo systemctl stop concourse_worker

# Back to fly
fly -t $TARGET prune-worker -w $WORKER_NAME

# Back to the worker
sudo umount -f /opt/concourse/work_dir/volumes
sudo sync
sudo losetup -d /dev/loop0
sudo rm -rf /opt/concourse/work_dir/volumes.img
sudo reboot

Pruning the worker (which really only needs to happen before the reboot) tells concourse to ignore any volumes that may or may not exist. Invoking land-worker may or may not actually do things.

Darwin Worker

I wrote something on this a few years back. Which is, of course, out of date (at least, in regard to houdini).

Here’s my current launchagent (~/Library/LaunchAgents/com.rachelbrindle.concourse.worker.plist):

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "">
<plist version="1.0">

And the corresponding

#!/bin/sh -l

cd /Users/you/concourse
/usr/local/bin/concourse worker \
    --work-dir /Users/you/concourse/darwin_work_dir \
    --tsa-host $CONCOURSE_HOST:2222 \
    --tsa-public-key /Users/you/concourse/keys/web/ \
    --tsa-worker-private-key /Users/you/concourse/keys/worker/worker_key


Docker is cool.


FROM is needed at the top of the Dockerfile, this specifies the image you’re building on.

RUN will run a shell command at image-build-time. Use these sparingly, to reduce the amount of layers created.


Need to tag the image in your dockerhub username

e.g. docker build -t younata/my-image .

Need to login: docker login

And push: docker push younata/my-image:latest


Decentalized version control system.

Searching for when a given string was introduced

When you want to find out which commit first referenced a given string:

git log -S <string to search for> --source --all

See this stackoverflow answer.

Reverting commits without creating a new one

This is useful when you want to revert a set of commits, but also when you want to change them before committing again.

git revert -n <commit hashes to revert>

See the git documentation

iOS Development


UISplitViewController is a really neat class that can do both the neat “on ipad, show the main/detail paradigm”, and showing the regular “navigation controller with main, then go to detail if you tap somewhere”.

On iPad, to get both the main and the detail to show up, set the preferredDisplayMode property to .allVisible.

You should also make sure you’re displaying a navigation controller in your detail, so you can include the UISplitView displayModeButtonItem there.


On iOS, a UITableView is a subclass of a UIScrollView. While I understand why this is the case (99% of the time, you want a scrolling tableview), I’ve been starting to think that the OSX/cocoa approach of having them be separate classes is actually a better approach. But, I can see that this might have made the implementation of UITableView much easier to just always assume it’s in a ScrollView.

Refresh Control

It used to be that you had to use a UITableViewController and it’s refreshControl property to get a pull to refresh behavior. This is no longer the case. As of iOS 10, you can set UIScrollView's refreshControl property to get a refresh control on any scrollview (and any subclass). Of course, on earlier versions of the OS, you can also add the refreshcontrol as a subview of the scrollview, and it’ll still work. This is just less magical in how you do it.

Dismiss keyboard on scroll

The old (pre iOS 7) way of dismissing the keyboard when you scroll is to use UIScrollViewDelegate methods to be notified when the scrollview scrolled, and then call -resignFirstResponder from the scrollview.

The new way is to set the keyboardDismissMode property to either .onDrag on .interactive

Common Crashes

unrecognized selector sent to instance 0x8000000000000000

At first glance, the crash log will read like you tried to access an object that had already been deallocated. However, the giveaway is that 0x8000000000000000 address. This suspicious address tells you that you have a concurrent write bug. Somewhere, you have a race condition with multiple threads writing to the exact same address at the same time.


You can do either view-based animations, or layer based animations.

Layer-based animations are more customizable (you can do 3d effects), but are harder to work with as a result.


As of iOS 10, the new preferred way to do view-based animations is to use the UIViewPropertyAnimator class.


There are at least 3 ways to animate CALayer properties.

  1. Implicit animations.
  2. Implicit with CATransaction
  3. Explicit with CAAnimation

Implicit Animations

Implicit animations are fairly magical - set desired property on the layer to what you want it to be, and CoreAnimation will figure out how to animate the layer to reflect that.

This has the downside of being far less configurable, as well as being less obvious that an animation is actually happening.

You can also group animations using CATransaction, which also allows you to specify things like duration and such. It appears that CATransaction need to be wrapped inside of UIView animations.

CATransaction works by wrapping implicit animations up, and allowing you to modify their properties

You can call CATransaction.setDisableActions() with true in order to disable animations.

For testing reasons, even if animations are disabled, you still need to spin the runloop in order for the completion block to be called. Just call Date(timeIntervalSinceNow: 1e-3)).

Explicit Animations

CAAnimation is a cruftier API for handling animations. Most of CA hasn’t really been updated for recent objective-C, or even swift happenings.

For the most part, you’re going to use CABasicAnimation, for which you can specify a keypath to animate.

Note that the delegate for a CAAnimation is retained by the animation object. That is, it’s a strong reference, not a weak one (as others are). Be careful with that.

This does provide the nice benefit of adding block-based end notifications, with the following bit of code:

class BlockAnimationDelegate: NSObject, CAAnimationDelegate {
    private let onComplete: (Bool) -> Void
    func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {

    init(onComplete: @escaping (Bool) -> Void) {
        self.onComplete = onComplete

// [...]

let animation: CAAnimation // [...]
animation.delegate = BlockAnimationDelegate { finished in

Maintaining Position Post-Animation

One of the things noted in the CAAnimation Documentation is that the layer’s data model is not updated as part of the animation. This means that, by default, once the animation finishes, it’ll immediately go back to it’s starting position.

Fixing this is interesting, you can tell the animation to stick around for a bit, but why?

as Ole notes, you should instead set the fromValue property, so that the animation knows where to animate from, instead of letting it figure that out from the data layer.

For example:

let originalState = layer.position.y
let desiredState = CGFloat(50)

layer.position.y = desiredState

let animation = CABasicAnimation(keyPath: "position.y")
animation.toValue = desiredState
animation.fromValue = desiredState
layer.add(animation, forKey: "position")


For multiple themes in an app, I like using a ThemeRepository paradigm. When I only care for a single theme, then that’s overkill, and I’ll use UIAppearance as much as I can.

Styling UINavigationBar

(Adapted from this post).

UINavigationBar.appearance().barTintColor = navColor
UINavigationBar.appearance().titleTextAttributes = [.foregroundColor: textColor]
UINavigationBar.appearance().tintColor = navButtonColor

Styling the Status Bar

Note: This is deprecated as of iOS 9.

UIApplication.shared.statusBarStyle = .lightContent

Core Data

Core Data Programming Guide

Setting up

Apple’s documentation seems to be fine.

Persistent Store types are here, you’ll mostly be using NSSQLStoreType or NSInMemoryStoreType (for testing).


NSManagedObjectContext is the way to read/write objects to/from core data. Create a managed object context with a given concurrency type (either mainQueueConcurrencyType or privateQueueConcurrencyType), and only operate on it within blocks passed to perform(_:) or performAndWait(_:) calls. Be sure to only have one managed object context for your persistent store coordinator, or you’ll encounter strange crashes.

Additionally, keep in mind that NSManagedObject subclasses are not thread-safe (there are only a handful of properties/methods that are safe to access outside of a perform(_:) or performAndWait(_:) call). Instead of passing instances of NSManagedObject, pass around the object’s NSManagedObjectID (obtained from the managed object’s objectID property.

My preferred approach for accessing core data is to convert the NSManagedObject instance into another, thread-safe model object. This has the advantage of not leaking implementation details and concerns about my database layer to other layers of my app. Which, in addition to being good design, also means that I can switch out (or ignore) databases as makes sense for what I’m trying to do.

Storing Records

In my experience, using MyNSManagedObjectSubclass(managedObjectContext: context); context.insert(myCreatedObject) doesn’t work. Instead, use the older NSEntityDescription.insertNewObject(forEntityName:into:) to create and insert new objects.

Fetch Requests

Fetching by property with type URI

I ran into issues figuring this out. The approach you want is:

fetchRequest.predicate = NSPredicate("url.absoluteString = %@", urlToFetch.absoluteString)


Multiple NSEntityDescriptions claim an NSManagedObjectContext subclass

I encountered this in tests, where I was initiating up the Core Data stack from scratch with each test. Turns out that, because CoreData creates new classes when you bring up the context, you’ll end up seeing this warning with every new test.

The solution is to not create so many ManagedObjectModels - that is, instead of bringing up a new stack with each test, bring it up once, and then delete every object between run.

Core Graphics


CGFloat is a word-size agnostic way to express a floating point number (on 32 bit devices, it’s a float. On 64 bit devices, it’s a double).

CGFloat.leastNormalMagnitude is effectively the same as FLT_MIN (or DBL_MIN, depending on the device). It is less than or equal to all positive “normal” numbers. Subnormal means that “they are represented with less precision than romal numbers”. Note that zeros and negative numbers are also less than CGFloat.leastNormalMagnitude.

Core Spotlight

Making app content searchable!

In general, you should prefer to batch update the index. However, keep in mind that the default() index doesn’t support batching - you’ll need to create your own.


Add items with indexSearchableItems(:completionHandler:), and remove them with one of the deletion methods.

Opening an item that was searched for

Once you have your stuff in the index, you need to handle what happens when the user searches for and selects one of those items.

Doing this is the same codepath as continuing from a deeplink. Only, this time, the activity type will be CSSearchableItemActionType, with the item identifier (you should have picked one that actually refers to your item) as value for the CSSearchableItemActivityIdentifier key under the userInfo property. See Apple’s documentation on doing this.


There are 2 system ways to do layout in iOS.

  1. Frame-based
  2. Autolayout

Don’t use frame based layouts unless you have to. Especially when it comes to supporting multiple size classes and such, that’s way more effort than it’s worth.

In general, I prefer this for laying out code:

  1. Nibs w/ Autolayout
  2. Code w/ Autolayout
  3. Code w/ frames


From NSLayoutConstraint’s api:

Each constraint is a linear equation with the following format: item1.attribute1 = multiplier * item2.attribute2 + constant

Apple-Provided APIs

  • NSLayoutConstaint is the underlying api for specifying layout constraint. Everything else essentially gets converted to these when you use them.
  • NSLayoutAnchor, introduced in iOS 9, is a factory class that makes it way nicer to specify layout constraints, without having to resort to visual format language.
  • NSLayoutConstraint Visual Format Language, is used in a class constructor for NSLayoutConstraint.

Third Party Frameworks

  • PureLayout provides a declarative interface for creating and installing layout constraints. It works as categories on NS/UIView and NSArray.


Using localized string and such.


Sometimes, there are differences in the different localized versions of your app, and you need to test that in a unit test.

Here’s a fairly hacky way to do that:

private var bundleKey: UInt8 = 0
func setBundleLanguage(_ language: String) {
    let path = Bundle.main.path(forResource: language, ofType: "lproj")
    objc_setAssociatedObject(Bundle.main, &bundleKey, path, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
    object_setClass(Bundle.main, AnyLanguageBundle.self)

Network Link Conditioner

NSHipster describes how to install and use this.

This is a useful tool for seeing how your app works under different network settings.

A side effect of using Network Link Conditioner is that you can also identify when a test is mocking out the network by using a custom NSURLProtocol. Because those tests will also be affected by the network link conditioner. This is part of why if your unit test makes a network call, it’s not a unit test. Even touching the URL loading subsystem is making a network call.

By the way, NSHipster also has an excellent article on NSURLProtocol, because it is useful for mocking network requests for integration-style tests and the like.

URLSession and URLRequest



Apple provides a really nice built-in way to do caching, using URLCache. You can configure a URLSession object to use your specific cache via URLSessionConfiguration.urlCache.

Once configured, all requests through that session will use that cache, though it’s possible to override for specific requests, or for all requests from that session.

Note that URLSession.shared is configured to use URLCache.shared by default. This is transparent to the user (that is, there’s no easy way to determine whether or not the request actually used the network or returned cached data).

ETag and Manual Caching

Sometimes you want to manually cache responses. Because URLSession uses a cache by default, we have to tell our requests to not do that. There are a few ways to do that:

  1. Use a URLSession that isn’t backed by a cache (by creating one with the configuration’s urlCache property set to nil)
  2. Use a URLSession with a cache policy that ignores the cache (set the configuration’s requestCachePolicy to . reloadIgnoringLocalAndRemoteCacheData
  3. Have all your requests individually specify .reloadIgnoringLocalAndRemoteCacheData as their cachePolicy

Once you have the caching behavior set, you need to implement manual caching yourself. I’m going to describe using ETag because that’s better (and what my nginx server did for me).

The ETag header is one way to determine whether or not a resource has changed from when it was last served. It’s essentialy a hash of the resource information (as opposed to using something like Last-Modified for a time-based approach). You pair this with the If-None-Match request header to have the server calculate whether the data has changed (HTTP 200 response) or not (HTTP 304 response).

So, the algorithm for doing this is:

  • Make initial request
  • Record the value for the ETag (or Etag) header in the response you send.
  • In subsequent requests to the same url, include the If-None-Match request header, with value set to whatever you received for that Etag header.
    • If you receive a 304:
      • Use the older data you had (no cache update needed).
    • If you receive a 200:
      • Overwrite the etag you had with the newer etag header
      • Use the new data you received in the body of the response.


Local and Remote (push) Notifications, not NSNotifications.

This is going to describe the newer UserNotifications framework introduced in iOS 10, instead of the older UIKit-based way of doing notifications.


UNNotificationContent provides read-only access to information shown to the user about a specific notification. For setting information (e.g. when preparing to send a local notification), you’d use the UNMutableNotificationContent class.


Either kind of notification can be an actionable notification.


As of iOS 12, there are four kinds of notification: Calendar, Time, Location, and Push. The first 3 are used with local notifications, while the last is only used for push notifications.

  • Calendar triggers for a specific date: “Today at 7 pm”, or “every day at 8 am”.
  • Time triggers in a set time from now: In 30 seconds, or every 30 seconds.
  • Location1 triggers when the user either exits or enters a specific region. You can set to send the notification for both entry and exit.
  • Push is used to detect whether the notification you received is a push notification or not.

Types of Notifications

Local Notifications

Local Notifications are notifications generated entirely on the device. These would be things that appear when you enter or leave an area, at a certain time, etc.

The way to send a local notification is to create a UNNotificationRequest, with an identifier, content, and a trigger, then ask the current UNUserNotificationCenter to add(_:withCompletionHandler:) the request.

Remote Notifications

Also called Push Notifications. Push notifications are sent from some external server to your app.

As of iOS 7, you can also send “silent” or “content-available” notifications. These notifications do not present an alert to the user and instead wake up your app so that you can do something in response to the notification (usually update your content cache so when the user next opens the app they already have up to date information). See this apple documentation.


This requires location permissions, but not always permissions. Apparently, this is due to the system handling the monitoring as opposed to the app. I’ve never tried this, though.


NSUserActivity is a class to facilitate deeplinking into your app. The original (public) purpose was for handoff, it’s now been adapted for facilitating search and siri integration.

Setting up activities

You create one with an appropriate activity type, set the title, enable other properties as it makes sense, then finally call becomeCurrent().

Note that if you assign a user activity instance to a UIViewController‘s or UIResponder‘s userActivity property, then you don’t need to worry about calling the becomeCurrent or resignCurrent methods - these are handled for you.

Activity Types

These are strings, usually in reverse-DNS style, that describe the domain and the particular type of activity - e.g. com.rachelbrindle.second_brain.read_chapter describes opening/reading a chapter for com.rachelbrindle.second_brain. The activity types your app supports MUST also be mentioned in the Info.plist file, see NSUserActivityTypes.


Set the isEligibleForHandoff property to true.


This allows spotlight to present more optimized results to the user, as well as allowing the user to search for an activity they were previously engaging in.

Set the isEligibleForSearch property to true. If you want to help search results for other users, you can set isEligibleForPublicIndexing to true.

Note that your app must maintain a strong reference to any activity objects used for search results. Also, don’t use this to index all the app’s contents, that’s what the much more power core spotlight apis are for.

Continuing from a deeplink

(This only covers handoff, search and siri might be different)

The simplest way to continue from a deeplink is to implement application(_:continue:restorationHandler:) on your app delegate. Optionally, if your app might take a while to set things up (e.g. need to retrieve data from the network), then implementing and having your app delegate respond to application(_:willContinueUserActivityWithType:) will provide a nicer user experience.

Operation and OperationQueue



As the docs note, there are four things to override for your asynchronous swift subclass:

  • -start()
  • isAsynchronous
  • isExecuting
  • isFinished

And that you must send KVO notifications for the 2 properties (usually isAsynchronous is hardcoded to be true, so sending KVO for that is a non-issue).

Sending KVO means sending -willChangeValue(forKey:), then changing the value, then sending -didChangeValue(forKey:), see the following sample implementation:

class MyAsyncOperation: Operation {
    override func start() {
        self.willChangeValue(forKey: "isExecuting")

        someAsyncWork {
            self.willChangeValue(forKey: "isExecuting")
            self._isExecuting = false
            self.didChangeValue(forKey: "isExecuting")

            self.willChangeValue(forKey: "isFinished")
            self._isFinished = true
            self.didChangeValue(forKey: "isFinished")

        self._isExecuting = true
        self.didChangeValue(forKey: "isExecuting")

    override var isAsynchronous: Bool { return true }

    private var _isExecuting: Bool = false
    override var isExecuting: Bool { return !self.isFinished && self._isExecuting }

    private var _isFinished: Bool = false
    override var isFinished: Bool { return self._isFinished }


UIPopoverPresentationController is the new (as of iOS 8) way to do a popover. This replaces the older UIPopoverController, and should be used for anything recent.

Arrow Directions

If you only ever want to show your popover from a given direction, you can control this with the permittedArrowDirections property. According to the documentation, you can only do this when configuring, not after it’s been presented1. As the name suggests, this controls where the arrow on the popover shows, not where the popover is relative to the sourceRect/sourceView.

When the device rotates

You can also control where the popover comes from by updating the sourceRect/sourceView. This can be done after receiving viewWillTransition(to:with:) on the presenting view controller. Be sure to call view.layoutIfNeeded() on the presenting view controller before updating this, otherwise the sourceRect might be outdated2.


I haven’t tested this myself to see what happens if you do try to change that property after it’s been presented.


I’m unsure if you have to call layoutIfNeeded() if you use sourceView instead of sourceRect.


WKWebView is the view you should use to display web content inside of an app.


WKWebView is somewhat unique in that it has two delegate methods and protocols - uiDelegate and navigationDelegate


Implement a WKNavigationDelegate to respond to url navigations - starting a navigation, authentication issues, errors, etc.

Don’t open links.

Say, for example, you don’t want clicked links to be opened in the webview. You’d implement webView(_:decidePolicyFor:decisionHandler:) to detect if it’s a link, and then call the handler with .deny, like so:

func webView(_ webView: WKWebView, decidePolicyFor action: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
    switch action.navigationType {
    case .linkActivated:

You might instead choose to open the link elsewhere.


Implement a WKUIDelegate to respond to UI requests - javascript UI panels, upload panels, force touch.

UI Testing with iOS Devices.

XCUITest, introduced in iOS 9, is a technology for automating acceptance tests. It works by running your app in a separate process from the test, with the test communicating to the app using a form of IPC (Inter-Process-Communication). Elements are identified via accessibility IDs/values.


You can fetch a group of elements matching a predicate by calling element(matching:) on any XCUIElementQuery. Most objects in XCUITest are XCUIElementQuery’s.

Anything that conforms to XCUIElementAttribute can be queried as part of one of these queries.


  • Finding text on a Cell
    Honestly, I had more luck with app.tables.cells.element(boundBy: 0).firstMatch.staticText[LABEL_ACCESSIBILITY_ID].


For developing cocoa-based programs.

Dark Mode

Dark mode, because black is the new black.


In CSS, this is done via the prefers-color-scheme media query.

See this mdn page.

NSOutlineView and NSTreeController

View nested lists easily!

NSOutlineView is a subclass of NSTableView that provides a way to display hierarchical data. For example, file hierarchies (though, you’d actually use an NSBrowser object for a file hierarchy).

NSTreeController is a controller that works with NSOutlineView and NSBrowser to manage the data that they display.

In cocoa, controllers are super powerful because they allow you to bypass implementing a lot of the really boring delegate/datasource stuff that you’re forced to do in iOS.


This is a much better explanation of how to set up bindings correctly than I’m currently able to do.

Delegate Methods


Implement outlineView(_:tooltipFor:rect:tableColumn:item:mouseLocation).


Opening a URL

This is super simple, call with the url, and it’ll open in the user’s default browser.


Rust is a language that I’ve been in love with for ages.

It’s also one of the most frustrating languages I’ve ever used. This is because I’ve never written enough rust to actually be good at it.

It also has the best documentation of any language.

Serializing json in rust.

Follow this guide using serde.


Add to Cargo.toml‘s [Dependencies] section:

serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

Make your struct derive Serialize, and pass it to serde_json::to_string()

struct Thing {
    x: i32

fn main() {
    let thing = Thing { a: 1 }
    println!("{}", serde_json::to_string(&thing).unwrap());



Swift and Objective-C


NS_REFINED_FOR_SWIFT is a macro that helps you write “swifty” API for objective-c code.

In the header file, you tag the method declaration with this macro, and in a swift extension, you write a swift implementation that uses it. This method prepends two underscores (myMethod:whatever: -> __myMethod(_: whatever:)) to most methods, though in the case of initializers, it appends to the first argument label.

Patterns and Pattern Matching

Last Update: Swift 5

Pattern refers to “the structure of a single value or composite value”. Here are the list of patterns

Wildcard Pattern (Underscore)

This is what _ means. It matches and ignores any value.

for _ in 1...3 {
    // do something three times

Identifier Pattern

This is the basic assignment pattern. let someValue = 42 is an example, someValue is an identifier pattern that matches the value 42 of type Int.

Value-Binding Pattern

Value-Binding is something on top fo identifier, it’s one of the first cases of pattern matching you might find, e.g.

let someTuple = (4, 5) // Tuple Pattern
switch someTuple {
    case let (x, y): // Binds x and y to the elements of someTuple
        // do something with x and y

Tuple Pattern

Refers to “a comma-separated list of zero or more patterns, enclosed in parentheses.” Note that parentheses around a single element are effectively ignored.

The following are valid example of Tuple Patterns:

let aTuple = (1, 2)
let (a, b) = (3, 4)
let (a) = 2 // Not a Tuple Pattern

Enumeration Case Pattern (Enum)

It matches the case of an existing enum type. They appear in switch case labels, as well as if, while, guard, and for-in statements.

Using this enum:

enum AnEnum {
    case foo
    case bar
    case baz

let myEnum =

switch statement:

switch myEnum {
    case .foo:
        // do something
    case .bar:

if statement:

if case .foo = myEnum {
    // do something.

while statement:

while case .bar = myEnum {
    // do something

guard statement:

guard case .baz = myEnum else {

Optional Pattern

This matches optional values. Uses the ? syntax sugar to match things.


let someOptional: Int? = 32

// Matches using enumeration case.
if case .some(let x) = someOptional {
    // do something with x

// Matches using the optional pattern.
if case let x? = someOptional {
    // do semithng with x

This also works with for-in, and switch statements:


let arrayOfOptionals: [Int?] = [1, nil, 3, nil, 5]
for case let number? in arrayOfOptionalInts {
// prints "1", "3", "5"


let someOptional: Int? = 32

switch someOptional {
    case 32?:
        // something.
        // something else.

Type-Casting Patterns

This is the is and as patterns. is is used as a conditional (if foo is Int), or in switch statement case labels (case foo is Int: // do something with foo as an Int). as is used to change type to a related one, as needed (foo as String).

Expression Pattern

This represents the value of an expression. These appear only in switch statement case labels.


let point = (1, 2)
switch point {
case (0, 0):
case (-2...2, -2...2):

You can also overload the ~= operator to provide a custom expression matching beavior.

func ~= (pattern: String, value: Int) -> Bool {
    return pattern == "\(value)"

switch 3 {
case "3":
    print("This actually matches")


Vapor is one of two swift web frameworks to have gained traction (the other is Kitura, from IBM). It appears that vapor has slightly more documentation available that Kitura does, so I use vapor.

However, Vapor still has PLENTY of rough edges that make it a pain in the ass to develop against.

Specify the http status error

To the best of my knowledge, there are two easy ways to return a custom http status error: throw an AbortError, or return a Response. (The other way is to create your own type that conforms to ResponseDecodable, and have it set the http status in encode(status:headers:for:))


AbortError is a protocol, which means you have to create your own instance of it in order to return one. Simple enough, but still annoying. Your custom implementation needs to have 3 properties: status, reason, and identifier. As the name indicates, you throw your error from the request handler.

Return a Response

From your asynchronous request handler, you can chain on .encode(status:for:) to set the status. (The second parameter is the request object your request handler was called with).


I haven’t gotten around to writing a microframework to do this, but here’s my Application extension I add to every vapor project I do:

import Vapor

@testable import App

extension Application {
    static func testable() throws -> Application {
        var config = Config.default()
        var services = Services.default()
        var env = Environment.testing
        try App.configure(&config, &env, &services)
        let app = try Application(config: config, environment: env, services: services)
        try App.boot(app)

        return app

    func sendRequest<Body>(to path: String, method: HTTPMethod, headers: HTTPHeaders = .init(), body: Body?) throws -> Response where Body: Content {
        let httpRequest = HTTPRequest(method: method, url: URL(string: path)!, headers: headers)
        let wrappedRequest = Request(http: httpRequest, using: self)
        if let body = body {
            try wrappedRequest.content.encode(body)
        let responder = try make(Responder.self)

        return try responder.respond(to: wrappedRequest).wait()

This is used as:

let subject = try Application.testable()

let response = try subject.sendRequest(to: "/my/path", method: .PUT, body: Optional<String>.none)