While on holiday I found some need for an TV App. When we go on holiday we always take with us the Wireless HD from Seagate. In it self a very nifty device, able to stream up to five devices, of which three can be in full HD.
There is, however a small issue, there is no native TV app to browse on the device and select an video to watch. Instead you can browse on you iPhone/iPad, and using AirPlay you can stream the Video to the TV. In many ways annoying.
Enter a rainy afternoon and a couple of nights later, one TV app that enables us to just browse the device and select the desired video and play.
In order to make this happen there were some challenges:
- No idea how the iOS app was communicating with the device.
- Need to “recreate” the calls when found.
- What language to do it in: Swift or Objective-C.
- Using Test Driven Development.
It was a nice challenge to write an app in Swift, I found it in the end easier then expected. The app works. There are still issues that I need to solve. And adding Unit Tests is also easier then expected.
Trying to use Storyboards it proves again, to me at least, I do not like them one bit. In example I have an UITableView and want when tapping on a cell to do two different things, if item selected is a folder then reuse the UITableView and load with the list of items in that folder. Or if the item is an video item send the video item to the detail view controller so we can play the video. For now I’m unable to make this happen with Storyboards. Will investigate if this is at all possible. Enough ranting about those <bleep><bleep> Storyboards.
My biggest realization is that my domain knowledge on the iOS SDK (Objective-C) really helped in figuring out what I needed. It was really just a matter of finding the correct syntax in Swift. And yes, I cut some corners by using a class that contained generated Swift code and re-used a view controller from the UIKit catalog. For me it helped in gaining the understanding I needed to learn Swift. Will I do it next time, no. Besides both classes did not compile under Swift3 anyway. So I needed to do some fixing and thus gaining the again needed knowledge in Swift syntax. It has to be said that when writing in a language that is still in Beta (Swift 3) add extra fixing compile errors. When back home installed the then latest Xcode 8 (B6), I again had to revisit a number of classes to fix language changes. It helps in gaining more knowledge in Swift.
All in all a fun project to keep myself occupied with. Again the app is build by an engineer, so it lacks certain design features, for me it was and is more about the coding in Swift then anything else.
Test Driven Development (TDD)
Let’s start with the last, normally I advocate always to do TDD when creating something, it just helps keeping the code clean, tested and verified. However the phase I’m currently in, so not that much experience with Swift, adding learning writing tests in Swift is just not yet worth it. This does not mean that I will not write tests at all. Not just now. When I know what I can do in Swift I’ll add the Unit Tests later. I know it is not just yet TDD, but has to do for now. When I got back from my holiday, I migrated the project to Xcode 8 B6 and started with adding tests where I can. I’ll write about this in a separate blog post at a later date.
How does the app Communicate?
So we need to figure out how the current app is making the calls. Ok, an easy one, Charles to the rescue. Thanks to this amazing tool, a real walk in the park to figure the calls out. It turned out that the calls are straight forward REST calls with JSON responses, cool. That saves a lot of headaches. Now it was a matter of figuring out the correct call I could use.
Recreating the found calls
When I figured the call I would like to use I needed a means to help me create the calls. I really just love the tool Paw, it is very easy to test an API and as a bonus it can help in generating some boilerplate code to be used in the codebase. And this brings me to #3.
What language to use?
Well that was a no brainer, finally I had a real example app I could write in Swift. Before I was just playing a bit in the playgrounds and when I had some spare time, following the BNR book on iOS development (5th edition). So let’s write in Swift, and while we are at it, Swift 3 and in Xcode 8 Beta. This choice gave some extra challenges, a lot of the examples are written in Swift 1 or 2, and as you know a lot has changed in Swift 3. But these are relative easy to overcome. (Annoying, since they almost every time are manifested in compiler errors.)
What UI elements to use?
So let’s start with thinking about the type of app I would like to have, one way of figuring that out is, is a visit to the tvOS documentation page, and from here I downloaded the UIKit catalog. I was assuming it was written in Swift and I was not disappointed, however the code did not compile for me in Xcode 8 beta 3. (To add to the challenges already mentioned, the internet connection is not great and downloading a zip file of 12 MB takes around 20 minutes, with numerous retries. In the end I had to result in doing a “curl” in the terminal to download. Safari failed on this connection. It is definitely not the same as having fiber-to-the-home. 😉 )
So after some trial and error, I got the UIKit app to compile and run. Cool, now I have an App that will give some ideas in what I would like to create. I decided in creating a Split View Controlled app, so I would have an UITableView as the Master view controller and the Detail view controller to display the button to play and perhaps some additional data. Pretty straight forward. Thanks to the UIKit Catalog it also provides me with Video Player controller. That saves a lot of time. I just need to adjust that controller to add object to play instead on playing a generic URL. Should be doable.
Defining the model.
Any good app needs a model. Something to reflect the data we are going to use. In my case I simply took the elements I needed from the JSON object returned and created my model object out of it. Also thanks to the UIKit Catalog app, it gave me an example solve this. So I created an MobileDataItem struct. The model only needs to hold a couple of strings.
Adding the Generated NSURLSession code to the project.
As stated before I used Paw to generate a NSURLSession class. It provided me all the necessary boilerplate code to make calls to the Wireless HD device. It did not compile, it needed to be refactored into Swift3 compliant code. After the needed refactoring, I added a couple of extra parameters in the send Request call and an Array to hold the MobileDataItem’s.
Next refactored the generated code to be able to use the parameters with the rest of the fixed values that make up the URL parameters. So the calls work, and I get data back, just plain JSON, so should not be that hard to use, right? In Objective-C it is not that hard. In Swift not that easy. Really not that easy. Again went to some blog posts and found a gem SwiftyJSON. First I checked what the license is for using the SwiftyJSON code. and it is a MIT license, that is thus ok. Followed by verifying if there is already a Swift3 branch. Let’s use that one, it should work, right? And yes, it worked. Made life really easy. And thus created a function that takes the JSON object and the bool value if the object represents an folder or an item and creates the MobileDataItem objects and stores it in the array. The listing result has two arrays, one for the folder items and one for the other items. Using the struct I was able to combine the two and have the datasource ready for the UITableView.
Now came the question, how to send the data back? For the quick solution, I choose to send a notification. I know a protocol is perhaps better. But wanted to know how to do notifications in Swift. And that turned out to be a little more difficult then expected. The documentation and implementation is somewhat lacking. In the end it worked.
Setup the codebase.
I started with the template Single View Application, and to make the challenge even bigger, let’s use the provided storyboard. Normally I would not use a storyboard at all, find them annoying as hell. And yes, this app proved to me again, they are annoying as hell. For now it works, and there is room for improvement.
Master View Controller
Creating the master view controller, is just as the default an inheritance of the UITableViewController, I tried to get it to work with an UIViewController in which I placed the UITableView object, this did not work for me. It should work, but no luck with the Storyboard. So back to the default, an inheritance of UITableViewController. Writing the delegates is pretty easy in Swift. The autocomplete in Xcode 8 has made some serious improvements over the previous versions of Xcode. Getting the UITableView to work, pretty straightforward.
The data is loading, and refactored the ViewController so I could re-use it. Since every folder listing is the same and only when we have selected an video item we need to send that to the detail view controller.
First I created an custom init method, in which I would load the data, this did not work. In the end I had to overload the viewDidLoad().
When you select an item, the item is then send to the detail view controller packed in a notification so the VideoVC can use the item.
Detail View Controller
As the detail view controller I re-used and refactored the VideoViewController from the Apple UIKit example. It was there and convenient to use. Adding the notifications was a challenge. The documentation was some what lacking, but in the end it worked.
The play button is only enabled when we have selected an item to play.