Developing iOS Apps Using Swift Tutorial Part 2

 

This section completely updated to reflect changes in Xcode 8.3.1, as of April 17, 2017

In part 1 we went over some basics of Swift, and set up a simple example project that creates a Table View and a puts some text inside of them. If you haven’t read that yet, give it a read here.

For this section, we’re going to do something a little more ambitious. We’re going to hit the iTunes Search API for the iTunes Store, download the JSON results, parse them in to Dictionaries and then populate our Table View with this information. Then, we will add some user interaction by adding a click event to the tableview, so that when an item is clicked the iTunes store item will be opened.

If this sounds like a lot of work, don’t sweat it. This is pretty basic functionality for iOS apps and it’s one of the most common things any developer has to do. Let’s get going…

Connecting the UI

The first thing we need to do is get a reference to our tableView, so it can be used from code. Go ahead and add this line to your ViewController.swift file, just under the class definition, but outside of any functions.

@IBOutlet var appsTableView : UITableView!

This bit of code allows up to connect our Table View in our Storyboard to this variable, “appsTableView”. Save this file and open up your storyboard. Now select the View Controller object (the one with a yellow icon) and in the right-hand side pane click the last tab, the Connections Inspector. Here you should now see an outlet for “appsTableView”. Click and drag from the dot next to this outlet on to the Table View in our scene.

Connecting the Table View to the View Controller

Let’s also add a variable to hold the table data itself. Just under the class definition for ViewController add:

var tableData = [[String: String]]()

This variable is an Array type that contains multiple Dictionary types (or hashable types if you prefer). Inside these values the key is of type String as well as the value. Or in other words I can get or set String values by accessing this variable with any String key in the dictionary, as we’ll see in a moment. If that does’t make sense just yet, just keep going and you’ll see how it’s used later in the tutorial.

Making the API Request

Now that we have the UI connected, we’re ready to make an API call. Create a new function called searchItunesFor(searchTerm: String). We’ll use this to make our requests happen for arbitrary search terms.

To keep this tutorial short, I’m going to just post my final code and let the comments do some of the explaining. I’ll also break it down line-by-line afterward. Also, I’m always open to questions and further discussion in the comments though, so feel free to chime in!

func searchItunes(searchTerm: String) {
    // The iTunes API wants multiple terms separated by + symbols, so replace spaces with + signs
    let itunesSearchTerm = searchTerm.replacingOccurrences(of: " ", with: "+", options: .caseInsensitive, range: nil)
    // Also replace every character with a percent encoding
    let escapedSearchTerm = itunesSearchTerm.addingPercentEncoding(withAllowedCharacters: [])!
 
    // This is the URL that Apple offers for their search API
    let urlString = "http://itunes.apple.com/search?term=\(escapedSearchTerm)&media=software"
    let url = URL(string: urlString)!
    URLSession.shared.dataTask(with: url) { (data, response, error) in
        if let error = error {
            // If there is an error in the web request, print it to the console
            print(error)
            return
        }
        // Because we'll be calling a function that can throw an error
        // we need to wrap the attempt inside of a do { } block
        // and catch any error that comes back inside the catch block
        do {
            let jsonResult = try JSONSerialization.jsonObject(with: data!, options: []) as! [String: Any]
            self.didReceive(searchResult: jsonResult)
        }
        catch let err {
            print(err.localizedDescription)
        }
        // Close the dataTask block, and call resume() in order to make it run immediately
    }.resume()
}

First, we fix some issues with the search terms. For one thing, the iTunes API is not going to like seing spaces, instead it wants plus signs (+), so the replacingOccurences function does this work, and we put the result in the itunesSearchTerm variable.

Next, we also want to make sure no other characters will cause issues. For example a question mark (?) may be viewed by the API as the end of the request if it was included in the search term. So we use the method addingPercentEncoding to encoded every character in to a percent-encoded form, and store this new value in to escapedSearchTerm.

After that we define our URL, which is just the iTunes search API URL with the escapedSearchTerm inserted in to the term field. We store this in the variable urlString. Next, we convert this URL String in to a proper URL object. This will basically validate that the URL is valid, or at least properly-formed, and then it gives it some useful methods as well that are common to URLs.

Now comes the network request. We make the request from the URLSession API that Apple provides. You could create a URLSession for this task, but Apple’s API will provide a pre-constructed one called shared. We access this with the simple dot-notation URLSession.shared.

