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 tried replacing ICANN’s authority in the past, long before gTLDs existed. They offered some snazzy options, and I grabbed 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.

Localizing attributed strings on iOS

In an iOS app, localization can be especially difficult when dealing with attributed strings. Fairly often, designers request something like:

Read our Terms of Service, Privacy Policy, or contact us with any questions.

or like:

Searching for burgers in SOMA, San Francisco, CA:

The golden rule of localized strings is to treat them as atomic units:

  • Never concatenate strings to form sentences. Many languages have different sentence structure or gender rules than English and you cannot just substitute a single word or phrase in for any other.
  • Never use substrings for searching in an original string. If you depend on the standalone translation of “Privacy Policy” matching the sentence version, you will likely find translators do not understand this intent. You may also find the same search term multiple times (imagine if the user searched for “Search”).

Often these complexities are cited as reasons to avoid localization. But, unless you have geographic constraints, you will find a substantially larger audience with a localized application.

ZSWTappableLabel and ZSWTaggedString are two open-source libraries I have released to help solve these problems.

ZSWTappableLabel makes links inside your attributed strings tappable, as the name suggests. It’s a UILabel subclass which does not do any drawing itself, making it fast and easy.

ZSWTaggedString is the powerhouse. It transforms an HTML-like syntax into an attributed string. You can read more about the syntax and advanced usage on its GitHub page, but here’s how you might use it for the examples above:

Read our <i><tos>Terms of Service</tos></i>, <i><privacy>Privacy Policy</privacy></i>, or <i><contact>contact us</contact></i> with any questions.

Searching for <term>%@</term> in <location>%@</location>:

In my experience, localizers1 are familiar enough with HTML to have no issues with localizing these strings. By marking the regions you intend to be visually distinct, they can more easily understand your intent, producing better localizations.

While on the subject, here are a few best practices for localization in iOS:

  • To handle the current locale changing, or the dynamic type setting changing, reload your UI when observing:
    • NSCurrentLocaleDidChangeNotification
    • UIContentSizeCategoryDidChangeNotification
  • To represent dates, durations, distances, lengths, etc., use an appropriate formatter.
  • To create your own date formats, use +dateFormatFromTemplate:options:locale: on NSDateFormatter. Remember that these need recreating if the locale changes.
  • To combine a first and last name, use ABPersonGetCompositeNameFormatForRecord with a temporary ABPersonRef, or use the new NSPersonNameComponentsFormatter.
  • For sending non-user-facing data to a server, use en_US_POSIX as your locale.

    Read more tips and tricks at NSHipster about NSLocalizedString and NSLocale.

  1. If you’re looking for recommendations on localization, working with Applingua has always been wonderful. 

Saving optimal JPEGs on iOS

Conventional wisdom for creating a JPEG version of a UIImage is first to turn it into an NSData and immediately write it to disk like so:

NSData *jpegRepresentation = UIImageJPEGRepresentation(image, 0.94);
[jpegRepresentation writeToFile:outputURL.path

Most of the time this is exactly right. However, if file size is important, Image IO is a great alternative. It is a powerful system framework to read and write images, and produces smaller files at the same compression level.

Why Image IO?

A project I am working on requires uploading photos en masse. Low upload bandwidth makes file size a limiting factor, so I sought out ways to reduce it.

I put together a test project to find the differences between the two methods. The results are pretty interesting:

  • Image IO files are on average 20% (but up to 30%) smaller1.
  • Image IO takes about 2x longer.

The only discernable visual difference is the grain in the images, but even that is minor. Here’s a diff between two versions of the original photo. The changes are nearly all in the grain.

A diff between the two image output types

Using Image IO

First, you’ll need to add two new framework dependencies:

@import ImageIO; // to do the actual work
@import MobileCoreServices; // for the type defines

When creating your JPEG file, the first step is to create a CGImageDestinationRef specifying where to write the result:

CGImageDestinationRef destinationRef = 
CGImageDestinationCreateWithURL((__bridge CFURLRef)outputURL,
                                /* file type */ kUTTypeJPEG,
                                /* number of images */ 1,
                                /* reserved */ NULL);

Image IO is able to produce files of a few different types2 but my focus here is JPEGs. Next, we set up the properties of the output file, specifying a constant compression factor:

NSDictionary *properties = @{
  (__bridge NSString *)kCGImageDestinationLossyCompressionQuality: @(0.94)

                                (__bridge CFDictionaryRef)properties);

And, importantly, we specify what is to be written out:

                           /* image */ image.CGImage,
                           /* properties */ NULL);

And finally, we write it to disk and clean up the reference:


  1. The UIImage version has a color profile, while the Image IO version does not. However, running both files through Image Optim produces a 7% reduction on both, so I am choosing to ignore this difference. Afterall, you can’t remove the color profile anyway! 
  2. The following are possible types you can use, from the documentation:

    Constant UTI type
    kUTTypeImage public.image
    kUTTypePNG public.png
    kUTTypeJPEG public.jpeg
    kUTTypeJPEG2000 public.jpeg-2000 (OS X only)
    kUTTypeTIFF public.tiff
    kUTTypePICT (OS X only)
    kUTTypeGIF com.compuserve.gif

Using the Xcode Structure menu

Xcode’s Editor > Structure menu has a few great actions:

Screenshot demonstrating where to find the menu item in Xcode

These actions all act on either your cursor position or selection.

Balance Delimiter

Normally you can double-click quotes, brackets, or parenthesis to select the matching character and all text in-between. The Balance Delimiter behaves similarly: it looks at your cursor position (or selection), finds the nearest pair and selects in-between.

This doesn’t have a default keyboard shortcut, but you can set one up in Xcode’s Key Bindings preferences. I set it to ⇧⌃I since I use it in similar ways to Re-Indent.

Re-Indent ⌃I

Objective-C is a fairly indentation-heavy language, and Xcode generally does a good job at indenting while you’re typing. However, if you’re pasting text or refactoring things can get pretty hairy, so let Xcode fix up your code for you with Re-Indent.

Shift Left ⌘[, Shift Right ⌘]

You can also indent manually using the Shift Left and Shift Right actions, which unindent or indent by one tab, respectively. They do exactly what they say on the tin.

Move Line Up ⌥⌘[, Move Line Down ⌥⌘]

These actions move your current line (or selection) up or down by one line. Simple, right? What’s really useful is that it is context-aware: they understand going in and out of control flow or blocks. Much faster than cutting, pasting and re-indenting each time.

Comment Selection ⌘/

Rather than wrapping your code in /* … */ and dealing with the conflicting multi-line comments you probably already have, simply select what you want to temporarily eliminate and hit the shortcut. Each line selected is then prefixed by //.

Error arguments in Objective-C

From the Programming with Objective-C (backup) overview from Apple:

When dealing with errors passed by reference, it’s important to test the return value of the method to see whether an error occurred, as shown above. Don’t just test to see whether the error pointer was set to point to an error.

and from the Error Handling Programming Guide (backup):

Success or failure is indicated by the return value of the method. Although Cocoa methods that indirectly return error objects in the Cocoa error domain are guaranteed to return such objects if the method indicates failure by directly returning nil or NO, you should always check that the return value is nil or NO before attempting to do anything with the NSError object.

For example, let’s say we’re executing a fetch request:

NSError *error = nil;
NSFetchRequest *request = /* … */;
NSArray *objects = [context executeFetchRequest:request

To test if the fetch was successful, we must do:

if (objects) {
    // hooray!
} else {
    NSLog(@"Got an error: %@", error);

A method taking an NSError ** does not guarantee how it is used when successful. In this case, even a successful method call may end up with error set to something other than nil.