Wordpress & Swift: View Single Posts Natively In iOS

Swift: View WordPress Posts Natively In iOS

NOTE: Xcode 7 and Swift 2.0 was used for this tutorial
×

In a previous tutorial I covered how to display single WordPress posts in a UIWebView. This particular tutorial is in response to a question I received, asking is it possible to display WordPress posts natively in iOS instead of using a UIWebView. The quick answer is “Of course”, but the real question is, “Do you really want to?”

I’ll be using the files from the Latest Posts project on GitHub. The files have been updated to Swift 2.0. In addition I will be using Version 2 of the WP REST API. I will also take this opportunity to get you up to speed with a few of the new features in Swift 2.0 and WP REST API 2.0

The guard keyword in Swift 2.0

guard replaces the if keyword and implements the “Bouncer Pattern” to your conditional statements. This is effective in preventing nested conditionals and best suited for instances when you aren’t quite sure about what input is allowed but definitely sure about what input IS NOT allowed. guard is also cleaner with unwrapping optionals, as you see in the example above. This pattern should look familiar to many a WordPress developer.

Alamofire for Network Calls

I’m using Alamofire for networking in this tutorial for a couple of reasons. The first reason is to point out the fact that the JSON results are now handled by enums, and to also cover the parameters parameter that we’ll be leveraging for our WP REST API calls.

WARNING: Remote calls will no longer function if you’re server does not support TLS 1.2 and your SSL isn’t SHA2 encrypted. Read About It

The Work Around

Open your Info.plist in text editor. After the last tag, paste the following:

×

Meanwhile, back at the ranch…

If you haven’t already, I suggest you upgrade your WP REST API to version 2, UNLESS you’re using it for production purposes. At the time this tutorial was written, the WP REST API was still in Beta. As you can see, my json url looks a bit different from the previous version:

We want to navigate to our functions.php to modify the output of that json call. First we want to get our featured images back, and second, well, we’ll get to that a little later in this tutorial. Add the following to your functions.php:

We’ll revisit this filter later when we build the native single view in our app.

Back to the app…

Navigate back the LatestPostsTableViewController class and find the following constants:

Alamofire allows you to better manage your url parameters. Add as many or as few parameters to the parameters dictionary as you need for your network call.

When you fire up the app, you notice nothing has changed client-side. So let’s get started with building out SinglePostViewController. In Xcode, go to File > New > File… and select an iOS Cocoa Touch Class

SinglePostViewController Step One

Next, name the new Cocoa Touch Class “SinglePostViewController” and save.

SinglePostViewController Step Two

Navigate to the Main.Storyboard and drag a new View Controller onto the story board. While you’re at it, delete the Web View Controller we created in the previous tutorial if you aren’t working with the updated copy. Select the new View Controller and attach it to the SinglePostViewController class from the Identity Inspector.

SinglePostViewController Step Three

Now let’s go back to the SinglePostViewController class and build out interface. Yes, you read correctly, we will build the interface programmatically, because…

I Don’t Like Auto Layout.

I’ve tried patiently to work with Auto Layout, but my patience ran out. If you’re building a native app that doesn’t need to render data from a network call, then use Auto Layout. But if you’re building an app such as the one in this tutorial, where you’re not always sure how much data you will display, then building your interface programmatically is the way to go.  You’ll notice quickly that constraints will break if you have a title that’s longer than the width of the view screen. You’ll also notice inconsistent layouts between different devices. Adding you UI elements programmatically will solve this issues, in addition to boosting performance since the Interface Builder and Auto Layouts adds a little overhead.

UI Elements

Add the following lazy variables inside the SinglePostViewController class, right before the viewDidLoad function:

Inside the viewDidLoad function, lets’ start by setting up the scrollView:

If you’re coming from a web development background, keep in mind that unlike a browser, scrolling is an optional component in an iOS all. Scroll bars won’t magically appear when you add elements beyond the view port. As a matter of fact, unlike a <div>, elements don’t automatically stack on top of each other either. Always think “position: absolute” when using Swift to add UI elements programmatically. If you launched the app right now and tapped one of the news cells, the app will crash, due to the removal of the Web View Controller. We’ll need to make a few modifications to the LatestPostsTableViewController class to point it to the right direction. In the LatestPostsTableViewController class, comment out or delete the prepareForSegue method and add the following:

From now on, when a cell is selected, it will manually segue to our new Single Post View Controller. But before this can work, we need to go back the Main.storyboard and select the Single Post View Controller. Choose the Identity Inspector from the right panel, and in the Storyboard ID, enter “SinglePostViewController“. Now if you fire up the app and tap a row, you’ll see a blank screen. Don’t worry, our scroll view is there. We still have elements to add.