From here, we use the shared sesion to create a new Data Task by calling it’s dataTask function with the url we created the line before. The next part is the completion handler. This itself is a function we define inline, inside of the curly braces.

So, everything here inside of the curly braces, where the indentation is increased, is only executed after the Data Task has been completed. Hopefully when this happens, what we get back from the server is JSON data containing the search results! It’s also possible that there was some kind of error, so we check whether or not an error is present by using the optional binding syntax:

if let error = error {
...

This syntax replaces the optional error: Error? with an unwrapped optional error: Error, but only if it exists. Most of the time, the value of error will be nil, and this block of code will be skipped entirely.

Next we are going to decode the result from a Data object in to a dictionary we can use to access each JSON element in the results.

Because the Apple APIs for JSON deserialization can throw an error, we perform this work inside of a do { } block, with a catch let err block after it. If something goes wrong during JSON deserialization, the catch block will be executed. But if everything goes to plan, the do block will complete executing.

Once we get the deserialized data back, we call a new method self.didReceive(searchResult: jsonResult).
This method is not defined yet, so we’ll define it next.

Finally we call resume() at the end of the dataTask block in order to execute the API request immediately.

Getting the data we need out of the JSON response

Now that we are getting a JSON response back, we will want to take out the values we care about. In this case that’s the price, a thumbnail, and the name of the app. So next, let’s create the didReceive method inside our ViewController class in order to parse out this data and store it inside the tableData variable that will inform our Table View.

func didReceive(searchResult: [String: Any]) {
    // Make sure the results are in the expected format of [String: Any]
    guard let results = searchResult["results"] as? [[String: Any]] else {
        print("Could not process search results...")
        return
    }
 
    // Create a temporary place to add the new list of app details to
    var apps = [[String: String]]()
 
    // Loop through all the results...
    for result in results {
        // Check that we have String values for each key we care about
        if let thumbnailURLString = result["artworkUrl100"] as? String,
            let appName = result["trackName"] as? String,
            let price = result["formattedPrice"] as? String {
            // All three data points are valid, add the record to the list
            apps.append(
                [
                    "thumbnailURLString": thumbnailURLString,
                    "appName": appName,
                    "price": price
                ]
            )
        }
    }
    // Update our tableData variable as a way to store
    // the list of app's data we pulled out of the JSON
    tableData = apps
    // Refresh the table with the new data
    DispatchQueue.main.async {
        self.appsTableView.reloadData()
    }
}

Here we are first off checking that we have the right data type. The results argument passed in to the didReceive function here is our JSON value returned from the iTunes Search API. We are going to want to check for three keys:

  • artworkUrl100
  • trackName
  • formattedPrice

Why these keys? This is just the keys Apple chose in their API docs. If you take a look at a sample API response you’ll see keys contain the info we want about an app.

So what we do is we loop through everything inside of the array of results, and then check that each of these three values are present, and are convertible to a String type. Here we’re using a compound form of the optional binding syntax that allows us to check for the valid presence of multiple values. if let varA = valueA, let varB = valueB { }. Not only are we checking for their presence, but if they can be represented as String objects.

If all three keys are present, and they can all be represented as strings, then we have an app record! We’ll add it to the list of apps, a temporary variable we created to store each new app we come across for this query.

I’m using slightly different keys for our app’s purposes. This is mainly just to show there is no reason these keys must match.

Finally, once the list of apps is completely updated, we assign the tableData variable that is going to be used to hold the Table View’s data to the array of apps we just created. From there, we call reloadData() on the appsTableView, which let’s the table know there is new data to work with, and that it should re-render itself.

You’ll notice the reloadData() is inside of another block. This one is from the Grand Central Dispatch API. By putting this code inside of the block DispatchQueue.main.async { }, we’re able to assure this is executed on the foreground thread. Because this code is all being executed in response to a network request (the API call), we end up on a background thread. This is because we know we have to wait a moment for the API to respond. The iOS App won’t just freeze the app up while it waits for the response. Instead, it will send the response in to a background thread.

So, now that we are actually updating some UI, we want to jump back on to the main thread and make sure it shows up immediately. If you are curious, you can call reloadData outside of this block and see how it effects the performance of the app (after we update the actual UI in the next step)

Update the UI with the new data

Now that we’ve got our API call updating the tableData variable, and calling reloadData() on the appsTableView we connected earlier, we can proceed to actually implement those UITableViewDataSource methods we set up in the beginning of the tutorial…

You may remember from last time we implemented the function for our Table View that we had a count method, which determines the number of rows; and a cell method, which actually creates the cell and modifies it for each row.

We’re going to update these now to use the data we pulled down from the web.

Swap out your methods with these two functions:

// MARK: UITableViewDataSource
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return tableData.count
}
 
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "MyTestCell")
 
    // Get the app from the list at this row's index
    let app = tableData[indexPath.row]
 
    // Pull out the relevant strings in the app record
    cell.textLabel?.text = app["appName"]
    cell.detailTextLabel?.text = app["price"]
 
    return cell
}

