Show Menu

Looking to hire an app developer?

Submit your 30 day Job Listing for FREE

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:

    
    var api: APIController = APIController()
    

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

    
    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.

    
    protocol APIControllerProtocol {
       func didRecieveAPIResults(results: NSDictionary)
    }
    

    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:

    
    class SearchResultsViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, APIControllerProtocol {
    

    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.

    
    func didRecieveAPIResults(results: NSDictionary) {
       // Store the results in our table data array
       if results.count>0 {
          self.tableData = results["results"] as NSArray
          self.appsTableView.reloadData()
       }
    }
    

    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.

    
    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:

    
    func connectionDidFinishLoading(connection: NSURLConnection!) {
       // Request complete, self.data should now hold the resulting info
       // Convert the retrieved data in to an object through JSON deserialization
       var err: NSError
       var jsonResult: NSDictionary = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as NSDictionary
    
       // Now send the JSON result to our delegate object
       delegate?.didRecieveAPIResults(jsonResult)
    }
    
    Download Full Code

    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

    having issues?

    We have a Questions and Answer section where you can ask your iOS Development questions to thousands of iOS Developers.

    Ask Question

    FREE Download!

    Get your FREE Swift 2 Cheat Sheet and quick reference guide PDF download when you sign up to SwiftMonthly


    Sharing is caring

    If you enjoyed this tutorial, please help us and others by sharing using one of the social media buttons below.


    Written by:

    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.

    Comments

    There are currently: 19 Responses to “Developing iOS8 Apps Using Swift – Part 3 – Best Practices”. Leave your comment!

    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.


    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 ‘()’


    Good stuff. Thanks for this :)


    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.
    __________________________

    import Foundation

    protocol APIControllerProtocol {

    func didRecieveAPIResults(results: NSDictionary)

    }

    class APIController {

    var delegate: APIControllerProtocol?

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

    func searchItunesFor(searchTerm: String) {

    // The iTunes API wants multiple terms separated by + symbols, so replace spaces with + signs

    println(“searchTerm= (searchTerm)”);

    var itunesSearchTerm = searchTerm.stringByReplacingOccurrencesOfString(” “, withString: “+”, options: NSStringCompareOptions.CaseInsensitiveSearch, range: nil)

    println(“itunesSearchTerm= (itunesSearchTerm)”);

    // Now escape anything else that isn’t URL-friendly

    var escapedSearchTerm = itunesSearchTerm.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)

    var urlPath = “https://itunes.apple.com/search?term=(escapedSearchTerm)&media=software”

    var url: NSURL = NSURL(string: urlPath)

    var request: NSURLRequest = NSURLRequest(URL: url)

    var connection: NSURLConnection = NSURLConnection(request: request, delegate: self, startImmediately: false)

    println(“Search iTunes API at URL (url)”)


    After getting through the protocol “tunings”, I got this to compile and run. However, didReceiveResponse never gets called like it did in the second tutorial.

    In my APIController, I now have:

    var data: NSMutableData = NSMutableData()
    var delegate: APIControllerProtocol?

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

    In SearchResultsViewController, I have:

    var api: APIController?

    and

    self.api = APIController(delegate: self)
    self.api!.searchItunesFor(“iTrinity”)

    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?


      Check your APIController extends NSObject :

      class APIController: NSObject


      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.


    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:

    protocol APIControllerProtocol {
    func didRecieveAPIResults(results: NSDictionary?)
    }

    and handled the check in my implementation. I’d be curious to know how you would handle this, because I think there are at least a couple of ways of handling it. Thanks.


    Hi! Firstly thanks for the brilliant tutorials – really helpful introduction to Swift.

    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.


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


    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?

    Thank you in advance


      What you’re describing is constructor dependency injection, and yes it’s probably a better choice here :)


        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”?


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

          init(coder aDecoder: NSCoder!) {
          self.api = APIController(delegate: self)
          super.init(coder: aDecoder)
          }


    Just before the line `delegate?.didReceiveAPIResults(jsonResult)`, if I `println(delegate?)`, I get nil.

    Is there some special magic to have this delegate variable populated? Because it looks like it is not mentioned in the tutorial.


      Yes, you should add the following line on the SearchResultsViewController viewDidLoad() method api.delegate = self


    Thank you very much for those short but very useful tuto ! I’ve decided to learn iOS programming since the Swift arrival and I do that with your help !


      Just, I’ve followed your tuto from the part 1 to this one. Everything was working great until the

      Using the Protocol

      part.

      I downloaded your code and copy past your two files keeping my Main.Storyboard aside. The bug was still here. I have concluded that the problem comes from the Main.storyboard file.

      The error I have is in the SearchResultsViewController in this line :
      var cell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier(kCellIdentifier) as UITableViewCell

      Do you have change something in the storyboard that you did not mention in your tuto ?

      Thanks !


        You need to set the identifier for the Prototype cell in your storyboard. It is mentioned in the tutorial.


    Leave a Reply

    Would you like to sign up to the mailing list by our sister site: SwiftMonthly?