Developing iOS8 Apps Using Swift – Part 3 – Best Practices

In parts 1 & 2 to this Developing iOS8 Apps Using Swift tutorial series we went over some basics of Swift, and set up a simple example project that creates a Table View and a puts some API results from iTunes Search API inside of them. If you haven’t read that yet, check out Part 1 and Part 2.

This article is part of the create iOS8 Applications with Swift tutorial series, here are the other published articles:

In this section we’re going to take a stop and clean up some of the code we’ve created so far by removing our network logic from our view controller code, and fixing some other issues that will hurt our performance. This may not be the most glamorous part of writing a new app, but it’s important! Let’s do it…

Split up your iOS8 Swift Code

First things first, let’s rename our View Controller to be something more meaningful. Open up your ViewController.swift file and replace all references to ‘ViewController’ with our new name, ‘SearchResultsViewController’. Rename the file as well to SearchResultsViewController.swift.

If you try to run the app from here you’ll get a crash. This is because our storyboard file hasn’t been updated to know about the new class! So open up the Main.storyboard, select your ‘View Controller’ object in the scene (the left-hand side nav), and then select the Identity Inspector (right-hand side, third button.)

From here let’s change the class from ‘ViewController’ to ‘SearchResultsViewController’. Now we should be back on track. Check that the project still works, and let’s proceed.

Let’s now move the API code out to it’s own class. Right click in the xcode navigation pane and select ‘New File…’. This one is going to be a Cocoa Touch Class, under the iOS->Source navigation option.

This one handles all API work, so I’m going to call it APIController.

Now let’s grab our searchItunesFor() function, and all our delegate functions like connection(didRecieveResponse….). Cut and paste these out of the Search controller and in to the APIController. You’ll also need to copy over the data variable for the response to build it’s results.

If you try to build this straight away you’ll see three errors:

1) searchItunesFor() is now undefined in our SearchResultsViewController.
2) self.tableData is now undefined in the APIController.
3) self.appsTableView is now undefined in the APIController.

To deal with this we need to let these objects know about each other. So let’s make APIController a child object of our SearchResultsViewController. It’s pretty easy to do, just add the following line underneath your SearchResultsViewController class definition:

1

var api: APIController = APIController()

Now modify the line that calls searchItunesFor() to this:

1

api.searchItunesFor("Angry Birds")

The only difference here is that we’re calling the method from an instance of an APIController, as opposed to doing this all procedural.

That takes care of the first set of errors, but now we need to also get APIController’s results back to the SearchResultsViewController. We would like for our API controller to be able to respond to arbitrary API calls, so we should define a protocol that views can subscribe to in order to get results!

Defining an API protocol

There are two error lines in our APIController right now referencing the tableData results, let’s just remove these. We’re going to use something a little cleaner.

Above the class definition in our APIController, let’s add a protocol that declare the function didRecieveAPIResults() as a required implementation.

This doesn’t do anything on it’s own, but now we can add the protocol to our SearchResultsViewController. Not adhering to the protocol will now cause an error, so we don’t make the silly mistake of not implementing didRecieveAPIResults!

Adhering to the protocol

Now modify your SearchResultsViewController to adhere to the protocol:

Building now should give an error that SearchResultsViewController doesn’t conform to the APIControllerProtocol, great! Now we just need to add the function within our SearchResultsViewController class. It will look pretty much like the contents of our connectionDidFinishLoading class from before.

The one thing left to do is change our API Controller to include a delegate object, and to call this method when the connection finished loading some API data.

Using the Protocol

Back in our APIController.swift file, let’s add the delegate, just under the class definition.

1

var delegate: APIControllerProtocol?

The question mark at the end here indicates that delegate is an *optional* value. Without the question mark, we will get a compiler error for not using an initial value for this variable, but with it we’re okay. The delegate object can be of any class here, as long as it adheres to the APIControllerProtocol by defining the method didRecieveAPIResults(), as we have done in our SearchResultsViewController

Finally, in the connectionDidFinishLoading method, let’s remove the tableView code and replace it with our fancy new protocol method:

