The Quadrangle for iOS

Hello, World!

I’m Evan, co-editor-in-chief and webmaster of The Quadrangle, and I’m thrilled to announce the release of our first native mobile app for iOS. If you haven’t already, I urge you to download it from the App Store. It’s free!

The Quadrangle Mobile Screenshots

With this post, I intend to present a rough outline of the process of building a news app for iOS. My hope is to demystify the admittedly daunting task of building an app like this and provide the tech-savvy reader with a basis for building her own.

WARNING: This post will not be funny. Not, you know, intentionally, anyway. For the sake of brevity, I am going to assume a beginner’s knowledge of the basics of programming for iOS. If you don’t have that, then this might be a little boring. Of course, feel free to read through anyway and pretend like you understand!

The Quadrangle mobile app does the basic things any news app should: it downloads the latest stories, presents them in a mobile-friendly format, allows them to be viewed by category (and tag), and allows them to be easily searched. With the exception of a few little ornaments, the app doesn’t do a whole lot more. It is not meant to be an incredible tool or a fun game, but rather an engaging and seamless way to read The Quadrangle on an iOS device.The main tab bar (Storyboard)

The entry point to the app is a standard UITabBarController, which I’ve subclassed to introduce custom transitions (courtesy of VCTransitionsLibrary) and some simple deep linking (for when the app is opened from a push notification or a smart banner, and it is appropriate to load a specific article).

That view controller has five child view controllers which each represent a tab: FeaturedCategoriesTagsSearch, and More. The root view controller for each tab is embedded in a UINavigationController to allow for the forward/backward navigation that has become standard on iOS.

Featured

The first tab is for the latest featured articles–those are articles with the hidden “featured” tag–and these stories are the ones that are placed in the rotating carousel on the main page of the website. Now we’ve arrived at the first technical challenge of the app: how do we download the featured articles to the device? This is a complicated question with several parts, and requires a bit of background information about the site.

The Quadrangle is built on WordPress and hosted via a private hosting company’s servers. When we publish an article via the WordPress backend, it goes into a database on the server. Luckily for us, WordPress exposes a RESTful API that can be installed via a plugin on the website. This means that the app can make HTTP requests and receive data containing the articles. This data comes back as one or more JSON objects containing the requested articles as well as their metadata. There are all kinds of routes for requesting the latest posts, posts with a certain tag or category, posts matching a search term, and also retrieving lists of categories and tags.

That’s the high-level overview of how we get data from the site. On the app itself, I’ve created a class to handle Network Requests. Although there are lots of third-party libraries that try to simplify the process of dealing with RESTful APIs, in simple use cases like The Quadrangle I prefer to use Apple’s first-party methods and classes, which already have several layers of abstraction built in. Requests are made in the following manner:

The url is passed in based on what kind of request we’re making (i.e. featured posts or posts in the Opinion category) and, if the request is successful, data contains our JSON object(s). To read the NSData blob, I like to call NSJSONSerialization‘s JSONObjectWithData method to get either an NSArray or NSDictionary, depending on the data. (Note that this is context specific, so, at a certain level, the requesting methods must have an idea of the kind of data they expect to receive.)

When dealing with objects in memory, I like to put all my data into custom classes (rather than keeping them as key-value pairs in an NSDictionary), so I have a class called Story which exposes a custom initializer initWithDictionary:(NSDictionary *)dictionary which retrieves values in the dictionary by key and stores them as properties on itself.

As per good MVC design principles, all this data retrieval logic is contained within a set of dedicated classes and is accessible by the view controller via a manager singleton. This singleton exposes methods which take a callback block as argument, and call it on completion with either the requested data (generally an NSArray of custom objects) or an NSError. This design patterns allow for improvements to efficiency and data caching without having to change any of the front-end code.

The featured tab (Storyboard)

Speaking of front-end, the Featured tab’s root view controller is a subclassed UITableViewController which displays a list of articles, handles the standard pull-to-refresh gesture, and handles navigation for when a user taps on a specific article they’d like to read. When new articles need to be displayed (i.e. the page has just loaded for the first time, the user has pulled to refresh, or the user has scrolled to the bottom and we need to load older articles), the view controller makes a call to the content manager and passes in a completion block. When this block returns with the data, we update the table view’s data source and call reloadData on the UITableView. IMPORTANT: Because, in asking for new articles, we made a network request (unless the data was cached, but our front-end makes no assumptions about this), it is likely that the completion block will not be called on the main thread. This is bad, because reloadData triggers an update to the UI, and UI updates should all happen on the main thread. (If they don’t, weird things can happen, and weird things almost always means a bad user experience.) To get around this, I do the following:

