Development


A Few Things I Use the Command Line For

21st October 2021

I’ve noticed myself using the command line a lot more recently, at home, and work, so I thought I’d share a few of the little tools and handy commands that I use on a day to day basis.

Note: I use ZSH as my shell, with oh my zsh, so they may differ slightly if you’re using something different.

Aliases

The most helpful commands that I use have to be aliases for the most minor commands. But because they’re used so often, it saves so much time.

The majority of them are for two things - moving to common directories, and launching applications.

Here are a few examples of directories I have set up with aliases:

  • h: home directory
  • dev: developer directory where I store all projects
  • tc: Text Case directory
  • blb: Bulba directory

That’s just a small snippet, but usually, I have most projects set up with a very small alias. But even if I don’t have one set up for each project, I’ve got one that puts me at the root of my developer folder anyway.

As for applications, I’ve got a few that I use a lot:

  • xc: Opens a xcodeproj in the current directory
  • xcw: Opens a xcworkspace in the current directory
  • vs: Opens the current directory in Visual Studio Code
  • fork: Opens the current directory in Fork - A Git GUI, for when I want to dig into any conflicts.

Git

Being a developer, I use Git quite a lot. And that is where oh my zsh comes in handy, as it comes with a huge number of aliases for common Git commands. Here’s a great cheatsheet.

Here are the ones I use the most, and also what the full command is:

  • gco: git checkout - checkout branch
  • gaa: git add --all - adds all changes in the current directory
  • gc -m "": git commit -v -m "" - commits the current changes with a message
  • gp: git push - pushes commits to the configured remote repo
  • gf: git fetch- fetches branches and tags from the configured remote repo

I’m aware that those are pretty minor commands, but they’re so much easier when they’re just one two or three letters.

Also, oh my zsh does come with an alias for committing changes with a message, but it’s gcmsg and that’s longer than just using gc with the -m option.

The most used Git command I use though has to be a little ZSH function I made myself:

function gacp {
    gaa; gc -m $1; gp
}

It stands for “git add commit push”, and as you can probably tell, it adds and commits the following changes, with the supplied message, and pushes it to the remote repository.

Most of the time I’m doing stuff like this:

gcap "JIRA-123 fix tests"

Woops!

FTP

I don’t use these a lot, but I do have a few aliases to update various websites. They basically use the scp command (secure copy) to transfer files from a local directory to a remote server. Guide.

This isn’t exactly what I have, but they all follow this rough syntax:

scp -r /Users/chris/website user@123.123.123.213:../var/www/

This will recursively upload the contents of a local directory to a remote server. I use this whenever I update changes to my blog theme.

HTTP Requests

Whether I’m working on a mobile app or REST API at work, I’m usually testing various requests throughout the day. And while I sometimes use a tool like Postman, especially when I’m building a collection for QA testers, I do find it a bit cumbersome sometimes. So that means I end up resorting back to the terminal.

I’ve seen a tool called httpie which does seem to be quite good, but I’ve found curl to be good enough for my uses.

Tip: If you’re stuck with the syntax and don’t have time to wade through documentation, I’d recommend using a tool like Postman to build the request, and you can then export the curl request.

Most of the time I’m just performing GET requests, so the syntax is simply:

curl https://dev.chrishannah.me/feed.json

If you need just the headers of the response there’s the -I option, and if I want both the headers and body it’s a lowercase -i.

Environment Variables

Usually, I’m using the command line because I want to test quite quickly, and with slight tweaks, so I find making the command as short as possible helps with this.

The first one for me is to use environment variables. So for example I’ll use one for the base URL of the API, and usually a few for any variables that need to be in the path, especially if these are user account numbers, as it makes it a lot easier to quickly test different scenarios.

This means that a request like this:

curl https://company.com/api/account/2a3e4832-14e6-430d-8c34-748f4626e864/transactions

Can be made a lot shorter by using two environment variables:

export base=https://company.com/api 
export account=2a3e4832-14e6-430d-8c34-748f4626e864/transactions