Okay, I know that might have seemed like a lot of boilerplate, and now our app does the exact same thing as it did before, but now we have something much more flexible. We can now use APIController for any API call to the iTunes search API, and have a custom delegate get the response. I think we ran out of time on this one, so we’ll move on to the interaction in Part 4

Problems?

We have a new Questions and Answers section so you can get help from the awesome community.

Jameson Quave

I am an app developer in Austin, TX. I spend time dabbling with iPhone, iPad, Android, and web technologies. I write about technology, startups, and my technology & entrepreneurial experiments. More of my Swift Tutorials can be found on my site.

jguffey95

Indeed, this solved the problem for me. The article really should be updated so that the code works without having to find this comment

This is a great tutorial – despite this on minor flaw.

One more thing to note, in addition to adding a parameter to APIController initialization, you have to provide an init function for the APIController class as well, as follows:

init(delegate: APIControllerProtocol?) {
self.delegate = delegate
}

Finally, once I removed the “?” marks from APIController (that Ben added) it worked for me.

neilmckay

The full code for download for this section does not match the material covered here, maybe the next section?

I’m getting an error: ‘APIController’ is not constructible with ‘()’

Stephen Pengilley

Good stuff. Thanks for this

James Bond

Hi Quave,

I am learning Swift only thru your Site which shows step by step instruction to move forward. Thanks for it. I am stuck with the below. Pls help.

I am getting the error “Missing argument for parameter ‘delegate’ in call” while constructing at the line var api: APIController = APIController()

Can you advise for the resolution? My ApiConstructor shown below. Your help is highly appreciated.

OR It would be nice if you share both codes of ‘APIController’ and ‘SearchResultsViewController’ pertaining to this article (The source code attached with this page belongs to complete version of article which deals with next chapters like Master Detail screens handling.
__________________________

in the viewDidLoad(). No other code changes except what was in the tutorial. So I get a blank table because no data is returned. The “Search iTunes…” message does appear in the console, though.

Any clues?

jUAn

Check your APIController extends NSObject :

class APIController: NSObject

Art Wong

This is where I’ve been stuck too. I am reviewing the full code provided in the post to get some clues. However, does anybody have sample code that looks more like Part 2, but with the basic APIController broken out? All in all though, for me Swift is much easier to learn than Objective-C … still getting my feet wet with protocols and such, but I think I’m getting there.

mabco

This goes back to the previous tutorial, but you really should be checking for a nil coming back from JSONObjectWithData (returns AnyObject!). Otherwise, if there is an error, you will get a runtime error in didReceiveAPI results when you go to access result.count. I changed my APIControllerProtocol to this:

I’ve got an error coming up reading “missing argument for parameter ‘nibname’ in call” on the line var api: APIController = APIController()
Is this something simple I’ve done wrong? I’m relatively new to Objective C so it probably is just something silly! Thanks again.

jquave

Are you subclassing UIViewController in your APIController?
Remove that if so, it changes the constructor to need a nibname

franciscocsg

Hey Quave,

I think you forgot to mention that we have to add:
api.delegate = self on SeatchResultsViewController class.

Otherwise this code does not work.

I, also, would like to ask if it wasn’t better to pass self on the APIController constructor to avoid bugs like this one?

I tried to implement it that way and I was wondering, where is the correct place to construct the APIController(delegate: self)? You cannot do this in the declaration of “var api: APIController”, because “self” does not exist there.

A proper place seemed to be an init()-function of SearchResultsViewController itself, but I did not manage to do so, because in that init, I’m told to call super.init first in order to access “self” but a simple call to super.init() without parameters doesnot work (base class UIViewController seems to have no empty constructor).

So I placed api=APIController(delegate: self) in the function viewDidLoad(), but since this are all callback functions, this does not seem to be “clean”, because you don’t actually know, if another callback function which uses “api” might be called beforehand…

Do you have an advice, what is “good practice”?

jquave

You can lazily load the api, or you can use the UIViewController init method which comes from storyboards, which is :

Submitted Articles, Images, content and comments remain the copyright of the original author(s). It is the responsibility of the owner to not break any NDA'S, or other copyright laws. iOS-Blog.co.uk accept no responsibility and such is not liable for any content that fails to adhear to these.