This ensures that the UI updates happen on the main thread, and we avoid weird bugs.

Okay, so that’s how we refresh the list of articles, but how do we actually display them? We don’t want to display the entire text of the article at first; there’s not enough room on the screen and we want users to be able to scroll through and decide which they’d like to read. We do want to show them a few things, however: the headline, the image, the date the article was published, and a brief excerpt. To have full control over the layout of these elements, I decided to subclass UITableViewCell. (NOTE: There are other ways of customizing table view cells without subclassing, such as using different prototype cells in the storyboard or accessing subviews via tags. Different people prescribe different methods, but I generally prefer to avoid Interface Builder whenever possible.)

Anyone who has worked with UITableView before is familiar with the following method:

This code retrieves the reusable cell object from the table view, gets the corresponding story from the list of stories, and then populates the cell with information from the Story object. The custom table view cell handles the layout and display of that information.

Okay, so that’s not too hard. There’s a big problem though, which I forgot (or, you know, deliberately failed) to mention: the JSON object we downloaded earlier does not contain an image for the article. Instead, it contains a URL at which we can find it. This means that we have to wait until we’ve downloaded the post to find out where its image lives, and then we have to go and fetch it. There are two ways we can go about this: proactively and lazily. In the former method, we go and retrieve the image for each article we download so we have it ready. Here’s the thing: text is cheap, but images are not. Downloading a big batch of articles uses a lot less data than a big batch of images. To spare our users the strain on their data connections (and, unless they have unlimited plans, their wallets), we opt for the latter method: laziness. This means that we only download (and cache!) an image when we know the user is going to see it.

Loading animation

To handle the asynchronous download and display of images, I decided to subclass UIImageView. (There are third-party libraries out there which do this, but the ones I tried were too buggy for my liking.) Each custom image view is instantiated with a story. If we’ve already downloaded the story’s image, we just call setImage and display it. Easy. But, if we haven’t, then we need to go and fetch the image at the URL we’ve been given. In the meantime, we display a little loading animation. Once the network request returns, we tear down the animation and display the image. BUT– there is one important caveat here, which can be very frustrating until we know why it is happening. (Trust me, I know from experience.) Since UITableViewCells are reused, there is an edge case where, by the time the network request returns with our image, that same custom table view cell object is being used for a different article. We need to check for this case by keeping a reference to the story whose image we went to fetch and checking it against the story whose image we’re displaying when the request returns. If the cell now corresponds to a different article, we pocket the image and don’t display it.

Okay! Now that we’ve downloaded the list of featured articles and displayed them to the user, we want to create a view so that they can read the full text of an article. We want to transition to this view when a user taps on an article in the table view. UITableView and UINavigationController make this easy. The following method is called on the table view’s delegate when the user taps a cell:

I mentioned that I like to avoid interface builder whenever possible. I make an exception for laying out the flow of an app using Storyboard, because I like to get a visual representation of what the app looks like as a whole. Storyboard also makes it easy to navigate between screens of content. (There are ways to do all this programmatically, but with storyboards, the process of instantiating and transitioning to and from views is taken care of for you.) In my storyboard, I have created a transition between my featured stories table view controller and a new view controller that will display a single story. Before we transition to the detail view, however, we need to tell it which story it will be displaying:

Because articles The Quadrangle publishes have lots of rich text (italics, bold, underlines, lists, headings, etc), I decided to render the full text for the article (with HTML tags included) in a WKWebView. What’s nice about this solution is that we get all the power of a webpage but, since we’ve already downloaded the content, it displays immediately. To create a great reading experience–designed specifically for iOS–and to use The Quadrangle‘s custom fonts, we can add a <style> tag to the HTML string with our CSS inside. Custom fonts can be imported using the CSS @font-face with a base-64 encoding, as suggested by this StackOverflow post. The following method converts a file on disk into a base-64 string:

