Yearly Archives: 2016

How to read Command Line Input on macOS

I was building a simple Command Line Tool app for macOS. One thing the app needed was user input, i.e. it should wait for the user to type something that I’d like to make use of in the app.

Turns out it’s a rather complicated affair, and I haven’t found a comprehensive starter guide on how to actually accomplish this.

I wanted to create a Command Line Tool app that was capable of accepting text input from the Terminal window, use it, and then write output back for the user to read.

But that wasn’t enough: I also had to tell Xcode to setup the app appropriately, otherwise the Terminal window wasn’t launched – which is of course necessary for a Common Line Tool.

In this article we’ll do just that: prepare Xcode to launch Terminal, wait for input, and print it out again. Here we go.

Continue reading





How to use a specific voice for text-to-speech in iOS

There are two ways of creating voices with which we can make iOS talk: creating a voice using a locale (classic), or creating a voice using a specific voice identifier (futuristic).

Let me show you both options here.

Classic and Easy

In this snipped we’re creating a voice with a certain dialect, British English in this case:

NSString *phrase = @"I'm listening.";

AVSpeechSynthesizer *synthesizer = [[AVSpeechSynthesizer alloc]init];

AVSpeechUtterance *utterance = [[AVSpeechUtterance alloc]initWithString:phrase];

AVSpeechSynthesisVoice *voice = [AVSpeechSynthesisVoice voiceWithLanguage:@"en-GB"];

utterance.voice = voice;

[synthesizer speakUtterance:utterance];

While very straightforward, we don’t know if the voice is going to be male or female. All we can specify is the language and dialect.

Futuristic and Specific

The above works fine and probably was enough when the speech synthesiser framework was introduced in iOS 7, but since then there are a myriad of other voices we can use in our applications. To specify one of them, we need a voice identifier.

Here’s how to do it:

NSString *phrase = @"I'm listening.";

AVSpeechSynthesizer *synthesizer = [[AVSpeechSynthesizer alloc]init];

AVSpeechUtterance *utterance = [[AVSpeechUtterance alloc]initWithString:phrase];

AVSpeechSynthesisVoice *voice = [AVSpeechSynthesisVoice voiceWithIdentifier:@"com.apple.ttsbundle.Karen-compact"];

utterance.voice = voice;

[synthesizer speakUtterance:utterance];

The setup is almost the same, but instead of the voiceWithLanguage method, we’re using the voiceWithIdentifier method here.

Finding Voices

To see a list of all available voices on the device, we can access the speechVoices method of the AVSpeechVoices class. This will return an array of AVSpeechVoices, all of which have a name, quality and identifier property. The latter is what we’re looking for so we can create a specific voice.

Here’s a method lists all available voices on the current device:

- (void)listVoices {

    NSArray *allVoices = [AVSpeechSynthesisVoice speechVoices];

    for (AVSpeechSynthesisVoice *voice in allVoices) {
        
        NSLog(@"Voice Name: %@, Identifier: %@, Quality: %ld", voice.name, voice.identifier, (long)voice.quality);

    }
}

Not all voices may be installed on all devices. For example, Alex is an optional high quality voice that the user needs to download first before he will show up in this array.

The quality parameter either returns 1 for standard/low-res, or 2 for enhanced/hi-res voices. Again it is up to the user to enable the hi-res quality of a voice under Settings.





How to test if an NSArray or NSSet contains an object

There’s a handy method in the NSArray class that lets us check if an array contains a particular object or not. The containsObject method returns a BOOL for us to evaluate.

Consider this:

// create an array
NSArray *array = @[@"Apples", @"Pears" , @"Bananas"];

// check if Pears are in the array
if ([array containsObject:@"Pears"]) {
    NSLog(@"We have Pears.");
}

Likewise, we can check if our array does not contain a particular object by testing the opposite:

// check if Grapes are not in the array
if (![array containsObject:@"Grapes"]) {
    NSLog(@"But we don't have Grapes.");
}

Both the NSMutableArray and NSSet classes has the same method, so we can check for the (un-)presence of our objects in mutable arrays and sets the same way.





What’s new in iOS 10.2

iOS 10.2 introduces new features including the TV app (US Only), a new and unified experience for accessing your TV shows and movies across multiple video apps.

Emoji have been beautifully redesigned to reveal even more detail and over 100 new emoji have been added including new faces, food, animals, sports, and professions.

This update also includes stability improvements and bug fixes. Continue reading





How to turn an ISO language code into the language identifier

Oftentimes we deal with cryptic language abbreviations, such as en-GB or de-DE. While it’s more or less clear to us humans what this means, it would be nice to have a way to display something more readable to our users.

We can do this with a method from the NSLocale object. Consider this snippet:

NSString *code = @"en-GB";
NSLocale *locale = [NSLocale currentLocale];
NSString *translation = [locale displayNameForKey:NSLocaleIdentifier value:code];

NSLog(@"%@ is %@", code, translation);

// output:
en-GB is English (United Kingdom)

Here we translate en-GB into “English (United Kingdom)”. First we take the current device’s locale, then we use the displayNameForKey method to turn the ISO language code into a human readable description.

The beauty is that no matter what language the device is set to, iOS will display the translation in the matching locale. For example, on a German device, en-GB would be translated into “English (Vereinigtes Königreich)”. Even though Germans would probably never refer to the UK as that. But I digress.

This also works with abbreviated language codes, such as “en” (instead of en-GB). In that case, the output is simply “English” without the country identifier.

We can also force iOS to display the translation in a locale of our choice. Rather than using the currently selected locale on the device, we can pick one and display the output accordingly:

NSString *code = @"en-GB";
NSLocale *locale = [NSLocale localeWithLocaleIdentifier:@"fr-FR"];
NSString *translation = [locale displayNameForKey:NSLocaleIdentifier value:code];

NSLog(@"%@ is %@", code, translation);

// output:
en-GB is anglais (Royaume-Uni)




How to explode an NSString into single NSString characters

Say you had an NSString, and you’re interested in each single character. You’d think there’s a method for it, such as getCharactersInRange. Turns out that’s not really helpful, because it delivers unichar characters rather than NSStrings.

To extract single characters as NSStrings, we can use the substringWithRange method like so:

NSString *phrase = @"Complex stuff.";

// split NSString into its characters
for (int i=0; i<phrase.length; i++) {
    NSRange range = NSMakeRange(i, 1);
    NSString *character = [phrase substringWithRange:range];
    NSLog(@"Character is %@", character);
}