Which means it can look like this:

curl $base/account/$account/transactions

The biggest benefit I find is that allows you to edit the command much easier.

Using Files

Another tip I have for curl is that if you have a bunch of headers that you need to use, then it helps to have these stored in a file.

You can do this by using the -H command followed by @ and then the filename. For example, this command will read the headers from a file named headers.txt:

curl https://website.com -H @headers.txt

The header file needs to be in this format:

Key: Value

So something like this would work:

Content-Type: application/json
Authorization: Bearer [token]

This is especially handy for me as most of our APIs at work require various authorisation tokens that can be quite large.

You can also use other options to use files for storing the body of the request, but I’ve not had much experience of that, so I’ll have to defer to google.

JSON

This goes hand-in-hand with making HTTP requests, in that the responses are usually JSON. For that I use the JSON processor, jq.

Most of the time, I’m just using it to “beautify” data from a curl request, so I pass through the response to jq by piping the output from curl to jq like so:

curl https://dev.chrishannah.me/feed.json | jq

What that does is take the response from the curl request and output a pretty printed version of it.

But you can also use jq to parse the JSON response and pick out certain fields.

So for example, a request to my blogs JSON feed at https://dev.chrishannah.me/feed.json will return a fair bit of JSON data. Something like this:

{
  "version": "https://jsonfeed.org/version/1",
  "title": "Chris Hannah's Dev Blog",
  "home_page_url": "https://dev.chrishannah.me/",
  "feed_url": "https://dev.chrishannah.me/feed.json",
  "description": "A devlog by Chris Hannah",
  "author": {
    "name": "Chris Hannah",
    "url": "https://chrishannah.me"
  },
  "items": [
    { ... }
  ]
}

But say I only wanted to read the author object, I’d just need to use this command:

curl https://dev.chrishannah.me/feed.json | jq '.author'

Which will return just this:

{
  "name": "Chris Hannah",
  "url": "https://chrishannah.me"
}

There’s a lot more it can do as well, and I’d recommend checking out these examples.


I’m sure there are tons of other resources that go into far more detail on what you can do with the command line. But I thought I’d share a few things that I use it for, just in case it might prompt others to find some ways to save themselves time!

“Built using Bulba”

20th October 2021

Two announcements. Firstly, I’m building a static site generator. And secondly, that I’m using said generator to power a new blog of mine.

Bulba

That’s the name. My new side-project, which is a static site generator, built using Node.js, that can transform a few Markdown files into a static site. It’s still early days now, but it’s already got paginated index pages, archive, about page, JSON feed, and of course, a page for every blog post.

I’m in no way a Node.js or JavaScript programmer, so I can’t always be certain that my code is the best. (Nor can I say that it’s the worst). But I think it will be a fun project to work on, and maybe in the future other people can use it.

You can find Bulba on GitHub and NPM.

Development Blog

To both demonstrate the features of my new tool, and to also share the progress of it’s development, I’m using Bulba to power a new development blog. It’s got a pretty nice url as well: dev.chrishannah.me .

Right now, I’m using it for Bulba specific updates, but I can imagine that in the future I may use it to write about other projects I work on too.

Earlier today, I published a post, “Introduction to Bulba”, on my new blog, where I introduce Bulba in detail, showing how it works, what features are currently implemented, and also what I’m working on next.

Goodbye, Qwiki

8th March 2021

Unfortunately, I've decided to remove Qwiki from sale.

If you weren't aware, Qwiki was a Mac app that placed Wikipedia in your menu bar. It was pretty simple, you could search for a page, view a page, and there were a few methods to export links from the app.

Qwiki was first released in June 2016 and received updates until November 2019. At that point, I was relatively happy with the app, since it was only ever meant to be a minimal way to quickly search Wikipedia.

However, as time has gone on, the codebase has become stale, and the app, in general, doesn't feel at home on the more recent versions of macOS.