The first one is pretty simple. The number of rows should be the same as the count of the records in tableData (the number of app returned from the API).

The second function is what creates the cell, which is what is displayed in the Table View. First we call the constructor to make a fresh UITableViewCell to work with. Then we retrieve the current app that should be displayed by accessing tableData[indexPath.row]. The variable indexPath is passed in as an argument by Apple’s Table View class. It represents the current index that it is asking for a cell for. It’s kind of a backwards way of thinking about it, but basically this function is the table itself asking for an individual row. Which row? Well… the one at indexPath.row.

Next, we’ll set the text values for the text and detail labels of the cell. We’ll set these to be the value of the app array we created earlier in response to the API response.

Call the API

As a final step, we’ll manually make the API call for our searchItunes method by adding it inside of the viewDidLoad function. The viewDidLoad method is called on this View Controller object any time the screen it represents is being constructed. This is a common pattern in iOS development.

Find your viewDidLoad function (the Xcode template will have already added it) and add a call to the searchItunes function.

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
    searchItunes(searchTerm: "JQ Software")
}

It’s alive!

Finally, run the app! Do you see the search results? You should see a few of my apps from the App Store pop up if you used my JQ Software search term. Try some other search terms and rebuild the app to see the different results that come back. You’ll notice we don’t yet have images in place, but we’ll get to that in later sections. We did get the data for that already after all.

If you run the App using JQ Software as a search term it will look like this at this stage:

JQ Software Apps List

Next time in Part 3 we’ll work on the interaction piece of the app, allowing users to search for anything they like, and making the table cells clickable!

Download the Source Code

Go to the Next Part

Go to Part 3 now ->

Follow me on Twitter


Sign up now and get a set of FREE video tutorials on writing iOS apps coming soon.

Subscribe via RSS

