Select snippet type

Our next story is "I want to select the type of snippet I am creating". The tasks are:

  • Append the SnippetData model to allow for Text and Photo types
  • Create an alert controller that allows the user to select the type of snippet that is created

For this feature, we're going to expand on our SnippetData model to include the ability to have a type using an enum. Then we're going to create and present an alert controller to the user that allows them to choose a type. Finally, we're going to update our view controller to respond to the different options the user can select, and create the correct type of data.

Update SnippetData model

Current task: Append the SnippetData model to allow for Text and Photo types.

When we created our SnippetData model, it was a very simple Swift struct that didn't hold any data. Now we want to add a type property to the struct so that it knows what kind of data it is holding. We're going to create an enum to describe the possible types that can exist for our SnippetData, and use a String as a backing type for our enum. This means that our enum will be built on top of String values. Let's add this enum to our SnippetData.swift file:

enum SnippetType: String {
    case text = "Text"
    case photo = "Photo"
}

At the top, we declared a new enum called SnippetType, and then said that it is built on top of the String class. This syntax is similar to the way we use inheritance in Swift, which makes sense, since, in a way, we are inheriting and writing on top of a string. Inside the enum, we define two possible cases, Text and Photo. We then assign a String value to our cases, which is the backing (or raw) value of that case.

Then, inside our SnippetData struct, we add a new property to hold our new SnippetType information, and update the initializer to take an argument for the type:

struct SnippetData {
   
    let type: SnippetType
   
    init ( snippetType: SnippetType ) {
        type = snippetType
        print ("(type.rawValue) snippet created")
    }
}

At the top of our struct, we declare type with the let keyword, which means it is a constant. This makes sense because, once we initialize a snippet, its type should never change. Then, in our init function, we updated it to accept a parameter for our SnippetType, and then we assign the parameter value to our type constant.

Finally, you'll see that we changed our print() function. We replaced the word new with the expression (type.rawValue). If you remember from earlier, the () syntax allows us to splice data values into a string. In this case, we are inserting the rawValue property of our type enum. Remember how, when we created our SnippetType enum, we assigned each case a backing value? That is the rawValue we're using here. So if we create a new SnippetData with the type SnippetType.Text, then our type.rawValue will equal the Text string we assigned in our enum, and it will print out Text snippet created when it is initialized.

Now our SnippetData model has the capability to support multiple types. Let's enable our user to select which type they want to make.

Create an alert controller

Current task: Create an alert controller that allows the user to select the type of snippet.

In our basic pre-visualization of the app, we saw that we were using an action sheet with a few options that let the user select the type of snippet they were creating. Now we're going to walk through the process of creating an action sheet like that using the UIAlertController.

Since we want to give the user this option after pressing the new button, we're going to delete the code we have in our createNewSnippet() function, and replace it with some new code that presents an action sheet. Let's take a look:

@IBAction func createNewSnippet(_ sender: AnyObject) {
    
    let alert = UIAlertController(title: "Select a snippet type", message: nil, preferredStyle: .actionSheet)
    let textAction = UIAlertAction(title: "Text", style: .default) { (alert: UIAlertAction!) -> Void in
        self.data.append(SnippetData(snippetType: .text))
    }
    let photoAction = UIAlertAction(title: "Photo", style: .default) { (alert: UIAlertAction!) -> Void in
        self.data.append(SnippetData(snippetType: .photo))
    }
    let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
    
    alert.addAction(textAction)
    alert.addAction(photoAction)
    alert.addAction(cancelAction)
    present(alert, animated: true, completion:nil)
}

First, we create a new UIAlertController, and initialize it with a title and a preferred style of .actionSheet. The title property is what goes at the top of the action sheet and describes what the alert is for, while the message property is for giving more detail. Since our alert is pretty simple, we don't need both, so we set the message to nil.

Note

UIAlertController is also used to show you alerts in the middle of the screen, so if you set the preferred style to .alert, it will take that form. Try it out!

Next, we create new UIAlertActions. These are going to be the actual options that the user can select from the action sheet. The first two UIAlertActions are very similar, since they are both used to create a new Text snippet and Photo snippet, respectively.

When creating a UIAlertAction, we need to pass in three things: a title, a button style, and a completion handler. The title is the text on the button, the style is the formatting of the button, and the completion handler is the code that runs when the button is pressed.

let textAction = UIAlertAction(title: "Text", style: .default) { (alert: UIAlertAction!) -> Void in
    self.data.append(SnippetData(snippetType: .text))
}

For our text/photo actions, we pass in the title, set the style to .default, and then use what's called a trailing closure tacked on to the end. Inside the completion handler closure are two parts: the definition of the parameters being passed into the closure, and the body of the closure.

The first part is the (alert: UIAlertAction!) -> Void in line, which tells the closure that we are passing in a parameter named alert that is the type of an explicitly unwrapped optional UIAlertAction, and returns Void (nothing).

In the body of our completion handler we have the following line:

self.data.append(SnippetData(snippetType: .text))

Here we are initializing and appending a new SnippetData struct to our data array. We use the new initializer we wrote for our SnippetData struct, and let it know that its snippet type is .text. In our photo action completion handler, we'd write .photo.

Note

Also notice that we have to write self. at the beginning of the line. In a closure, we need to be explicit about the scope, so by letting the closure know exactly what we're talking about with self.data, it can capture the scope of the function and use it later on whenever the completion handler is run.

Further down, you'll see our cancelAction is a little different. Since our cancel button doesn't really do anything, we can pass in nil for the completion handler, instead of using a trailing closure like the other actions.

Finally, we add all of our UIAlertActions to our UIAlertController by writing alert.addAction( UIAlertAction ) for each action. Then we present our UIActionController like this:

presentViewController(alert, animated: true, completion:nil)

This presents our alert controller to the user.

And that's it! Build and run the project on the simulator, and try out our new type selection. In the debug area, you'll see that the console now says Text snippet created and Photo snippet created, depending on which button you press. Before we move on to the next user story, remember to commit your changes to the local Git repository now that we've finished a story and the project is stable.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.19.211.134