I've felt for a while that I shouldn't be offering an app that isn't being maintained, but my thoughts were that while people still used it, there could still be more that could get value from it. But after receiving a few support requests asking for Catalina, mainly around the text appearing too small, small icon resolution, and a few more things, I can't continue to make that excuse anymore. So therefore if I have no plans to ever update the app, I can't ask people to pay money for it.

I toyed with the idea of just making it free, but I still think a free app needs to be of a certain standard, and to a point, maintained.

This means that I now only have one app, Text Case, although it is available on iOS, iPadOS, and macOS. I do have plans to work on a new project this year, but nothing is in the works just yet.

Having an App Featured in the Mac App Store

5th March 2021

At the start of last month, I released a major update to my app, Text Case. It got some good reviews, and that helped boost sales, which I'm very grateful for. But as you would expect, after a week or so, the initial surge ended, and it went dropped to a stable level every day.

But I was looking at the app analytics section App Store Connect one day and I saw  a huge amount of impressions on the macOS version. It was around 1 million impressions, where usually it would be around a thousand a month.

In the end, it was around 3 million:

I immediately thought it meant that my app must have been linked somewhere popular, or that it was featured in the App Store. Turns out it was the latter. Text Case was in fact featured in the "Apps and Games We Love Right Now" section.

I started thinking that this could mean a sudden increase in downloads because so many people would know about the app.

But as it turned out, there may have been a small increase, where the sales didn't drop off as fast as it would have done without the feature, but it wasn't what I was expecting.

Below is the number of units during the same period as the above impressions (February):

After a while thinking about this experience, I've come to the conclusion that this just shows how much word-of-mouth and direct recommendations work, compared to simply being visible on the app store. If a few reviews can have the same impact as millions of impressions, then they must be pretty effective.

For a while, I always imagined that simply being featured could push an app to get a large number of sales. Maybe it's because Text Case is a particularly niche app, but I think this proves that directly reaching people that would benefit from your app is the best way to grow sales.

Maybe this won't surprise anyone, but it's a perspective that a few people may find interesting, so I thought I'd share.

Text Case 2021.1 Beta

17th January 2021

After quite a long time of development, the major update to Text Case that I've been working on is finally ready for a public beta.

This update changes the focus of the app from simply using built-in formats, to focus on user-created flows. A flow is a collection of formats, which together can become a more complex text transformation.

Along with flows, 6 more complex formats have been added. The complexity in them is that they support parameters. So when you add them to your flow, you will need to configure them. The new formats are:

  • Replace all occurrences of text a specified string of text another string of text.
  • Replace the first occurrence of a specified string of text with another string of text.
  • Replace the last occurrence of a specified string of text with another string of text.
  • Remove all occurrences of a specified string of text.
  • Remove the first occurrence of a specified string of text.
  • Remove the last occurrence of a specified string of text.

As they are more complex actions, they have specific Shortcuts actions, Replace and Remove. You can obviously also access them if you add them as part of a flow, as flows are now accessible via the Shortcuts app too.

There's a few extra tweaks to the design, and also some configuration options for the Share extension. But that's essentially the main changes in this update. I'll write a much more comprehensive post when the update is finalised and ready to download from the App Store.

If you want to give it a try, you can join the TestFlight beta. Any feedback or ideas are completely welcome.

Having Some Fun With Continuous Integration

19th February 2019

I had an interesting day at work today, as I was configuring a new project work with our CI server, and have things like Unit/UI Tests in a readable format, and also convert the code coverage into something that could be stored along with the build artefacts.

Just for some background, we use Bamboo as a server, and I’m pretty limited with what I can actually configure myself, without getting someone with higher privileges. So I try to work within my limitations, and see what I can come up with.

I use Fastlane as the main solution to manage the whole process. And that means I can use the scan and slather commands to do the heavy lifting for the testing/code coverage. The way I had to integrate it in our CI server was reasonably simple. The test results were handled by setting the output type to junit, and then adding a simple JUnit Parser task on Bamboo. The code coverage was slightly more complex, as it needed me to run a python package that converts it into a “Clover” format that Bamboo could understand.