UILabel: Title

Launch the app and you should now see the title when a news cell is tapped. Continue with the featured image and date:
And finally the content…
let’s check out progress…

 

SinglePostViewController Content

Dude, where’s my content?

And here is where things get interesting…

So I’m not getting any errors in Xcode’s debugger. There [content][rendered] key is definitely not empty, all the content is there.Obviously something about that particular key is not meshing with Swift. So I decided to tackle the anomaly on the server side first. in the functions.php go back to the my_rest_prepare_post filter. After the featured image block, add:

So I’m using WordPress’s built in strip_shortcodes function to remove all the shortcodes from the json response, if there are any. Now let’s check out the results…

  • SinglePostViewController Nothing

    Still Nothing

  • But there's content on this view

    But there’s content on this view

Ok, we still don’t have any content from the Latest Post tutorial, but we do have content on the other views, albeit un-scrollable, un-rendered HTML. At this point I suspect MY ISSUE has something to do with the plugin I’m using to display code snippets. So let’s use something a little strong than the strip_shortcodes function. replace:

with:

We stripped out all the tags, shortcodes, and any code snippets than maybe rendered as shortcodes. Before we check our progress, let’s go back into Xcode and and make sure the Single Post View Controller scrolls after the content is added. In the SinglePostViewController class, after the viewDidLoad method, add:

There may have been instances in your web development journey where you had to calculate the number of elements and their dimensions on a given page with javascript. The methodology is no different. Now let’s see what we have…

SinglePostViewController Scrolling

It works… sort of?

Ok, so we have our WordPress content loaded in a natively in the app, and the scrollView works (don’t forget to add padding at the bottom) but all the formatting, images and context is lost.

This is playing out like a bad 80’s romantic comedy

Swift is that stuffy, constipated retired Army General and his new girlfriend  is WordPress, who is crazy and carefree. The point here is that Swift wants to know exactly what type of input it’s dealing with at all times. In any given post in WordPress you can have code snippets (like my tutorials), video, audio, embedded pdfs, and who know what else a plugin developer will create by the time this tutorial is finished. So how do we marry these 2?

UIWebView

So we’re back here again. But isn’t this cheating? Don’t you have some regex to format all this HTML so we can display it in the UILabel? Where are the HTMLtoSwift libraries?

UIWebView ≠ HTTP Request

In the previous tutorial I used a UIWebView to load a post page. But you don’t have to use the UIWebView to LOAD pages. You can also bake HTML inside of it. Swift doesn’t have dynamically formatted text, nor should it. For more complicated formatting, we are given the UIWebView to work with. I figure if Apple didn’t want ANY web content loaded in their apps they would have never included UIWebView in Xcode. Since we loaded all our JSON upon the initial launch of the app, we don’t need to make another http request.

Comment out the viewDidLayoutSubviews method and the postContent code block in the viewDidLoad method. Navigate to the top of SinglePostViewController class and add the UIWebViewDelegate

Next, setup the UIWebView in the viewDidLoad method

Add after viewDidLoad method

Finally, go back to your functions.php and remove the the strip_tags line:

SinglePostViewController Oh Snap

Oh Snap! It works!

As you can see, the entire page rendered, without making another http request. Depending on the size of your content, there will be a slight delay in rendering. To get around this you can either addSubviews inside the webViewDidFinishLoad method and/or use the webViewDidStartLoad method also provided by the  UIWebViewDelegate to run a cool preloader.

But wait, There’s more!

While we got my page to render natively, retaining the images and code snippets, the formatting is still not quite right because my style sheet didn’t make the trip in the JSON result. No sweat, we’ll just add a style sheet inside the app itself. Go to File > New > File… > iOS > Other > Empty

create css in swift

Name the file appStyles.css (or whatever you want with the extension css) and save. Open the appStyles.css and paste your css inside. Next, Navigate back to the SinglePostViewController class and inside the viewDidLoad method, replace the postContentWeb code block with:

  • SinglePostViewController Formatted Two

    I…

  • SinglePostViewController Formatted One

    possess…

  • SinglePostViewController Formatted Three

    many styles.

There are still a few crumbs to clean up but you get the picture. So now you know how to natively load content into an iOS app using Swift. But I have a question…

Occam’s razor

Was this tutorial worth avoiding one extra HTTP Request? The previous tutorial also worked just fine. The lesson to be gleaned from this tutorial is that you have more than one way to solve a problem, and that plan your course of action thoroughly before deciding that only one method is the only solution.

NOTE: Don’t forget to add your json url and parameters

Also, when you open the project, open it up from the Latest Posts.xcworkspace file.
×

And don’t forget to Git the latest version of this project