Tuesday, November 17, 2015

Swift Optionals: A Brief Overview (Swift 2.1)

An Optional in Swift is a datatype that's not used by any other language, but serves a great purpose as it guards against null pointer exceptions.  This article explains what Optionals are, and how the can be used.

Primative datatypes (Ints, Strings, Floats, etc) can't store nil values, as is the same in any other object oriented programming language:

But in Swift, even object references can't store nil:

That's where Optionals are useful.  If you have a variable that could have a nil value, then it should be declared as an Optional.  You're giving it the option of either having a value, or having no value.

Declaring Optionals

For example, in some cases a method can't return a value.  Below, the Int() initializer could work for one string, s1, but wouldn't for the second string, s2:

as it's impossible to convert "abc" to an Integer.  Since y needs the option to have no value, we should declare it as an Optional.  We can do so by adding a "?" after the datatype declaration:

which removes the compiler errors.

Another example of when an Optional would be necessary are for properties that cannot be initialized when an object is constructed.  For example:

What's happening here?  Because we're adding the button property, and it's a regular Swift variable, and Swift variables can't store nil values, the compiler gives us the error message "Class ViewController has no initializers".   Again, by declaring button as an optional, by adding the "?" after UIButton, Swift then knows that it could store a nil value.

Using Optionals

Unfortunately, once you declare an Optional, you can't use it unless you find out what it is.  Does it have a nil value or not?  In order to find out, you need to unwrap it first.

If you don't unwrap it, and try to use it, you may see an error like this:

You could just follow the compiler's suggestion to add a "!" to unwrap y, but that does not safely unwrap the variable, and could lead to an app crash if the variable does have a nil value.  To safely unwrap the variable, we'll want to use an "if let" or "if var" statement.  In the example below, we'll be changing the value, so we'll want to use "if var":

We've safely unwrapped the variable, and can handle the case where the value is nil.  The only time you should unwrap using "!" is when you're 100% sure that the value will not be nil.  Otherwise your app with crash.

Optional Chaining

To access the properties of Optional object, you'd normally need to do something like this:

first unwrapping the image using the "if let" statement, then accessing the size property.  With Optional chaining, you're able to unwrap the image object using the "?" notation, and access the size property in one line:

As you can see, this is a slightly more efficient way to unwrap an object and access a property.

Monday, November 2, 2015

How to transition between sound effects seamlessly in Swift 2

As part of Udacity's iOS Developer NanoDegree program, we're creating an app called Pitch Perfect. This simple app allows a user to record audio, then play back the audio with different audio effects.

Two of the audio effects that are part of the app are playing audio fast, and playing audio slow. In order to do that, you'll need to add an audio file to your project to play.  You can do that by right clicking your project in the Navigator section of Xcode, then choose "Add files to <project name>...":

Ensure when you do so, that Add to Target is checked for your project:

Your file will appear in the Navigator area within your project, and you'll now be able to access it.  The file we're using in this example is called "movie_quote.mp3".

Next, you'll need to import the AVFoundation framework into your ViewController, so you can work with Audio or Video files:

Next we need to instantiate the AVAudioPlayer class.  You should do so as a global variable if you plan to access it in multiple functions (as we will do in this example):

Now, in the viewDidLoad() function, we can create the AVAudioPlayer object using the audio file we've added to our project.

The AVAudioPlayer method we use to create the object take two arguments:
1. contentOfURL, of type NSURL
2. fileTypeHint, of type optional String

For the contentofURL object, the AVAudioPlayer method is looking for a path to get to the audio file. To do so, we can use NSBundle.mainBundle().pathForResource to create a string path for the file:

Because the contentOfURL is looking for an NSURL type, you can use NSURL.fileURLWithPath to convert the string:

Lastly, we'll create the AVAudioPlayer object:

Now we have an AVAudioPlayer object is available.  To see what you can do with these objects, you can find the class reference here.  In our example, we'll explore:

- the play and stop methods
- the enableRate, rate, and currentTime attributes

In our ViewController, we've created a playback function, as well as button actions.  You can see in the screenshot below, we have two buttons, one for fast playback and one for slow.  

In the playback function, playAudio we start by stopping any current playback using the stop() method.  Next, we set the enableRate attribute to "true" so we can change the rate.  Then, we set the rate using rate attribute, passing in the rate argument from the function.  Finally, we play the audio.

You'll then notice in the @IBAction functions, we're calling the playAudio function, and passing in a value to either speed up, or slow down the playback speed.

The code above allows for a seamless transition between sounds effects.  You can tap the slow button and the audio will play slowly.  At any time, you can tap the fast button, and the audio will immediately speed up.

If we wanted to change that, and have the audio restart each time a button is clicked, you can do so by setting the currentTime attribute to 0.0 to ensure the audio starts from the beginning:

That's it - you can now choose to seamlessly transition between audio effects, or restart audio each time a new button is clicked.  For more information on these topics, go here: