Tuesday, August 23, 2016

Popular Movies App: A Technical Look

After several weeks and many hours of work, it's complete: my first Android app for the Udacity Nanodegree. It's been a steep learning curve coming from iOS with no prior Java knowledge, but the project's challenges have made its completion even more rewarding.

As much as I enjoy the accomplishment, the journey has been the most important part. From reading the specification to receiving the code review, the last two months have been a great experience. The finished project isn't just a completed app, but a culmination of technical skills that demonstrate the Android framework, and what its developers, can do. This post showcases a few of the more interesting aspects of the Popular Movies app, how I chose to implement them, and the challenges faced along the way. 





The User Interface

According to the specification, the project displays a grid of posters for movies that are currently popular or highly rated among viewers. Tapping on a movie shows a screen that includes details such as release data, average rating, trailers, and reviews. There's also an option to mark a movie as a "favorite" for offline viewing.


When looking for movies, people are generally searching for a specific type, so in addition to the options to view "popular" and "top rated" movies, I also allow users to filter by genre. The number of available genres wouldn't be suitable for a tab-based layout or a spinner, so the filtering options are shown in a navigation drawer. 


Genres can be selected from the navigation drawer.

The actual movies themselves were displayed without borders, in a GridView. The image thumbnails also have text containing the movie's title. 


The details page is where things get interesting. It primarily consists of LinearLayouts and is designed for vertical scrolling. For version 2 of the project, I was required to display trailers and reviews for the selected movie. Although the built-in views are suitable for displaying this data, reusing multiple views would make the coding process much easier. For this reason, I created a compound view by subclassing view groups. Doing so allowed the reuse of multiple views, simplifying the code, without compromising the visual appearance. 



Custom views for trailers and reviews.

The Data Model


Data for the app is supplied by The Movie Database API. While I initially used standard HttpUrlConnections for version 1 of the project, part 2 demanded more requests than simply fetching movies. The first code review suggested I try the Retrofit library to help with networking, but because of its simplicity, I opted to use Volley for this project instead.

The data model was another important aspect of the user experience. By tapping the "favorite" button, movies would be saved to the device storage for offline viewing.

Although the requirements only asked for the movie data to be stored, I thought it would be the best experience to also persist trailers and reviews. That way, this information would still be available if the connection was offline, since no additional downloads would be required.

Each movie can have multiple trailers and multiple reviews, so this task is best solved with a relational database. Knowing the difficulty of working with raw SQLite, I decided to implement the data model using GreenDAO. The library had a few nuances making it more difficult to work with than my previous experience with Apple's Core Data, but it did its job well. I'm glad I gave it a try, though I intend to try out other ORM solutions before recommending its use.

The Challenges


Tackling a project of this scale is a challenge of its own, but there were a few specific challenges that I faced when building the app.

Because of the way Android manages the lifecycle of activities and fragments, they can be destroyed and recreated several times during the lifetime of an app. Events like rotating the device or moving an app to the background can cause these changes in the activity's lifecycle. As a result, it's important for developers to save and restore the UI state.

Doing so is a straightforward process but it's important to get it right. Because the app makes use of custom views, restoring these views' state is an added challenge. Saving and restoring each fragment can be tedious at times, but it's definitely worth it to provide the best user experience.

Another consideration in producing a quality app is handling edge cases. Because I wanted to persist movies and trailers along with the movie data, the potential for additional bugs was introduced. For example, a user could mark a movie as a "favorite" before the movies and trailers are downloaded. If the network were to go down before these downloads complete, the app would need to remember to download this data next time the network is available. These scenarios don't always happen, but when they do, a high quality app should handle them accordingly.

A final challenge involved adapting the UI for tablets with the master-detail flow. The two pane layout works well in landscape but most tablets in portrait mode don't have enough screen space. For the alternate, portrait mode layout, I took a less conventional approach, displaying the detail view as a popover. Perhaps it won't be the next UI design craze, though I still think it's an interesting solution to for two-pane layouts in portrait mode. Implementing it this way, the detail fragment can be reused across orientation changes, since it won't be tied to a separate activity like in the phone layout. 



The details page on a tablet in portrait mode.

