After owning an iPhone 13 Pro for two years, I received my new iPhone 15 Pro Max
in the post earlier today. And I already want to share some of my very early impressions.
Primarily because I’m very glad I upgraded to this model, and it’s (obviously?) a very
good upgrade from the 13 Pro.
Design
Starting with the actual design of the device, it’s not too much different from recent models.
I don’t personally care what material is used, but I’m definitely a fan of the finish. And while
the edges of the 13 didn’t cause me any trouble, I am finding myself appreciating the slightly
softer edges of the 15.
In both the 13 Pro and my new 15 Pro Max, I’ve chosen the colour closest to black. The 15 seems slightly
darker in appearance, which I appreciate. My favourite colour so far was the black iPhone 5, but I think
this is pretty close.
Action Button
I haven’t set this up properly yet. Although I do envisage settling on having it launch
either the standard Camera app or possibly Halide.
I can’t say I have particularly strong opinions
about it at the moment, but I think I’d prefer if the action was immediate, rather than requiring a
long-press. Although, it would be better if you could configure different actions for a long-press or a
typical button press.
USB C
Most of the time I’ve charged my 13 Pro via a MagSafe charger on my bedside table. However, I do have
a Lightning cable in my work bag that I use occasionally, and I also prefer to take a cable when
on trips. So USB C won’t exactly make a huge difference to me. But it will certainly be handy to be
able to have one cable for practically all of my devices.
Camera
I haven’t used this too much yet. But I did play around with Portrait mode, and so far I’ve been very happy
with the results. The adjustable aperture and focus point both work well, and while I don’t know how often
I will use them, they’re good additions. I think all three cameras are different to my 13 Pro, so while I’ve
been initially impressed, I want to spend more time with it to have any real opinions.
Speakers
I don’t know if the speakers in my 13 Pro had deteriorated, or if an improvement came in last years or this
years models. But the speakers seem much better. They’re certainly louder than my iPhone 13 Pro, and they seem
to also be clearer.
Dynamic Island
I want to separate my feelings on the Dynamic Island into two parts.
Firstly, it’s clearly a bad thing to have a cutout in a display. It means that software either has to work around
it, or completely disregard that area of the screen.
On the other hand, I do like what they did with the cutout. I like having quickly glanceable information there,
like timers, food delivery times, and also the currently playing music. And I also like having the ability to tap
it and quickly navigate to whatever is appearing. For example, tapping the now playing “bar” (not sure what this is called)
to open the Music app is handy.
However, if I was given the option, I’d rather the cutout didn’t exist at all.
My opinions on the 15 Pro Max will no doubt change as I use it. But as for right now, this is how I feel. I may write about it here again, or if not, I’ll probably just post about it on Mastodon.
With it, comes a new author field, and also a source field. Which means a text shot can contain a title, author, source, and the quote. This hopefully makes Text Shot become more usable when sharing quotes from books, and maybe a few other places that I haven’t thought about.
Alongside the new fields, there’s also a “Copy Alt Text” button. This, of course, generates and copies a description of the text shot, that you can upload alongside the image when you post it to sites like Mastodon.
Here’s an example text shot:
And here is what the alt text would be for it:
A text shot containing the following information:
Title: Text Case CLI via Homebrew
Author: Chris Hannah
Source: chrishannah.me/text-case-cli-via-homebrew.html
Highlight: To use these formats, you can pass in input in three different ways - you can use the --input option to pass a string of text, the --in option to specify a file to use as input, or you can pipe in data from stdin.
There’s also a bunch minor UI changes that no-one will probably notice. 😅
I wrote just a few days ago, about Text Case coming to the command line. And it’s
already time to announce that it’s now available to install from Homebrew.
Okay, so it’s not in the core tap, I have my own custom tap (maybe that will happen eventually).
But it’s still a pretty easy process.
All you need to do is:
brew tap chrishannah/textcase
brew install textcase
Then you’ll be able to format text using the textcase command. Which is pretty easy. I used
it myself to format the slug inside Neovim when writing this post.
While I’m here, I may as well explain what functionality is supported in the very first release
of Text Case CLI.
To start off, the currently supported formats are:
stripHTML - Strip all HTML tags.
stripWhitespace - Remove all whitespace.
trimWhitespace - Remove any preceeding or succeeding whitespace.
clapCase - Put 👏 between every word.
hashtags - Convert words into hashtags.
rot13 - Reverse all characters.
shuffled - Shuffle all characters.
slug - Convert the text into a slug.
smallCaps - Convert all characters into small capital characters.
mockingSpongebob - Turn your words into something sarcastic Spongebob would say
upsideDown - Flip all characters.
capitalise - Capitalise the first letter.
capitaliseWords - Capitalise all words.
lowercase - Make all characters lowercase.
reversed - Reverse all characters.
uppercase - Make all characters uppercase.
sentence - Capitalise text as a sentence.
To use these formats, you can pass in input in three different ways - you can use the --input
option to pass a string of text, the --in option to specify a file to use as input, or you
can pipe in data from stdin.
The outputted string will be sent to stdout, but you can also use the --out option to have the
resulting text written as a file instead.
If you have any questions or feedback about Text Case CLI, then feel free to email me, or you can
find me on Twitter/X or Mastodon.
Written: while relaxing in a caravan in Wells-next-the-Sea.
I spent quite a bit of time trying to work out how to get a Swift LSP working in Neovim the other day. Enough time that I wanted to share it here.
I won’t go into too much detail about what an LSP is, how it works, or its benefits. There are a lot of other people who can do a much better job of that than me.
However, I would like to say that this is just how I have it working myself. There are bound to be many other ways you can get a Swift LSP working in Neovim. It just happens that this is a pretty simple configuration, that I will likely also improve in the future.
The first step is to obviously install the Swift LSP. Apple’s one (I’m not sure if there are any others) is SourceKit-LSP. You can build this from source (this is what I did), or the README also explains that it’s also included in the toolchains found on Swift.org. So do whatever is best for you. But, be sure that it is accessible on your $PATH.
After that, you’ll need to configure Neovim to find the LSP, tell it what files to look for (swift), how it can detect the root directory, the name of the command, etc. And then, more importantly, start the LSP, and attach it to the buffer.
This is Lua code, so use it in whatever .lua file makes sense to you. However, if you just want to include it in a .vim file, you’ll need to wrap it like so:
" lsp
lua << EOF
-- lua code goes here
EOF
After that, you should be able to write terrible Swift code and have it tell you all the things that you’re doing wrong.
If you didn’t already know, I make a text transformation app for iOS, iPadOS,
and macOS, called Text Case. It’s grown quite a bit over it’s lifetime,
supporting now well over 60 different formats, custom flows, and many more
features. And I’ve now decided, the next place I want to bring it to, is the
command line
And not just to be an additional benefit of purchasing the existing apps. This
is an open-source project, which eventually I want to distribute via Homebrew.
That may sound weird, seeing as you need to pay to have it on other platforms.
Why would I release it for free?
Basically, I’m a fan of free and open-source software. And while I don’t think
it’s crazy to ask for £2.99 for an app that I’ve put quite a number of hours
into making, I do also want to give something back. So, I plan on building
a version of Text Case that will allow people to quickly transform text, using
the command line.
At the same time, I’m bound to improve my own programming skills, seeing as my
code will now be in the public eye. And maybe I’ll learn more about building
and distributing open-source software.
However, I do have to say that not all of Text Case will be coming to the command
line. I only plan on adding support for the core formats. At least, that’s the
plan for now.
Right now, it supports just 17 different formats, and I’ll be working on
bringing the rest over as time goes on. And because it’s open-source, you can
keep up to date with the current state by checking it out on GitHub.
(You can even use it right now, if you’re comfortable with building from
source.)
Here’s a quick screenshot of the current version in action:
I hope this news sounds good to at least some of you. In the mean time, I’m
going to get back to adding more format options.
Configuring Nginx to Work With Hugo’s “Ugly” URLs #
I recently migrated my blog from running on Ghost to Hugo. With
that, came a few changes to how the pages were built, how the URLs were formed,
and also the rules around showing 404s and redirecting where possible.
In a default Hugo installation, individual blog posts are each written as
index.html files inside a directory named after the given slug. So you would
end up with something like so:
That means when someone visits either /hello-world or /hello-world/,
they’ll be taken to the content (Which is actually stored as
/hello-world/index.html).
Just to make things difficult, I didn’t want my file structure to look like
that. It felt wrong. Instead I wanted each blog post to be generated as it’s
own html file, with the slug being the filename.
Fortunately, Hugo has an option called uglyURLs, which does exactly
that. So the same structure above would instead look like this:
However, while this may look good to me. It introduced a few issues that I had
to deal with. Primarily being that the .html extension had to be given for
a page to load. There was no more /about being redirected to /about.html.
And definitely no /about/ being supported.
That obviously would break a lot of past links to my blog posts. So I had to
find a way to deal with this.
So I set about finding a way in nginx, to essentially remove the need to have
the extension in the URL. But I then realised, that it wasn’t that simple.
Because, let’s say I have an about page with the filename about.html.
Ideally, I want /about.html, /about, and /about/ to redirect to this
page. But at the same time, I have a mini-site for my app, Text Shot, that
sites at /text-shot/index.html, and ideally I want /text-shot/index.html,
/text-shot/, and /text-shot to all redirect to this site.
That led me to a lot of Googling, regular expressions, and quite a bit of time
spent trying various options in my nginx configuration. (If you were super
unlucky, you may have hit a 404 while I was testing).
By default, my nginx configuration handled the requests with the try_files
function:
try_files $uri $uri.html $uri/ $uri =404;
Basically, if request was /about, it would try to fetch a file from these
locations (in order): /about, /about.html, /about/, and then if all of
those fail, produce a 404.
The code to deal with removing the need to include .html was relatively
simple. All it does is check if the request includes the extension, and if so,
remove it and perform a 302 redirect to the plain URI.
However, that still meant that if you visited /about/, it would think you
were looking for /about/index.html, rather than the /about.html page.
So, I had to then add another redirect. So if the url ended in a trailing
slash (but wasn’t just /), it would also perform a 302 redirect to a url
without the trailing slash. I ended up with this:
For reference, $uri is the full request URL, and $request_uri is anything
after the host.
There’s bound to be a way for this logic to be improved. But as far as I can
see, it works how I expect, and it gives me the exact behaviour I was looking
for.
Now for some examples. First off, the ways in which you would get to a file
located at /about.html:
(Note: all redirects are stated, the rest is the priority order of the
try_files function.)
/about.html: 302 to /about -> try /about -> try about.html.
/about/: 302 to /about -> try /about -> try about.html.
/about: try /about -> try /about.html.
And for the page located at /text-shot/index.html:
/text-shot/index.html: 302 to /text-shot/index -> try /text-shot/index -> try /text-shot/index.html.
/text-shot/index/: 302 to /text-shot/index -> try /text-shot/index ->
try /text-shot/index.html.
Note: nginx will search for an index file when try_files checks $uri/, so
there’s no need to handle that. However, there were circular redirects when /text-shot/ would be redirected to /text-shot, and then eventually back to /text-shot/, so I added an explicit attempt to $uri/index.html to avoid this.
Admittedly, a few of those examples are a bit odd, and likely will never
happen. But I had to cover all bases.
As you can see, it’s not perfect. There are some requests that go through
a 302 just to end up at the exact page that was requested. Like #2 above.
In an ideal world, I’ll never touch this configuration again. And there’s also
a low chance that anyone reading this would ever need to do anything similar.
But, I can’t say I found many resources online for my specific scenario, so
I thought I’d write my own.
I guess I brought this all on myself when I decided I wanted to both install my
own custom Hugo blog on a linux VM, and to also want static files with the full
filename, not just convenient “pretty” URLs that Hugo generates by default. Oh
well, at least I learned a bit more about how to configure nginx I guess.
As of tonight, I’ve now completed the final stage of transitioning my blog from
Ghost to Hugo.
As you may have known, my blog has been powered by Ghost for a few years.
It’s definitely served me well. However, a while ago I started to like the idea
of static sites. Seeing as my blog is essentially a list of static posts, it
felt a bit weird that the pages were being served dynamically. So, I started
on the journey of moving to Hugo.
It took a bit of work to adapt my existing theme to work with Hugo’s template
system. But I managed to get it pretty similar, while also adding a few
improvements at the same time.1
Of course, being a static site, pages now load super fast, and they’re
extremely lightweight. But that’s not the only good thing about using Hugo.
It’s also super easy to host other static pages alongside the source Markdown
post files. This means I can build mini-sites for my apps, and keep them
managed within my blog. I can control the structure of the site better, either
by using categories/tags or by structuring the source files in the way I want
them to be generated. And another great one, I can now create custom pages and
templates. For example, I built a custom archive page for all of the posts
on the blog, and a few extras that as a bit different for essays and
travel updates.
In the early version of this new Hugo blog, I had it being deployed to
a Digital Ocean app via a GitHub action that was triggered after pushing new
posts. But, that didn’t allow me full control of the VM it was running from, so
I decided to switch to a droplet (VM), and have built my own minimal
installation.
Obviously, Hugo is installed, but apart from that the only other things I had
to install was nginx and certbot. So very minimal. I was wondering how I’d
manage the deployment, because I still wanted to have the site automatically
rebuild after pushing my changes. Luckily, I found a guide by Josh
Hausotter that shows you how to configure a remote repository on your
Digital Ocean droplet and a “post receive” action that runs a script whenever
changes are received to generate the static files and move them to the correct
directory. I honestly never thought about using git this way.
As for how I write and publish my posts from my own machine. I do that using
Neovim on my Mac, and then just pushing to the remote repository. Neovim
might not be the most trendy tool for writers, but I find it works for me, and
I also use it for writing code, so I’m pretty comfortable with the keybinds.
You may be wondering, what does this mean for you? Do you need to change
anything? Well, in theory, no. The posts are now stored as static html files,
however I have configured nginx for these to work without the extension, and
the filenames/slugs haven’t changed. Technically, the RSS feed is now
different. Hugo generates the RSS feed in an index.xml file at the base of
the site, which you can find here. However, I have set up directs for
/feed and /rss, so you shouldn’t need to do anything.
Hopefully, this change will go by mostly unnoticed. But if you do notice
something odd, you know where to find me. (Links are at the bottom of the
page)
I’ve been thinking about the cameras on older iPhones, and what makes people
prefer certain models, rather than whatever model is the newest.
A quick answer would be that it’s just subjective, and we all like different
things.
But if you think about it, it’s also affected by how people use cameras, what
they class as photography, and also what they expect in a photo.
From a photography point of view, it’s easy to imagine that one model had
a desired focal length or aperture. And then, if you go deeper into
photography, you might be looking for certain colour reproductions,
temperatures, tones, lighting, etc. For example, I’m a fan of natural grain in
images, so I would prefer one that produces a certain type of grain.
So there’s already a few different metrics that divide opinion. But on top of
that, there are a lot of people that use the iPhone’s camera that aren’t
photographers.
Some may want the best tool to capture memories, others might just want to
document their life, just like the cliché of people posting photos of their
lunch to Instagram.
And in those cases, it may seem that the latest model would always be the best
answer. But that might not always be true. Recent phones tend to come with
a lof of built-in adjustments and corrections that are applied automatically to
photos. Therefore, even if you’re aim is to take a photo that documents
a certain scene as accurately as possible, the “better” camera might not
actually be the best option.
It’s weird to think that a better camera doesn’t always produce better photos.
Maybe it’s just because my own opinions have been changing recently, but
I get the feeling that there seems to be a general resurgence of analogue over
digital. Film photography is having a moment, so are mechanical watches it
seems, and the act of writing in a physical notebook also seems to be growing
in recent years. That’s in no way a definitive list, but it’s just a few things
I’ve noticed.
This may sound weird, but it feels to me like it’s in some way related to the
current period of nostalgia that we seem to be going through as a society. In
a lot of ways, it’s like people are trying to bring back the 90s1.
Technology is always going to be massively affected by feelings of nostalgia,
simply because of the rate of change that it goes through. However, while there
has always been the divide between analogue and digital, there now seems to be
a new divide2 in digital technology, offline vs online.
There’s no WiFi or Bluetooth, so it’s just out here on its own. It never has updates to install, so it’s never going to get better, but it’s also not going to change in ways I don’t like. It’s also going to work just as well in 20 years as it does today.
As he mentions in his post, while completely offline technology won’t
improve, it also won’t get any worse. Which definitely happens to more
recently technology that requires a connection to the internet.
I don’t think we’re at some major turning point in society where we’re all
going to start writing in paper notebooks, switching to a dumb phone, etc. But
it’s worth at least noticing the new era of always needing to be
connected3. And at the same time, celebrating the good sides of having
technology that can exist on its own, in the same state, for as long as the
hardware still works correctly.
Written: In my dimly lit living room, listening to music from Isenseven videos.
I’m not complaining, the 90’s were pretty good. ↩︎
The More Social Networks There Are the Less I Want to Use Them #
There’s been an influx of new1 social networks recently, such as Threads,
Mastodon, and Bluesky. And to some extent, I’ve tried to keep up with them. But
if anything, I’ve noticed that my use of social media, in general, has been
dropping.
I used to use Twitter for talking about everything, and Instagram for posting
my photography. But then Glass came along and for a while my serious
photography was going there, and then BeReal made me want to save a photo for
them every day. As for text content, there’s been Micro.blog,
Mastodon, and Threads that I’ve tried as alternatives to
Twitter.
However, all I tend to use now is just Instagram, Twitter/X, and Mastodon.
Instagram is where my friends and family tend to be, and that’s where I post
photos of what I’m up to, and also any real photography. Mastodon is where
I’ve found a lot of tech bloggers and developers have flocked to, so I’m there
for that crowd. And I still haven’t given up on Twitter/X, because I’ve found
it to still be the best place for current events, football content, and
a bunch of tech people are still there.
And when I say, I use these three platforms, that’s not the same level as
before. I used to try and read every tweet in my timeline, both on Twitter
and Mastodon, and I’d spend countless hours scrolling through Instagram.
Now, I’ve got notifications turned off for everything, and I’d say I browse
Twitter/X slightly regularly. But I only really go on Instagram and Mastodon
now when I want to post something. I haven’t found them to be good places to
browse. I get too sucked in when using Instagram, and I haven’t yet cultivated
a good enough following list for me to spend a lot of time in Mastodon2.
I’ve found that right now, I’m more interested in people in the real world than
on the internet. That’s not a dig at anyone I’ve talked to online. But it
doesn’t replace talking to people in the physical world.
I think the reason why I’m preferring to write for my blog over social media,
is that it’s a more biased relationship. It allows me to collect my thoughts, and
then express them in whatever form I feel fits the content and context. And
then if people want to reply in any way, they can do so via email, Mastodon, X,
etc. But, at a slower pace, and also in any which way they feel relevant.
The real-time speed and perceived urgency of social media are reasons why I’ve
stepped back from it a bit. So, if you’ve sent me a message online or by email,
know that I’m probably not ignoring you. I either haven’t got around to
reading it yet, or I haven’t yet found time to think and reply.