Share!We can also add in custom HTML to display the image, the headline, and the date in a way that looks great on a mobile device’s screen. We do this by wrapping these elements inside HTML strings, appending them to an NSMutableString, and then loading it into the web view by calling loadHTMLString.

Now that we’ve rendered the article on the screen for the user to read, after they’re finished we’d like them to share it! As suggested in this StackOverflow answer (StackOverflow, by the way, is an invaluable tool for developers old and new), we detect if the user has scrolled to the bottom of the screen with the following UIScrollViewDelegate method:

If the user taps the share button, we present them with a UIActivityViewController, pre-populated with a link to the article:

Now, the user can browse a list of featured articles, tap the one they want to read, and share it with friends!

Categories

Now on to the second tab. Don’t worry, now that we’ve done most of the work for downloading and displaying articles, the remaining tabs will be much easier!

Categories

For this tab, we want to display to the user a list of the different categories for which we have articles. We can get this from the WordPress API. Just like with articles, I opted to create a custom class to hold the data for each category. This class is much smaller, however, since there’s much less data.

Once we’ve fetched our categories, it’s fairly trivial to display them in a table view. (In this instance, I opted not to subclass UITableViewCell and instead just use the basic cell, since we only need to display one string.) We can listen for when the user taps on a cell, just like we did with the featured stories table view, to transition to a view that displays a list of all the stories in that category.

Wait a minute! A list of stories, we’ve already made that! With a few modifications and abstractions, we can reuse the same custom UITableViewController class that we used for featured articles to display the articles in a given category. Instead of asking the WordPress API for stories with the “featured” tag, we can ask it for all stories in the category that’s been selected. We feed these stories to the data source and then everything works like before. Perfect!

Tags

From a programmer’s perspective, tags are just like categories. We can query the API for a list of tags, and then we can query it again for all the stories matching a given tag. The difference, however, is that, while there are less than ten categories, there are hundreds of tags. We don’t want to just dump a list of hundreds of tags in front of the user; that’s just not good design. But how do we display this information in a way that’s both compelling, manageable, and, dare I say, fun? To solve this problem, I used RTagCloudView.

TagsThis small library contains a single UIView subclass which gets assigned a delegate and a data source. The data source provides the names of tags, their fonts, and their colors, and RTagCloudView builds an interactive sphere. We can change the font of each tag based on the number of articles which have it (a property given to us by the WordPress API) to make frequently used tags more prominent. A delegate callback (similar to UITableView) tells us when a tag has been tapped, and from there we can show the user a list of articles with the selected tag.

Ah! A list of articles! We’ve already built it! Rather than an array of articles corresponding to a category, we pass in an array of articles corresponding to a tag and everything works like before, again. Perfect! Again!

NOTE: I modified the input system for the tag cloud to detect velocity of touches, so the effect you see in The Quadrangle app is slightly different from the library as is.

Search

Next up: search. There are many, many ways to do search in an app like this. With the relatively small volume of content The Quadrangle has produced so far, it would probably not be out of the question to do an on-device search, but this presents a few problems. First of all, to do a search of the entire database would require first downloading all the articles, which, while not an unreasonable task right now, is not scalable. Second of all, it would require implementing a search algorithm that could run efficiently on a mobile device. All things considered, I’d rather not.

Luckily for us, WordPress has a built in search engine! Unluckily for us, it’s rather bad. It does not sort results by relevance, its weighting algorithm is not transparent, and it lacks customization options for filtering results. That’s why, for search on The Quadrangle‘s site, I use the plugin Relevanssi. Without going into too much detail, it’s just… better. The trouble with Relevanssi, however, is that it does not play nicely with the WordPress API (yet). With help from the Github forums, however, I found a way to basically forward the search results to the API. This involved some server side php, and is beyond the scope of this already lengthy post.

At this point, you can probably guess how the search tab comes together in the app. A user enters a search term into the search bar, we ask the WordPress API for the results, and we display a list of articles. A LIST OF ARTICLES. We’ve already built it! All we have to do here is, if the user is in the search tab (which we can determine through the UITabBarController subclass and its selectedIndex property), add a UISearchController to the table view:

As the user types, we send requests to the API and display the results. IMPORTANT: We can run into a similar problem here as with displaying images: what if the network request returns the results for a partial search term but the user has already typed past it? The solution is fairly similar: we can keep track of the search term the user asked for and the one currently in the search bar, and if they don’t match we discard the outdated results.

More

There’s not a whole lot to this tab, it’s just a table view with a static list of extras, such as viewing certain pages of the website in a WKWebView, for instance the About page. (Since these are peripheral to the actual experience of the app, I didn’t bother to optimize the content for iOS.)

There are also cells which send you to our Facebook and Twitter pages. I thought it might be worth mentioning here because the app first attempts to open the page in the native iOS Facebook or Twitter app, and if the app is not installed on the device it falls back to opening the website in safari. Since this app is a native version of a website, I figured it’s good karma to make every effort to provide a native experience when possible. Here is the code for Facebook:

And for Twitter:

Miscellaneous

If you’re still reading (or if you’ve skipped down to the bottom), there are a few other aspects of the app which I think are worth mentioning, as they may be especially helpful to other developers.

The Quadrangle has a small set of fonts and colors that we use throughout our website, and it’s nice (when possible) to be consistent in these schemes throughout the mobile app. iOS makes it possible to alter the appearance of system elements via the appearance property. Here is an example of how the font and color can be changed on the tab bar:

Importing custom fonts is also quite easy. Just drag the .ttf or .otf file into your project, add it to your application target, and then add the Fonts provided by application key to your Info.plist file, make it an array, and add the filename of each font you want to include as items of the array.

Now, a word on loading screens: In an app like this, in which network requests can add a lot of latency, it is important to let the user know that the app is still responsive, and that it’s working on loading the requested content. The pattern I’ve used throughout this app is, when initiating a network request, to immediately bring up a loading screen (if no cached content is available) and then, in the callback from the network request, tear down the loading screen. (REMEMBER: do this on the main thread!)

To help give the loading screens a little flavor, I added some simple animations to them using Facebook’s Pop animation library. The bouncing logo on the loading screen is achieved with the following few lines of code:

Ah, and we can’t forget push notifications! It’s important for a news app to be able to send out push notifications for breaking stories. For The Quadrangle, since we generally only publish every few days, we want to send a notification to users every time a new article is published. Rather than writing my own push notification server (yuck!), I opted to use the free (for a while) service Parse. (Yes, Parse recently announced that they were ending their hosted service, but this app was written before that announcement.) Then, following this guide, I modified the server-side php to tell Parse to send out a push notification when a new post is published. On the app, we can respond to remote notifications from this method in the App Delegate:

Before we are allowed to actually display push notifications to the user, however, we have to ask for permission. Some apps do this the very first time they’re launched, but that might not be the best time to ask the user to allow push notifications, since they haven’t even used the app yet! I decided that an appropriate time to prompt them would be after they’ve finished reading an article for the first time. The following code will bring up an alert view that asks the user for permission. (I also like to build a custom view that will cover the screen behind the alert that urges the user to agree.)

Then we have to actually register for remote notifications using this callback:

Once we’ve gotten permission, we have to send the device token to Parse:

Finally, the last piece of the app I’ll touch on is a small, but many would argue necessary little feature: a prompt to ask the user to rate the app. By strategically controlling when we ask users to rate the app, we can attempt to garner higher ratings in the App Store. Hopefully everyone will rate The Quadrangle five stars, but a little extra prodding couldn’t hurt! When writing in Objective-C, I like to use UAAppReviewManager. This third-party library allows the developer to customize all kinds of parameters that determine when the prompt is shown, including number of days the app has been installed, number of unique app opens, and custom events which we can trigger in code. After a few length-of-use conditions have been met, The Quadrangle displays the alert after a user finishes reading an article by calling the following method when a user leaves the story view controller:

Okay! That’s about it! While there are a few other small details that go into making an app like this, I believe I’ve covered the main points. Hopefully, if you’re someone who was thinking of making a similar app (and if you’ve read this far, that’s probably not a bad assumption), this post can serve as a kind of high-level guide, with some low-level details to boot.

I hope you’ve enjoyed reading this post, or at the very least have found it helpful. Even more than that, I hope you will enjoy reading The Quadrangle, on the web and in the app!