125 thoughts on “Developing iOS Apps Using Swift Tutorial Part 2

  1. Great to see a tutorial up so quickly, after nearly 3 years I consider my self to be a good iOS programmer, as I have not had much exposure to other languages one thing confuses me a bit in terms of structure in Swift.

    In OjbC you have a header where anything public i.e. available to external classes, and then properties could either be in the .m or .h depending on if they were private or public.

    I have played a bit with Swift, Am i right in understanding that all properties are now available to other classes unless declared inside a function. Also how do functions become private?

    Regards Melvin

    • Melvin, thank you for your comments. The header files are the old tried and true way of determine what classes can see what, but in Swift we’re dealing with something quite different. It does in fact appear that the only way to make something private is to put it inside a function. This is not unlike javascript, and has become a common pattern to make ‘modules’ out of functions in order to create encapsulated systems.

      Regarding actually making a function private, the easiest way is to use the module pattern. I’ve created a Gist for you here. Everything you see in this file is public, except the function named ‘someInnerFuncVar’.

      https://gist.github.com/jquave/6fb62d951cebe4f6c7e5

      • I find this pattern somewhat confusing coming from a more purely object-oriented background. Your example shows how a single function might use a nested function to hide it, but how does a class maintain private state that needs to be accessible from multiple functions? Or let’s say that your nested function would be useful somewhere else in the class – how do you implement utility functions in a class (in a DRY way) that should never be called from outside the class?

        In Obj-C, the de facto way to do this would be to add properties to the anonymous class continuation category in the .m file, effectively “hiding” them from the outside world, or to implement methods only in the source file and not declare them in the public header. There was also the option of creating a “protected” header for use by subclasses but not as a public interface.

        Is there any possible way to do things like this in swift? If not, it seems like clean API design will be very difficult, and that open-source libraries will be full of “// DO NOT USE THESE FUNCTIONS”

        I’m wondering why they didn’t just adopt the public/protected/private access model of c++ and similar languages. You still wouldn’t need a header, but it would be much easier to restrict access to properties and functions that nothing else has any business futzing with.

        • I read somewhere that private, protected, public etc. are coming to Swift in a near future. Apparently it was something that wasn’t ready. I believe I read this on some Apple forum where an Apple engineer replied.

  2. Thanks Jameson, thats what I kind of figured, it does represent a new approach it seems. Looking forward to the rest of the series.

  3. Hey. Thanks for these tutorials, I’m an Android developer with pretty much no experience with iOS, and your first couple of tutorials have been straight forward and make sense to an outsider like myself.

    • That’s great to hear! There are for sure some best practices that will need to be added in. I was planning to follow up with more interaction, but I may take a pit stop and start refactoring what we already have with proper code architecture principles. This code is not yet ready for production 🙂

      • No worries. I write a blog on Android development, and the code is always a proof of concept moreso than “I promise this won’t make your fellow developers want to strangle you”

  4. Hi! Great tutorial!
    I was playing with the code and run into some trouble.
    if I change
    “https://itunes.apple.com/search?term=\(escapedSearchTerm)&media=software”

    for

    “https://itunes.apple.com/search?term=\(escapedSearchTerm)&media=music”

    I get an EXC_BAD_INSTRUCTION on
    cell.text = rowData[“trackName”] as String
    But if I println(rowData[“trackName”]) it prints fine on the console.
    Any thoughts?
    Thank you very much!

    • Try a println() on the JSON response before you try to access its members. Its possible the music results don’t always include a trackName. Normally I would add error checking to validate the key before using it, but like all of us I’m still fresh and it’ll probably come in part 3 🙂

      • When searching for software I’m getting a valid JSON response but am unfortunately also getting the same error as Guillermo. Any ideas?

          • I’m getting this same issue too. I tried the above fix but didn’t help.

            My Xcode throws a breakpoint in at the `if jsonResult` line and when continuing to iterate through, the error I get in console is: fatal error: Can’t unwrap Optional.None

            I haven’t changed the code you’ve provided except for the cellText lines above trying to fix this

            Thanks for the tutorial! It’s been helpful, just too green to know how to debug this at this point

          • One additional note on my comment, it happens on the second item when iterating through, I get the above error and EXC_BAD_INSTUCTION as noted above

        • I think there is probably something wrong with the cell. Try setting up a re-use identifier in a prototype cell in your storyboard. This is discussed more in depth in Part 4.

    • I received this error as well. On my end, ctrl+drag+dropping from the ‘Table View’ to the ‘View Controller’ did not connect “var appsTableView : UITableView” as my referencing outlet.

      To Fix: I had to go back to the storyboard, ctrl+click on the “Table View” and then under “Referencing Outlets” select “appsTableView”;

      Hope this helps. Not sure why it didn’t connect in the first place.

      • I’m experiencing the same error Weirdly, I don’t even see ‘appsTableView’ underneath ‘Referencing Outlets’ at all. Even though I have ‘@IBOutlet var appsTableView : UITableView’ in the ‘ViewController’ class.

        • Fixed by dragging the TableView from the Storyboard into the ViewController, whereby it created a new IBOutlet.

          Curiously, the one it created had ‘ = nil’ on the end, not sure how important that is?

          Either way, it’s now linked up and working. Great tutorial! 🙂

          • Not fixed to me. I can’t link it.
            however, I have a lot of Xcode6Beta crashes while editing code. Have anyone the same issues?
            Maybe I need to download again.

    • i was getting the same “fatal error: Can’t unwrap Optional.None”. As mentioned by others looks like this occurs because the outlet is not getting created when the drag/drop from table view to controller occurs. In order to fix mine, I opened up the assistant editor so that the storyboard file and the viewcontroller.swift file were visible side by side and I did the drag/drop from the tableview straight into the code file. All compiled and ran perfectly after that. Great tutorial so far, really learning a lot considering I have extremely limited Obj-C experience (coming from a .Net background). Looking forward to working thru the rest of the blog posts.

  5. Just wanted to say well done on what is an easy to follow tutorial. Always good to have someone dive right in there and show how old things can be done anew! I’ll be singing your praises from the rooftop and see what I can do to create some exposure for your swift tutorials.

    I’m looking forward to the next one.

  6. Thanks for the quick tutorial. Will follow this for next episodes for sure.

    I’m familar with Obj-C but Swift is way faster to code things.
    atm I am totaly into Swift and trying to learn the ups and downs.
    The only draw back is, it does not support OS X 10.8 or lower.

  7. I followed along but where I ran into trouble was I made a data class and then in my View Controller I made a couple instances of that data class and tried storing those instances in an array so I could then fill my views rows by looping through the array.

    I get an error when I try storing the instances in an array have you tried this yet or know if its possible in swift? Seems like when I create my instances then try to store them in the array it doesn’t know they exist yet.

    Thanks for the great tutorial!

  8. Just for this quick and easy intro to Swift.
    However I’m having performance issues. When querying for “Disney” I’m getting a tableview of about 30 rows or so. But when scrolling the performance is just terrible. Slow. Could it be because of the images?
    Btw it happens both on my mac using simulator (iOS8) and on my iPhone 5 running iOS7

    • Yep you would want to ideally have caching of images and asynch loading. This demo intentionally cut all that out to keep things very simple. I’m working on the async right now for Part 5.

  9. I think we can replace this part

    // Download an NSData representation of the image at the URL
    var imgData: NSData = NSData(contentsOfURL: imgURL)
    cell.image = UIImage(data: imgData)

    with another one that using threads or downloading images in the background. I think the previous code will wait till all images loaded first.

  10. I had a strange problem. I’m doing everything programmatically(i think that this way i can learn more) and my cellForRowAtIndexPath is not being called! ‘oO’
    Even setting my delegates and datasource protocols and related methods, this method not get called… The strange thing is that my numberOfRowsInSection is called(first when we load the view and add the tableView as a subview, and than later, when i reload my tableview’s data once my request is finished). It is very strange… Here is a gist of my mainController that is being called as a rootVC in the window setted in my AppDelegate.swift :
    https://gist.github.com/TiagoMaiaL/f95910d19d3ee1bebfc3

    I used to make Lazy load in my setters(in fact, i made this in my swift code too), but i’m a bit confused, because now we have a keyWord called @lazy for this things.

    Thanks!

    • Tiago, this means probably one of two things:
      1) Your tabledata has 0 rows for some reason
      2) Your method signature does not match the expected method signature for the table view delegate/datasource

      But it’s probably #1

      • I thought that it was one of these erros.
        After the request is finished, my array has 4 items. But when i call reload data, it does not call cellForRowAtIndexPath…
        I had keep track of the states in this variables in each time. In relation to my method signature, it is correct, as i change that and the compiler shows me the error that my delegate is not implemented…

        Thanks for the quick answer!

      • I have the same problem. I can see that results are coming back, but tableView(tableView: cellForRowAtIndexPath:) isn’t being called at all. I’ve done a copy/paste from your gist and all I’m getting is a blank table view.

        • The only reason that wouldn’t call is the delegate & dataSource of the tableview isnt set to self. In the storyboard file, click on the table view and go to the very last tab of the panes on the right-hand-side. It’ll show if delegate & dataSource are set.

  11. > Now by control+click+dragging from the Table View to our ‘View Controller’ object, we’ve linked these objects. Easy, right?

    You meant the opposite right? Something like “control+click+drag from our View Controller to our Table View” – that pops up a dialog to select the appTableView property

      • Only worked for me dragging from ViewController object to TableView.

        As a newbie to iOS programming, the fact that you can name multiple functions the same was odd to me. Maybe a quick explanation?

        • You can’t name multiple Obj-C functions the same, but the “name” is defined by the method signature. In objective-c if you have a method like this:
          – (void) myMethodName:(int)myFirstParameter withASecondParameter:(int)mySecondParameter;
          Then the method name in Swift is “myMethodName(…)”, so for this reason you may see what seems to be methods with the same names doing different things, but that’s because under the hood, the named parameters in Swift are part of the method signature in Obj-C:
          “myMethodName: withASecondParameter:”

  12. Hi,

    first I want to thank you for this tutorials!!! Now the question. Why in connectionDidFinishLoading you use data instead of self.data?

    var jsonResult: NSDictionary = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as NSDictionary

    Im a newbie in IOS 🙂

  13. Hi,

    I’m doing your tutorial and it is working. Now I’m trying to adapt it to my project.
    Is there a simple way to parse a Json using swift ?
    I have a json like:
    {
    “title”: “Train speed”,
    “description”: “speed”,
    “images”: [
    {
    “position”: 1,
    “item”: [
    {
    “url”: “myurl_1”,
    “width”: 640,
    “height”: 359
    },
    {
    “size”: “L”,
    “url”: “myurl_2”,
    “width”: 320,
    “height”: 179
    },
    {
    “size”: “M”,
    “url”: “myurl_3”,
    “width”: 160,
    “height”: 89
    },
    {
    “size”: “M”,
    “url”: “myurl_4”,
    “width”: 160,
    “height”: 89
    }
    ]
    }
    ]
    }

    For the moment I managed to parse it using loop inside loop and loop again.
    Here is what I did:
    if let element = rowData[“images”]{
    var rowDataImg: NSDictionary = element[0] as NSDictionary
    if let element1 = rowDataImg[“item”]{
    var rowImgUrl: NSDictionary = element1[0] as NSDictionary
    var urlString: NSString = rowImgUrl[“url”] as String
    var imgURL: NSURL = NSURL(string: urlString)
    var imgData: NSData = NSData(contentsOfURL: imgURL)
    cell.image = UIImage(data: imgData)
    }
    }

    Thanks.

    JM.

    • var deserializedJSON: NSDictionary = NSJSONSerialization.JSONObjectWithData([myJsonString.dataUsingEncoding(NSUTF8StringEncoding)], options: NSJSONReadingOptions.MutableContainers, error: nil) as NSDictionary

  14. Thanks for the answer.
    I’m a beginner in iOs App (I’m a java developper actually) .
    Can you please show me how to get the value (for instance) of the “url” attribute? Can I use something like a xpath ?
    I’m used to groovy where you just do something like deserializedJSON.att1.att2.att3 ..

    Thanks.

    • Try this:
      var images: NSArray = deserializedJSON[“images”] as NSArray
      var items: NSArray = images[“item”] as NSArray
      var firstItem: NSDictionary = items[0] as NSDictionary
      var url: String = firstItem[“url”] as String

      The main issue here is that the JSON is being deserialized in to Obj-C types, NSDictionary and NSArray. If it were a pure Swift object it would be much better, but that API doesn’t seem to exist yet. Maybe I’ll write it.

      • I tried but it does not work.
        It fails at:

        var items: NSArray = images[“items”] as NSArray

        with the message: Cannot convert the expression’s type NSArray to type StringLiteralConvertible.

        Any idea?
        I’ve tried some stuff but I cannot get the thing working.

        Thanks.

        • I am having a very similar problem, getting these two errors:

          var rowData: NSDictionary = self.tableData[indexPath.row] as NSDictionary <-'NSArray' is not convertible to 'NSDictionary'

          and

          self.tableData = results <- 'AnyObject' is not identical to 'NSArray'

          Any ideas as to why?

  15. Hi Jameson,

    I have tried with tutorial which you have sent, but one problem i’m facing is “fatal error: Can’t unwrap Optional.None”, this error is raising on the self.appsTableView.reloadData()……

    Please help me regarding this to move forward
    TAI

  16. This is a great tutorial and I am really enjoying it however, I noticed that you’re not using the baked in inference that helps make this language so light. Any particular reason?

    • I start using it more later in the series. The only reason I’m doing it in the beginning is because I’m used to it. I would go back and remove it, but I also think it helps explain what’s happening under the hood a bit.

  17. Is part 2 in github by chance? I’m getting an error in connectionDidFinishLoading but am also noticing that connection(didRecieveResponse) and connection(didRecieveData) are not being triggered. URL seems good so my delegate connection must be broken somewhere.

    Thanks!
    Phong

  18. Minor nit, but it may also have an unexpected consequence.

    You are systematically misspelling “receive” as “recieve,” except in the one case where it really matters. Normally, I wouldn’t care, but you have a callback function:

    func connection(connection: NSURLConnection,
    didRecieveResponse response: NSURLResponse) {
    println(“Recieved response”)
    }

    Is this function just never going to be called, because it is misnamed? What will happen if the system really does want to call this method and it finds this misnamed method? Is it just ignored?

  19. Hey great tutorial!

    There’s one line in your searchItunesFor function that puzzles me:
    var results: NSArray = jsonResult[“results”] as NSArray

    I’m wondering: where did you get the argument name [“results”] ? Meaning how did you know to use that name and not some other name? And are there other names or values that can be used there?

    thanks!

    • actually, I can answer my own question here 🙂

      “results” comes directly from the iTunes Search API itself: that’s the word they use for returned JSON results. So it comes from Apple. All I had to do was look that up…

      thanks anyway – keep up the good work!

  20. I’m trying to use this code as a base for accessing another JSON API, but whenever I change the URL and try to run it I get an EXC_BAD_INSTRUCTION error on the line ” println(error.localizedDescription)” If I try to comment out that line, I get errors pretty much all the way up. Would this code only work for this particular API or am I just doing something wrong?

    • It should work the same with any API, I’m going to say there’s probably a different issue. I’m guessing you need to modify the way it’s deserialized a bit due to a difference in the way the data is structured.

  21. Great Tutorial!! I do have one question though. How would I go about connecting to a url that required username/password authentication? Thanks!

  22. Doesn’t compile on xCode 6 Beta 3.

    cell.text -> cell.textLabel.text

    Not sure what to do with cell.image though – I find it difficult to see what to do with deprecations, in java we usually get a comment with a clue as to what has replaced the deprecation

    • Just left a comment, but change:

      cell.image = UIImage(data: imgData)
      to
      cell.imageView.image = UIImage(data: imgData)

  23. In the latest Xcode6-Beta3, you won’t be able to run your program (due to deprecation errors) until you change the following:

    From:
    cell.image = UIImage(data: imgData)

    To:
    cell.imageView.image = UIImage(data: imgData)

    AND

    From:
    cell.text = rowData[“trackName”] as String

    To:
    cell.textLabel.text = rowData[“trackName”] as String

  24. Hey, I’m new to Swift and trying to follow your tutorial. I’m using xcode 6 – beta 5 and im getting a compile error for the line after jsonResult

    if (err?)

    Type NSError? does not conform to protocol ‘BooleanType.Protocol

    and in the console

    fatal error: unexpectedly found nil while unwrapping an Optional value

    This is likely an error on my part, just wondering if anybody else could reproduce it. Thanks for the tutorial

  25. My build is apparently successful but my table is blank. I checked that my view controller is connected to my delegate and datasource. Any idea what Could be going on here? Would have anything to do with me using Xcode Beta 2?

  26. I’m getting this error: The operation couldn’t be completed. (NSURLErrorDomain error -1002.)
    It’s getting thrown here:
    if((error) != nil) {
    // If there is an error in the web request, print it to the console
    println(error.localizedDescription)
    }

    I’m using Xcode 6 beta 6

  27. Thanks for making these guides!

    I ran into some troubles running the code :

    “Task completed
    The operation couldn’t be completed. (NSURLErrorDomain error -1002.)
    fatal error: unexpectedly found nil while unwrapping an Optional value”

    failing on line :
    var jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &err) as NSDictionary

    Im using beta6

  28. Just upgraded to Xcode 6 beta 6. When I run this tutorial (part 2) as described above, I get “The operation couldn’t be completed. (NSURLErrorDomain error -1002.)
    fatal error: unexpectedly found nil while unwrapping an Optional value”

    If I modify the URLPath and put something in like:
    let urlPath = “https://itunes.apple.com/search?term=apple&media=software”

    Then it works fine. Is it possible that beta 6 might have changed something and now your original code is returning an empty set from iTunes?

    All the xcode beta (breaking) changes almost make noobs like myself want to wait until it’s finally released…almost… 🙂

  29. I see above that you fixed the code for Beta6, but I am running beta 6 (6A280e) and I keep getting…

    The operation couldn’t be completed. (NSURLErrorDomain error -1005.)
    fatal error: unexpectedly found nil while unwrapping an Optional value

    at…

    var jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &err) as NSDictionary

    I even checked out your github repo to verify it wasn’t my coding mistake.

  30. It’s rather counterproductive to create a comprehensive tutorial for a new programming language if three months in you stop updating the code. Please keep your material up to date and accurate or remove it so people can learn accurate information.

  31. i have questions:
    1)why swift is a compilery language when it runs the same time?
    2)when we can exchange the data o fvariables this is correct?
    var name = “hello”
    name = 20 //exchange string to int

  32. Jameson Quave you can write the txt of the Conference that intruduction of swift?
    whit link:http:
    //www.bing.com/videos/search?q=swift%20programming%20language%2Byoutube&qs=n&form=QBVR&pq=swift%20programming%20language%2Byoutube&sc=0-27&sp=-1&sk=#view=detail&mid=728F416521E939E080A1728F416521E939E080A1

  33. Hey, thanks for the tutorial. however i needed to add a question mark at the end of these two in order for it to work:

    cell.textLabel?.text = rowData[“trackName”] as? String
    cell.imageView?.image = UIImage(data: imgData!)

    • Whether or not these properties are optional (and therefore whether or not they need the question mark) has been changing with every version of Xcode, so it’s slightly different for everyone. However, Xcode will automatically suggest fixes to these issues.

      In either event, you should learn why this is happening by reading up on Optionals in Swift and how they work.

  34. Hi,

    thanks for your great work here. I’m new to Swift (and iOS programming).

    I’m trying to run the code example above and I’m constantly getting the same mistake (even if I download your code from GitHub) – I’m using Xcode 6.1.1

    In ViewController.swift

    cell.textLabel.text = rowData[“trackName”] as? String
    Error: UILabel? does not have a member named ‘text’
    (…)
    cell.imageView.image = UIImage(data: imgData!)
    Error: UIImageView? does not have a member named ‘image’

    Any idea how I could solve this?

    Thanks

    • could solve it by myself:

      change

      cell.textLabel.text = rowData[“trackName”] as? String
      to
      cell.textLabel!.text = rowData[“trackName”] as? String

      AND

      cell.imageView.image = UIImage(data: imgData!)
      to
      cell.imageView!.image = UIImage(data: imgData!)

      • I’m also new to all of this and I just changed it to:

        cell.textLabel?.text = rowData[“trackName”] as? String
        cell.imageView?.image = UIImage(data: imgData!)

        I’m not sure what the difference between a “?” and “!” is, but they both seem to get the job done.

        • Using ? is safer, if the textLabel or imageView don’t exist, the code is not run.
          However, if you use !, you are *insisting* that they exist, and if they don’t, the app will crash.

  35. Hi,

    Great tutorial man, just fix the “or” to “for” under the line:
    let task = session.dataTaskWithURL(url, completionHandler: {data, response, error -> Void in

    and keep going with your great work

  36. I am getting this error:

    “extra argument ‘error’ in call” at this line:

    if let jsonResult = NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers, error: &err) as? NSDictionary {

    the problem seems to be “error: &err”

    I am using Xcode 7beta

    • Xcode 7 Beta uses a new Error handling paradigm. Here’s the short version of how to fix cases like this:

      1) Delete the error argument, and any references to the err variable.
      2) Put try! in front of the call that used to have the error argument (this is unsafe, but will help you get through this for now)

    • Oh you don’t need to set them to nil, we aren’t really interested in the differences in the option for this tutorial. The options basically just control whether or not you can modify values after retrieving them

  37. Doing this way build is ok

    if let jsonResult = try! NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableLeaves) as? NSDictionary {
    if let results: NSArray = jsonResult[“results”] as? NSArray {
    dispatch_async(dispatch_get_main_queue(), {
    self.tableData = results
    self.appsTableView!.reloadData()
    })
    }
    }

    but I get an error when launching the app

    2015-06-14 13:04:28.066 iTunesSearch[1578:295858] CFNetwork SSLHandshake failed (-9824)
    2015-06-14 13:04:28.068 iTunesSearch[1578:295858] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9824)
    Task completed
    An SSL error has occurred and a secure connection to the server cannot be made.
    fatal error: unexpectedly found nil while unwrapping an Optional value
    (lldb)

  38. Hi, thanks for the tutorial, it’s very interesting so far. I just ran into something that may be due to newer changes, perhaps? I’m not sure. Copying and pasting in the code above for tableView results in XCode giving some errors and it won’t compile:

    For the lines:
    if let rowData: NSDictionary = self.tableData[indexPath.row] as? NSDictionary,
    // Grab the artworkUrl60 key to get an image URL for the app’s thumbnail
    It’s complaining: “Expected ‘{‘ after ‘if’ condition”.

    And then a few lines further down for:
    trackName = rowData[“trackName”] as? String {
    It’s complaining: “Braced block of statements is an unused closure”.

    Being new to Swift I’m not sure yet how to address these but I’ll dig into it. Any suggestions would be great. Thanks for putting the work into this for us all!

  39. Hi, I think the code dosent work for XCode 7.3, would be nice it you could give some hints in fixing the problems, im new to IOS, would be nice.

Comment