Standard Notes is an end-to-end encrypted notes syncing application built for longevity:
Our revolutionary, paradigm-shifting 21st-century business plan is to keep your information ready for the 22nd century. The notes you write now should be there for you in a 100 years. That’s our killer app.
It’s one of the rare times when I value function over form. It’s an ugly application, feeling wholly unnative and lacking in important things like keyboard shortcuts. Why keep using it? There’s no other service that can sync my notes with zero knowledge across devices.
One of the bigger holes–not being able to attach and sync files–is partially filled today with the release of FileSafe:
When you use FileSafe, you attach files[…]to your individual notes. These files are then encrypted by Standard Notes offline (client-side) first, then uploaded in their encrypted form to your Dropbox, Google Drive, or WebDAV compliant server (Nextcloud, ownCloud, Seafile, Synology, and others).
It’s barebones. You can upload to a note and download from a note. There’s no previews, no inlining. It doesn’t work on mobile yet. It’s not what you would expect out of attaching a file to a note.
Privacy is such a strong differentiator.
Another risk for generic TLDs: your registry may turn the TLD into a spam-filled mess which permanently ruins your domain.
This documentary/ad from AT&T in the 1970s has Bell Labs employees introduce and describe how Unix differs from other operating systems, as well as about the ethos of Unix.
Related is the Computerphile interviews with Brian Kernighan who participated in this era of Bell Labs (and this video quite excellently).
It’s impressive how they managed to build so much fundamental concepts and designs in such a short period of time. Much of the computing world is still based on how those original Unix programs were written and the decisions of those working in Bell Labs.
The Style Guide for Google’s open-source projects includes some interesting recommendations that I hadn’t seen before, including:
For file size optimization and scannability purposes, consider omitting optional tags. The HTML5 specification defines what tags can be omitted.
This includes tags like <html>
, <head>
, and <body>
as well as closing tags for elements like </li>
and </p>
. The difference can be rather stark. An extremely basic page may look like:
<!DOCTYPE html>
<title>A page about nothing</title>
<h1>Introduction</h1>
<p>Also the conclusion.
Alignment rects in Auto Layout views
In the UIView
documentation, Apple describes alignment rects:
The constraint-based layout system uses alignment rectangles to align views, rather than their frame. This allows custom views to be aligned based on the location of their content while still having a frame that encompasses any ornamentation they need to draw around their content, such as shadows or reflections.
At first glance, this feature feels unnecessary: views can draw outside their bounds seemingly without performance issues1. However, the differentiation between frame and alignment is a powerful, and easily overlooked, feature in Auto Layout.
Automatic with constraints
Views that use Auto Layout for positioning and sizing get alignment rect support for free via alignmentRectInsets
. Unfortunately, going by the documentation, this is not evident2.
Let’s work through this using an example. The vertical line is going down the exact center of the container:
This view has a label and decorative yellow box to the side. Notice that we’re centering relative to the label, and not to the decorative box. This is accomplished with a very small amount of code:
override var alignmentRectInsets: UIEdgeInsets {
return UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 10)
}
The constraints within the view can completely ignore this fact. With the insets set, the layoutMarginGuide
and direct leading
, trailing
, etc., anchors are automatically inset. This is also true for constraints created via NSLayoutConstraint
.
The entirety of the positioning for the view above looks like this:
label.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
label.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor),
label.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor),
label.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor),
label.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor)
])
// alignmentRectInsets doesn't support RTL,
// so use left/right rather than leading/trailing
yellowBox.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
yellowBox.topAnchor.constraint(equalTo: topAnchor),
yellowBox.bottomAnchor.constraint(equalTo: bottomAnchor),
yellowBox.leftAnchor.constraint(equalTo: rightAnchor),
yellowBox.widthAnchor.constraint(equalToConstant: alignmentRectInsets.right)
])
We can now apply whole-view effects, like the border in the example above, without having to create elaborate view hierarchies. The decoration is part of the view, not floating outside of it.
The only downside here is that alignmentRectInsets
does not use the new NSDirectionalEdgeInsets
introduced in iOS 11, so right-to-left support may need to look at effectiveUserInterfaceLayoutDirection
.
Tappability
Apple recommends 44pt tappable areas for controls which are often designed to be aligned with other elements on the screen, often times closer than this tappable region.
To combat this problem, a common solution is overriding hitTest(_:with:)
on the button to allowing it to tapped outside of its frame. This makes it hard to visualize where a screen is tappable when debugging the view.
Let’s solve this problem using alignment rects. A custom UIControl
subclass can use the regular insets like the view example above. Unfortunately, UIButton
does not use Auto Layout internally, so a small subclass is needed to handle the insets:
class IncreasedTappableButton: UIButton {
// one potential optimization is to calculate insets that would
// make the button >= 44.0 pt tall/wide
override var alignmentRectInsets: UIEdgeInsets {
return UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
}
override var intrinsicContentSize: CGSize {
var size = super.intrinsicContentSize
size.width += alignmentRectInsets.left + alignmentRectInsets.right
size.height += alignmentRectInsets.top + alignmentRectInsets.bottom
return size
}
private var boundsInsetByAlignmentRect: CGRect {
return UIEdgeInsetsInsetRect(bounds, alignmentRectInsets)
}
override func backgroundRect(forBounds bounds: CGRect) -> CGRect {
return super.backgroundRect(forBounds: boundsInsetByAlignmentRect)
}
override func imageRect(forContentRect contentRect: CGRect) -> CGRect {
return super.imageRect(forContentRect: boundsInsetByAlignmentRect)
}
override func titleRect(forContentRect contentRect: CGRect) -> CGRect {
return super.titleRect(forContentRect: boundsInsetByAlignmentRect)
}
}
This gives us a button where the frame is slightly bigger, but things are positioned relative to the original size:
It is worth pointing out that this will not work inside a UIStackView
since the larger frame will extend outside the bounds of its container and UIStackView
is fairly aggressive about ignoring outside touches.
If outside views are opaque and do not cause blending or offscreen rendering, it does not appear there are any additional costs to having subviews drawing outside bounds.
-
I’ve spent hours searching for documentation about the performance impact of drawing outside bounds, and there’s not a lot of detail available. ↩︎
-
Often in Apple SDKs, the headers contain information the documentation is missing. This is from the UIView headers:
Constraints do not actually relate the frames of the views, rather they relate the “alignment rects” of views. This is the same as the frame unless overridden by a subclass of UIView.
I did not notice this and I am sure many others are missing this key piece of documentation. ↩︎
Doctors Without Borders uses drones to plan their operations by mapping people and infrastructure. What a smart use of of the technology. In places where accurate maps aren’t available, they generate them.
Better shadow performance on views
There are two different uses for the shadowPath
property on CALayer
:
- Improving the performance of having a shadow
- Creating shadows that don’t match the contents of the view. Check out Apple’s Using Shadow Path for Special Effects.
For performance reasons, always set a shadowPath
. This is a substantial improvement, especially if the view changes position via animation or presence in a scroll view.
When you can set a path
The shadowPath
tells the system what should be casting a shadow without having to look at the contents of the view itself. Since most views that need a shadow are opaque, we just need to describe the appearance of the background of the view.
Using the convenience initializers on UIBezierPath
we can create ovals, squares and rounded rectangles without difficulty. For more complicated paths, check out A Primer on Bézier Curves. You can still use UIBezierPath
or CGPath
to create them, but it will require more complicated math.
Starting with a simple, purple view with a shadow:
let purpleView = UIView()
purpleView.backgroundColor = .purple
purpleView.layer.shadowRadius = 10.0
purpleView.layer.shadowColor = UIColor.black.cgColor
purpleView.layer.shadowOffset = CGSize()
purpleView.layer.shadowOpacity = 0.8
We can tell the system to draw a shadow for the entire square:
purpleView.layer.shadowPath = UIBezierPath(rect: purpleView.bounds).cgPath
For rounded corners, we can set the cornerRadius
property on the layer, and create a matching shadowPath
:
purpleView.layer.cornerRadius = 16.0
purpleView.layer.shadowPath = UIBezierPath(roundedRect: view.bounds, cornerRadius: 16.0).cgPath
When you can’t set a path
Sometimes it’s not possible to set a path because there’s no easy way to describe the contents of the view. For example, text is a mess of random contents. Rasterizing the layer avoids having to draw the shadow repeatedly.
// create our label
let label = UILabel()
label.textColor = .purple
label.text = NSLocalizedString("Swift Lemma!", comment: "")
label.layer.shadowOpacity = 0.6
label.layer.shadowColor = UIColor.black.cgColor
label.layer.shadowOffset = CGSize(width: 0, height: 2)
// render and cache the layer
label.layer.shouldRasterize = true
// make sure the cache is retina (the default is 1.0)
label.layer.rasterizationScale = UIScreen.main.scale
This produces a view that looks like this:
Keep in mind
Always set the shadowPath
inside either layoutSubviews()
or viewDidLayoutSubviews()
. Since Auto Layout likely means there aren’t constant sizes for views, setting a shadowPath
elsewhere may become outdated or incorrect.
When creating a path, the coordinate system for the path is the layer it’s applied to. To make it easier, pretend the shadow path is a subview. For this reason, we use the bounds of the view to create its shadow path.
Layout margins within a UIStackView
The UIStackView
property isLayoutMarginsRelativeArrangement
allows insets similar to margin constraints on subviews in a UIView
.
Let’s consider a simple single-subview example:
let containedView = UIView()
containedView.backgroundColor = .purple
let stackView = UIStackView()
stackView.addArrangedSubview(containedView)
You can then configure directionalLayoutMargins
and enable them like so:
stackView.directionalLayoutMargins = NSDirectionalEdgeInsets(
top: 8,
leading: 8,
bottom: 8,
trailing: 8
)
stackView.isLayoutMarginsRelativeArrangement = true
This is what it looks like, before and after:
ICANN created a history project documenting its formation nineteen years ago. I’m captivated by the interviews: corralling support and preventing disputes between so many interests seems like an impossible task. I couldn’t imagine the current political climate would come close to breaking something away from the US government with such bipartisan support. Their policies are downright ridiculous at times, but their history is certainly rich.
Is using a generic top-level domain a good idea?
I’ve been thinking about switching over my website and email to one of the new top-level domains. This has lead me to investigating what the switch would feel like, and how stable the move would be.
Will it survive?
I am looking at the .engineer
gTLD now owned by Donuts. At the time of writing this, there are a total of 2706 registered domains since late 2014. That’s nothing.
That got me thinking: what exactly happens when a gTLD fails?
The answers aren’t clear. When applying, ICANN requires registrars put up cash in the form of a bond to cover operational costs for 3 years. If a registrar were to fail, another can propose to take over. Their database is stored off-site, and data can be migrated.
But what if nobody does? What happens to a gTLD if there’s not enough domains to stay in business? The answer, it seems, is that the domain ends. There’s no provisions at ICANN to maintain domains beyond the transfer procedure.
Donuts, for what it’s worth, has stated they would not shut any down:
We think of all the TLDs as one big registry. It[’]s profitable, so all our TLDs are profitable, but that is beside the point. We’d no more shut down one of our TLDs than you would shut down 100 “unprofitable” second-level names in .link.
There’s definitely risk, and that’s not what the internet needs. It should be that, regardless of the fate of any registrar, a domain you purchase today will be valid as long as you renew it.
As an email
Generic top-level domains have been available for registration since 2013, but there’s a number of services that can’t handle them. I’m surprised how many times I enter one into an email field and see “invalid address” as the result.
The responses I’ve received are generally the “doing it wrong” variety and not the “I’ve filed an issue and we’ll look into it.” I’m not sure what I expected to be honest; I hoped that it would be passed up the food chain, but it always dies in the first round of support.
This means, to use a gTLD, I need to keep a backup domain for services like AT&T, CBS, Virgin Airlines, and Crunchyroll. I expected that in 2017 it wouldn’t be a problem, and for the most part it isn’t an issue. It’s frustrating though.
Premium domains
New.net tried replacing ICANN’s authority in the past, long before gTLDs existed. They offered some snazzy options, and I grabbed zac.tech
to play around. It didn’t work on most ISPs, but it did work on mine.
That’s a valid gTLD now! I could register it again! For the low, low cost of $2800. Per year.
This notion of a premium domain name is a money-grab by registrars. What constitutes a “premium” domain is arbitrary: length, dictionary words, prettiness, etc. If you try to register one of these domains at NearlyFreeSpeech you get a perfectly correct error:
This means the registry of this gTLD plans to extort extra money from anyone who wants this domain.
It is, and they do. These premium prices may come down. Perhaps they’ll stop charging extra to renew them entirely. But when your registry has a few thousand total domains are premium bottlenecks the right way to go about this?
The future?
I’m worried that entire namespaces are being taken by companies for their internal use, like Google seems to be doing with .dev
. If you’ve got the cash, you can take complete, even dictatorial, ownership. That’s not how existing domains worked, but it’s the rules we’re living under with ICANN’s leadership.
But we can’t continue to have one namespace. We’ve been in a world where everything but .com
was wrong, and Verisign’s control over it has been harsh. These new top-level domains are nicer looking and there’s significantly more availability.
So I’m thinking about switching. There’s a lot to choose from, and more opening up every day. I’m on a ccTLD right now, and there’s a real risk that it could go away at any time through local laws or disputes. Remember when every startup was using Libya’s .ly
domain?
Generic top-level domains feel like an improvement for the internet as a whole. The cruft at the end doesn’t have to be cruft; it can be descriptive, it can be helpful, and above all it can be nice.
Libby is a nice way to browse the San Francisco Public Library’s collection of eBooks and audiobooks. It has a lot of rough edges. It makes up for it with the send-to-Kindle feature so I can read borrowed books on a proper screen.
How Apple explains audio session prioritization, from Activating an Audio Session:
I did not expect a comic when I clicked on the documentation link.
In Apple’s latest The Rock x Siri advertisement you can hear the Wilhelm Scream at 2m43s. After the episode of Twenty Thousand Hertz about its backstory, it’s hard not to find it everywhere.
Age of Swords comes out next week, but that didn’t stop this Barnes & Noble from putting it out early. If I were willing to go non-digital I’d be reading it right now.
I’m looking forward to Iron Gold. It’s the fourth book of the Red Rising series which explores distant future humans splintered into sub-groups with one dominant over the solar system.
Personal names around the world explains the complication of names. The biggest take away: ask users for their full name and short name. Don’t try and parse out first names or separate first and last into separate fields.
Butterick’s Practical Typography has recommendations and pointers about laying out and writing copy. The ranking of system fonts is especially useful. Love the dislike of title case in headings.
Vault 11 in the game Fallout: New Vegas contains an interesting experiment: how would people react if an authority tells them to sacrifice one of their own? Oxhorn examines the vault and draws parallels to the Milgram experiment. Excellent worldbuilding.
I’ve been listening to The History of English Podcast. It’s not easy to binge but it’s a mainstay in my weekly podcast rotation. I’m currently around the fall of the Roman Empire. It’s fascinating that our alphabet has bounced between cultures but with only minor changes.