Overall it was an incredible learning experience and the end product turned out great. In just 2 months, the great folks at Udacity took me from no Java knowledge to building a polished Android app. It's been an excellent journey so far and I can't wait to see what comes next.

Tuesday, August 16, 2016

Migrating a Core Data Model in One Minute

Database migrations can be a scary subject to beginners, especially if user data is on the line. While developing an app, you're probably used to resetting the simulator and reinstalling whenever you need to change the data model. Unfortunately, this quick-and-dirty solution won't cut it for a production app. You'll inevitably need to migrate the data the "right" way in order to keep your users happy. Fortunately, Apple once again succeeds in simplifying even the most daunting tasks, and its built right in to the Core Data framework. The best part is that it only takes about a minute.

To start, you can create a new project with the master-detail template with "Use Core Data" checked. Or you can download a pre-made template here. The examples in this tutorial use Swift.

To begin, open up your .xcdatamodeld file and select "Editor > Add Model Version" from the menu. There should be two fields (one with "Version Name" filled in and a dropdown titles "Based on Model". This second field should point to the latest model version. The name "Master_Detail 2" will work, so just click "Finish".


The .xcdatamodeld file should now look like a folder, and the new model should be inside.


For demonstrational purposes, we'll just add a new property to version 2 of the model.


That's all for the changes. All we have to do now is tell our app which model version to use. With the .xcdatamodeld file still selected in the left pane, head over to the file inspector and make sure Current is set to "Master_Detail_2" under the Model Version section.


Finally, since this is the first migration, we need to modify some code in order for everything to work. In the lazy variable "persistentStoreCoordinator" found in the AppDelegate, modify the end of the getter to look like this.


The dictionary specifies the necessary properties to automatically migrate the database. We then pass this as the "options" parameter when we call addPersistentStoreWithType(). For the full list of available options, you can refer to the documentation for NSPersistentStoreCoordinator.

And we're done. To recap, here's what we did.
  1. Created a new model version from the model editor. 
  2. Edited the schema by adding a new attribute. 
  3. Set the new model as the current version. 
  4. Configured our persistent store coordinator to automatically migrate the database. 
Ok, maybe that took two minutes (feel free to post your time in the comments), but it's definitely a straightforward process to do. There are situations where this lightweight migration method won't be enough, but it's a simple way to make minor changes to the data model. No more reinstalling and losing data: just happy users.

Tuesday, August 9, 2016

Solving the Android findViewById() Problem

One of the first things any Android developer learns is working with layouts. These files, defined in XML, are separate from the Java classes and specify the basis for an app's interactive components. When making the UI interactive, it's necessary to interface between these two files, accessing XML defined views from Java code. To achieve this, the Android SDK uses the concept of IDs.

IDs are XML properties that provide elements with unique identifiers. Each one is stored as a resource to be accessed by other files in the project. In our Java classes, once the view has been inflated, we can access subviews by calling findViewById(). For example, if we wanted a text field to say "Hello, world!", we could do this.


Seems straightforward enough, but if we want to set the text from many different places in the code, typing this would get tedious. It would be a better idea if we could get a reference to the text field once, and then refer to that name later.

Now the text can be set from anywhere, just by referencing the textView property. The view's id is only used once, in onCreate().

With adapters, another common approach to reduce verbosity is the View Holder. Similar to storing global variables, it allows you to reference views by a custom name, instead of an ID. The "holder" is a static class that keeps a reference to all the views, and is useful when working with lists of data, where each view is the same.
With both techniques, referencing views from Java classes is much easier. But after submitting my first project for the Android Developer Nanodegree, the reviewer suggested a different approach. By using the ButterKnife library, findViewById() can be eliminated from the code entirely. Using a simple annotation, view properties can be associated with an ID in one line. The annotations even work with existing view holders, and can be used to bind methods to buttons.
And for the ViewHolder class. Adding additional views only requires another declaration with @BindView.

While you still need to type the view's ID in the Java class, ButterKnife's annotations make the process simple, and require as little code as possible. It's not quite as magical as the @IBOutlet annotations in iOS, which assign IDs behind the scenes, but the result is the same: more readable, more maintainable code.

There are libraries other than ButterKnife, such as Dagger, that allow injection of more than just views. Advanced users may benefit from this additional functionality. But regardless of your skill level, if you're not yet using an injection library, I would highly recommend you give one a try. It will save you a lot of time and make your code much easier to maintain.


Tuesday, August 2, 2016

5 Things Udacity Students Taught Me About iOS Development

"You can't learn something until you teach it." While the saying is not always true, there are some things you need to teach in order to learn well. When I was learning iOS development, I was fortunate to get the experience to teach other Udacity students. As they learned from me, I also learned many thingsfrom them. This article showcases five of these previously overlooked concepts that I learned thanks to the help of other students.

1. Completion Handler Pattern

When I first learned about making network requests, it became clear early on that keeping networking code inside the view controller was not ideal. It was not only unorganized and unmaintainable, but violated the basic tenets of MVC architecture. When dealing with multiple network requests, abstracting this code to a separate class is a must.

Doing so introduces a problem: how can the networking code communicate with the view controller? While quite laughable looking back, my inexperienced mind found the perfect solution: NSNotificationCenter.

It actually worked quite well at keeping the view controllers small and separate from the networking code, but there was no real way to pass multiple values (data, response codes, error messages) back to the view controller. It was only when I started teaching other students that I saw prevalent use closures to implement the completion handler pattern. This approach was far more elegant than NSNotificationCenter and made network requests much more straightforward. Since then, I've used the completion handle pattern in all my networking code and have gotten the opportunity to explain its advantages to countless students. It can be a difficult concept to learn and beginners may not immediately see the purpose, but mastering it has proved very useful.

2. The Singleton Pattern

Like completion handlers, singletons are another one of those "tricks" that can be befuddling to beginners. While virtually every iOS developer has experience working with the AppDelegate or NSUserDefaults, the idea of creating your own singleton class may not seem intuitive. They can be used for a variety of purposes, but data storage and networking classes are the most common. It was reviewing students' networking code in particular where I first saw use of the singleton. Although many consider it to be an "anti pattern", its widespread usage in Apple's own code earns the singleton a mention here, and it's something any intermediate developer should be aware of.

3. Making Collection Views Look Pretty

If you've ever had to display multiple images in a grid format, you've probably been using a collection view. Setting one up is simple: all you need is to create a subclass of UICollectionViewController, create a cell, implement the data source, and you're done. While the images should now be displayed, the implementation is not quite complete. Just try running the app on different simulators and you'll notice that that the spacing can vary considerably.

Not only is the spacing off, but we have no control over the cell size.
This is not the perfect grid seen in the built-in Photos app, but it turns out that Apple lets us do the same thing with UICollectionViewFlowLayout. I overlooked flow layouts when learning about collection views, but once again, it was fellow students who exposed me to this UX lifesaver.

Much better. The cell size and spacing is under control.
4. Xcode Time Profiler

While discussing with a student the pros and cons of different algorithms for a certain task (because that's what us nerds do), we inevitably got onto the topic of Big-O and algorithm running time. After I explained how one approach had a better best-case scenario, he mentioned there was run-time testing within Xcode. It doesn't appear to actually determine the Big-O time, but it does provide a lot of useful metrics.

It's definitely something to cover in a full-length tutorial in the future, but for now, you can feast your eyes on the glory. Looks fancy, eh?
A screenshot of Xcode's Time Profiler
5. The System Isn't Always Right

For continuity, Udacity project reviewers can communicate with each other to discuss best practices and recommendations. Early on, another reviewer's suggestion stood out as especially useful. According to them, custom keyboards can sometimes incorrectly report the UIKeyboardWillShowNotification, causing the callback to be invoked multiple times. For this reason, they discouraged usage of the += operator to adjust a view's coordinates, since doing so would cause the view to move further than intended.


In the vast reaches of the iOS SDK, this bug may not sound significant, but it can potentially wreak havoc on one's app if left unhandled by the programmer. I now point it out to any student who moves views in this way, so they can not only be aware of this particular bug, but the reality that even Apple's code isn't perfect, and sometimes us developers need to find workarounds.

Conclusion

That wraps up the 5 things Udacity students taught me about iOSdevelopment. It's just a sampling of the most significant topics, but each one was a great learning experience, both as the teacher and the student. On an added note, if you're learning iOS development and are interested in these topics, they're certainly worth further research. I wouldn't want anyone using NSNotifications as a substitute for completion handlers.