What was more tricky, was getting this data nicely formatted when it was sent to our Slack room. The previous build plans all had notifications handled form Bamboo, and it just gave a short message with the number of tests that passed/failed. I wanted more insight this time though, as I knew the test data was available, and also that I had code coverage being generated. I decided that the simplest (maybe it wasn’t in the end) solution was to just find a way to read the information from the .xml files, and send a custom message to Slack as part of the Fastlane process.

What I ended up with is a kind of monstrous-masterpiece. In Fastlane I had the Slack command being called with some basic information about the build, such as the branch, project name, and whether it passed/failed. But to get the results of the Unit/UI tests, I thought I’d use grep to find the line in the junit file that had text like “tests=100 failures=0”, I then used sed to clean up the surrounding text, and had the final output as “Passed: 100, Failed: 0”. The code coverage was slightly harder. I used grep and sed again in the same way to find the total code coverage, but it was formatted like “1.00000000”, and I wanted a percentage. So I piped that through bc with a small calculation, and they’re not formatted as a percentage with two decimal spaces.

Then with some magic of environment variables, I added two build-specific URLs to the message payload. One for the build details, and the other which linked directly to the code coverage report.

What I ended up with was something like this:

  • iOS App
  • Tests Passed: 100, Failed: 0
  • Code Coverage 100.00%
  • Coverage URL https://build.com/coverage/IOS_BUILD_99
  • Build URL https://build.com/IOS_BUILD_99/something
  • Result Success
  • Git Branch master

I’m not sure if all of that is relevant for each build, or if I’ll have to include some other things I’ve forgotten about. But what I can say, is that it was really fun to come with all of these little scripts that come together with something so simple at the end. And it’s quite likely that no-one else seeing the messages will have any idea the lengths I went to to make everything appear so simple.

Starting Work on Text Case 2.0

10th January 2019

As you may have already seen on my Twitter, or in my journal entries, I’ve started to work on the second major version of Text Case, 2.0. The major changes will be to the user interface, so I want it to be slightly more colourful, fit more in what I see as the latest design language Apple has set out in the Shortcuts app, and also have the formats structured better.

The project started with me making a list of all the things that I will need to implement for it to be level with the functionality of the current version. Here’s that list:

  • Input
    • Drag and Drop
    • Input Field
    • Use Copied Text
    • From File?
    • Keyboard Shortcuts
      • Global Paste
  • Formats List
    • Tap to Copy
    • Hold to Share
  • Siri Shortcuts Support
    • Add to Siri
    • Shortcuts App
    • Backwards Compatibility
  • Action Extension
  • Settings
    • Theme
      • Light
      • Black
    • Title Case Format
    • Reorder Groups
    • Enable/Disable Formats
    • Custom App Icons

I started working on the most important section of the app, the formats list. Over the past few days I’ve been building up the style similar to the Shortcuts app, so instead of being simple white boxes that contain the formatted text, they’re more colourful and even have a slight gradient to add a bit of depth (I’m planning on experimenting with a small shadow as well).

So once the list was working, I added the core logic from the current version and made the formats work. I did adapt it slightly though, as it now groups similar formats together, which I think makes the app look a lot tidier. This change means that when I add the reordering feature, it will most likely me limited to reordering the groups rather than individual formats. You’ll still be able to hide any you don’t want to see though.

Then I added the input field. It’s also a bit cleaner, and fits with the new style. But it has essentially the same capabilities as before. I plan on investigating importing text from a file, and implementing drag and drop, but I think that’s supported automatically.

After I had the list displaying, input working, and the text being formatted, I worked on the interaction with the resulting formatted text. I’ve had a few bits of feedback in the past saying they would appreciate one-touch copying, and now I’ve added it! So you can simply tap any formatted text in the app, and you’ll get a nice alert at the bottom showing the exact text you’ve copied. Or alternatively, you can still tap and hold on formatted text to bring up the contextual actions, which are the same as before, copy and share.

