By Kevin McNeish on Thu, 09/05/2013
Do you have an idea for an app but lack the programming knowledge to begin building it? In this weekly blog series, I will take you, the non-programmer, step by step through the process of creating apps for the iPhone, iPod touch, and iPad. Join me each week on this adventure, and you will experience how much fun turning your ideas into reality can be! This is Part 22 of the series. If you are just getting started, check out the beginning of the series here.
In the last few posts in this series, you have learned a lot about retrieving entities from a database using Core Data, and displaying the results in a table view. Now that we have we have all the other functionality working in the Write Review scene of iAppsReview, it's time to learn how to save a ReviewEntity using Core Data!
In order to work through the steps in this post, you can use the iAppsReview project you have created so far. If you need a fresh copy of the project, you can download it at this link.
As I have mentioned in the past, saving entities to a database is easy to do—especially when you are using the mmBusinessController wrapper class. Let's take a closer look at this class to learn more about how entities are saved.
- Open the iAppsReview project in Xcode.
- Go to the Project Navigator, expand the Business Layer group, and then click on the mmBusinessObject.m file to open it in the Code Editor.
- Let's find the saveEntities method. When you have a large code file with many methods, the easiest way to do this is to use the jump bar at the top of the Code Editor. As shown in Figure 1, click the very last section of the jump bar.
|Figure 1 - Click the last section of the jump bar.|
- In the popup list that appears when you click the jump bar, select saveEntities as shown in Figure 2.
|Figure 2 - The jump bar popup list|
Selecting this method in the popup list takes you to the place in the code file where the method is located as shown in Figure 3.
|Figure 3 - The saveEntities method|
Let's take a closer look at this code. The first line of code declares a local variable of type NSError named error. You will see how this is used in just a bit.
The next line of code gets a reference to the business controller's object context. As you learned in a previous post, the object context keeps track of all entities that are retrieved from the database as well as any newly created entities. It tracks all changes that are made to these entities, and can save all changes to entities in the database on an iOS device.
The next line of code contains an if statement that makes sure the object context is not nil. If it's not, the nested if statement executes. This nested statement is an important one, so I've isolated it in Figure 4.
|Figure 4 - The main messages passed to the object context to save entities|
This code first sends a hasChanges message to the object context. If this message call returns NO, the save is aborted (because in Objective-C, the && operator doesn't evaluate the right-hand condition if the left-hand condition is false.)
If the hasChanges message call returns YES, then a save: message is sent to the object context. This causes the object context to save changes to all new and edited entity objects.
As an aside, you may be wondering about the ampersand (&) in front of the error parameter. Normally in Objective-C, the value of an argument is passed in a message. However, if you preface the argument with an ampersand, a pointer to the argument is passed. It's passed this way so that the save: method can modify the value of the error argument and the new value is available to the calling method.
If all goes well and the save: message call returns YES, the saveEntities method finishes execution without running any further code. However, if the message call returns NO, the NSLog() statement executes; which displays details of the error in Xcode's console, and afterwards calls the abort() function. As mentioned in the comments, this code is important to have in place when you are in the process of building and debugging your project; so that if an error occurs, you will know what caused it. We'll talk about the error handling code you can use for a production app in a future post.
Creating a New Entity
In the Write Review scene of the iAppsReview app, we have to perform the following steps when the user clicks the Post button:
- Create a new ReviewEntity object.
- Get the values from the user interface controls 2 store them in the properties of the ReviewEntity object.
- Save the ReviewEntity object.
- Post the review on the web (we'll get to this later in this series).
The mmBusinessObject class has a createEntity method that we can use to create a new ReviewEntity object. Let's take a look a behind-the-scenes look at this method.
Click the far right segment in the Code Editor jump bar, and select createEntity from the list (Figure 5).
|Figure 5 - Select the createEntity method.|
You should now see the createEntity method code shown in Figure 6 (I have broken the code into three separate lines to make it easier to read.)
|Figure 6 - The createEntity method code|
The code in this method passes an insertNewObjectForEntityForName:inManagedObjectContext: message to the NSEntityDescription class. This method accepts two parameters. The first is the name of the entity class (we just grab this off of the business controller object) and a reference to the object context (which we also grab from the business controller.) An object of type NSManagedObject is returned from this message call, and it is simply passed back from the createEntity method.
In object-oriented terminology, we call this a factory method because it manufactures, or creates objects.
The code in this method is standard, boiler-plate Core Data code. I want to impress this fact on you so that you know that when you use the mmBusinessObject class, you are really using standard Core Data code.
Implementing the New and Save Code
Now that you understand the basics of how to create and save entities, let's implement this code in the iAppsReview project.
- In the Project Navigator, select the WriteReviewViewController.m file to display it in the Code Editor.
- Use the Code Editor jump bar to select the postReview: method.
- You should see the postReview: method code shown in Figure 7.
|Figure 7 - The postReview: method code|
As it turns out, I have already implemented the necessary code for you! We tweaked this code in my previous post without taking too close a look at it.
The first line of code creates a new ReviewEntity object. The next several lines of code grab values from the various user interface controls and store then in the properties of the ReviewEntity object. Next, a saveEntities message is sent to the Review business controller, which saves the new entity in the database. Finally, a popViewControllerAnimated: message is passed to the navigation controller, which causes the app to navigate back to the main iAppsReview scene.
Testing the Code
Now it's time to take the code for a test drive.
- In Xcode, click the Run button.
- When the app appears in the Simulator, click the Write a Review option.
- In the Write Review scene, select an App Category and rating, and enter an App Name, and comments. When you do this, the Post button should become active as shown in the example in Figure 8. Remember that you can click the background of the scene to hide the keyboard!
|Fig 8 - The Post button is enabled.|
- Click the Post button. The code in the postReview: method executes as evidenced by the fact that the app navigates to the main iAppsReview scene.
So how do you know if the review was saved to the database?
Your Homework Assignment
My goal with this blog series is to teach you to write apps on your own. A real test of your ability is to write some code without training wheels. With that in mind, I have a homework assignment for you.
The My Reviews scene in iAppsReview is intended to display the reviews you have saved on your iOS device. Currently, this scene is still a prototype, displaying static review information. Your assignment is to convert My Reviews to a fully functioning scene that retrieves reviews from the database and displays them in a table view. Everything you need to know about retrieving and displaying entities in a table view can be found in this post.
After you are finished with the My Reviews scene, I'd like you to move on to the Review scene. This scene is displayed when you tap a row in the My Reviews table view and displays the details of the review. To get this to work, you are going to have to pass a ReviewEntity object from the My Reviews scene to the Review scene. Everything you need to know about passing data between view controllers can be found in this post.
You can also look at the code in the WriteReviewViewController and AppCategoryViewController code files for hints on how to complete these tasks.
In my next post I'll provide the solution to this post's homework. You will find that working through these problems on your own will help you learn these fundamentals of iOS programming, and show you what you truly understand and where you need a little more help. Happy coding!