The next step from here will be to start working on the settings section of the app, as that also allows me to test the rest of the app in different scenarios much easier. I’m already planning two changes to the settings in this new version. The first is changing the idea of an accent colour to a theme, as I want the format groups to control the colour. But I also appreciate that a light and dark theme is a minimum. The second change is custom app icons, they may be a basic selection, but the app no longer has a “main colour” so I’d like to give a few options.


If you want to stay up to date with the development of Text Case 2.0, You can find more regular content on my Twitter, brief updates on my journal, and I’ll still post any major progress here.

Text Case 1.3.1

12th October 2018

Another update to Text Case has just hit the store!



Just a small one this time though, to tie up a few things, before anything big can be planned or worked on. In fact it can be boiled down to three things:

  • A new text format, this time it’s KebabCase. And as usual it was requested, so I added it! There’s no chance that I can come up with every format possible, so if you want one added then please just let me know.
  • About section added (website links, App Store link, app version…)
  • And I’ve fixed a bug in the Action Extension. As the UI used to inherit some of the styles from the encompassing app, but it wouldn’t always look correct. I’ve fixed this by keeping it matching with the rest of the app, along with the chosen accent colour.

It’s not an extravagant update, but then again, they can’t all be.

Find Text Case on the App Store.

The Official Decision on Slate

10th October 2018

It’s been in my head for a while, but I’m finally making my decision about Slate official. If you weren’t already aware of what Slate actually is (was), it’s an iOS app for the Micro.blog platform. I’ve spent quite a lot of time working on it, and I used to use it quite a lot myself, as it was good enough for reading posts, and also publishing text-only posts.

I always knew the next steps for the app. The main things I planned to work on next were:

  • Image uploading
  • Markdown preview when composing posts
  • Faster timeline parsing/scrolling
  • A caching mechanism for timelines
  • Support for non-Micro.blog micro blogs

These are totally achievable features, and it’s what I classed as being necessary before being able to release it as a public app (It’s currently been in TestFlight beta for months).

But about two or three months ago, I basically stopped using Micro.blog. I transitioned my posts over here, but I still planned on using the service but have it tied to one blog. I’m not sure what really made me stop using it, but it sort of faded over a long period of time, and I didn’t feel like missing anything, so I didn’t go back.

This all tied in with me slowing down on the development of Slate. Although I was still doing occasional small updates, and it was in my head that I would soon spend some time on the bigger features that I wanted to implement. But the time never came. And I became less interested in building an app for a service that I wasn’t using anymore.

It also combined with me starting to focus on good UX in my apps, and trying to really create great experiences. And as a user of Tweetbot (Twitter client for iOS and macOS), I didn’t think I would realistically be bale to put the time and effort into making a great app for Micro.blog.

So after a huge chunk of time (probably just over two months), I’ve decided that I will no longer work on Slate. And at least for the foreseeable future, I don’t see myself touching the code at all.

It doesn’t mean I’m completely over with Micro.blog, the platform, though. I’ve listened to the Micro Monday podcast, where Jean MacDonald, the community manager, talks to members of the Micro.blog community), pretty regularly. Even since stopping using the service. And it still sounds like Micro.blog is a nice place to be, with loads of interesting people. So there’s a possibility of me returning as a normal user, but very unlikely that I’ll work on Slate again!

If you want to find me on Micro.blog, I am still under the username chrishannah. However, the only content that gets posted there currently is the RSS feed of this blog. But that’s where I’ll be if I return.

Text Case Has Been Released!

7th July 2018

I’m very glad to announce that Text Case is now released, and is live on the App Store!

Text Case is a simple utility that allows you to convert any text into various different formats.

It comes packed with an action extension that lets you select text anywhere in iOS, tap the Share button, and then you’ll find the “Convert Text” action. This will show you a preview of all available formats, and a simple tap on one of those will copy it to your clipboard, and you’ll be returned to the original app.

The available formats are currently:

  • Title Case
  • URL Encoded
  • Uppercase
  • Lowercase
  • Capitalise
  • Reversed
  • Mocking Spongebob (This one is for fun)

More formats will be added in the future!

Download Text Case on the App Store!