{"id":649,"date":"2014-06-04T11:41:23","date_gmt":"2014-06-04T17:41:23","guid":{"rendered":"http:\/\/jamesonquave.com\/blog\/?p=649"},"modified":"2015-04-16T14:56:24","modified_gmt":"2015-04-16T20:56:24","slug":"developing-ios-apps-using-swift-part-5-async-image-loading-and-caching","status":"publish","type":"post","link":"https:\/\/jamesonquave.com\/blog\/developing-ios-apps-using-swift-part-5-async-image-loading-and-caching\/","title":{"rendered":"Developing iOS Apps Using Swift Part 5 &#8211; Async image loading and caching"},"content":{"rendered":"<p><strong><i>This section completely updated to reflect changes in Xcode 6.3, as of April 16, 2015<\/i><\/strong><\/b><\/p>\n<p>In parts 1 through 4 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 inside of them. If you haven&#8217;t read that yet, check out <a title=\"Developing iOS Apps Using Swift Tutorial Part 1\" href=\"http:\/\/jamesonquave.com\/blog\/developing-ios-apps-using-swift-tutorial\/\">Part 1<\/a><\/p>\n<p><strong>This table is slow! Let&#8217;s speed it up.<\/strong><\/p>\n<p>So, we now have the functionality we&#8217;re looking for, but if you run this yourself you&#8217;ll see its super slow! The issue is that the images in these cells are downloading on the UI thread, one at a time, and they aren&#8217;t being cached at all. So let&#8217;s fix that.<\/p>\n<p>Let&#8217;s start by adding a lookup dictionary as a member for our SearchResultsViewController class:<\/p>\n<pre class=\"brush: js;\">var imageCache = [String:UIImage]()<\/pre>\n<div style=\"background-color: #f9ffd6; padding: 5px;\">\n<strong>Dictionary syntax<\/strong><br \/>\nThis is the first time we&#8217;ve seen this syntax so let me explain real quick.<br \/>\nThe type specified here is [String : UIImage], this is similar to the Objective-C NSDictionary type, a HashMap in Java, or just Hash in Ruby\/JS, but in Swift it&#8217;s very strict about the type. It takes a String as a key, and stores a UIImage as a value; no other types will be accepted.<\/p>\n<p>So for example, if I have an image named &#8220;Bob&#8221; set to the UIImage with the file name &#8220;BobsPicture.jpg&#8221;, I might add him to the dictionary like this:<\/p>\n<pre>\r\nimageCache[\"Bob\"] = UIImage(named: \"BobsPicture.jpg\")\r\n<\/pre>\n<p>The UIImage(named: &#8220;Bob.jpg&#8221;) part is just to get a UIImage from the file named Bob.jpg, this is standard Swift syntax for local files, the dictionary uses a subscript to set or retrieve it&#8217;s values. So if I wanted to get that image of Bob back out, I could just use:<\/p>\n<pre>\r\nlet imageOfBob = imageCache[\"Bob\"]\r\n<\/pre>\n<p>The reason we add the two parentheses is to call the constructor to init the empty dictionary.<br \/>\nJust like how we use APIController(), we need to use [String : UIImage]().<\/p>\n<p>Hope that makes sense, if not <a href='http:\/\/twitter.com\/jquave'>yell at me on Twitter about it<\/a>.<\/p>\n<\/div>\n<p>\nNow, in our cellForRowAtIndexPath method, we want to do quite a few things, we basically want to access the images in our cache, or download them if they don&#8217;t exist while on a background thread. After a new image is downloaded, it should be stored in to the cache so we can access it the next time the image is needed, without needing to go start another download process.<\/p>\n<p>Let&#8217;s start by moving the imgData call out of our optional chain, and instead put it inside of the block that updates the cells. We&#8217;ll do this so that we can use image data from our cache *or* perform a new download, depending on the situation. Here, we&#8217;re switching to using NSURLConnection&#8217;s sendAsynchronousRequest method in order to download the image data on a background thread.<\/p>\n<pre class=\"brush: js;\">\r\nfunc tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {\r\n    let cell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier(kCellIdentifier) as! UITableViewCell\r\n    \r\n    if let rowData: NSDictionary = self.tableData[indexPath.row] as? NSDictionary,\r\n        \/\/ Grab the artworkUrl60 key to get an image URL for the app's thumbnail\r\n        urlString = rowData[\"artworkUrl60\"] as? String,\r\n        imgURL = NSURL(string: urlString),\r\n        \/\/ Get the formatted price string for display in the subtitle\r\n        formattedPrice = rowData[\"formattedPrice\"] as? String,\r\n        \/\/ Get the track name\r\n        trackName = rowData[\"trackName\"] as? String {\r\n            \/\/ Get the formatted price string for display in the subtitle\r\n            cell.detailTextLabel?.text = formattedPrice\r\n            \/\/ Update the textLabel text to use the trackName from the API\r\n            cell.textLabel?.text = trackName\r\n            \r\n            \/\/ Start by setting the cell's image to a static file\r\n            \/\/ Without this, we will end up without an image view!\r\n            cell.imageView?.image = UIImage(named: \"Blank52\")\r\n            \r\n            \/\/ If this image is already cached, don't re-download\r\n            if let img = imageCache[urlString] {\r\n                cell.imageView?.image = img\r\n            }\r\n            else {\r\n                \/\/ The image isn't cached, download the img data\r\n                \/\/ We should perform this in a background thread\r\n                let request: NSURLRequest = NSURLRequest(URL: imgURL)\r\n                let mainQueue = NSOperationQueue.mainQueue()\r\n                NSURLConnection.sendAsynchronousRequest(request, queue: mainQueue, completionHandler: { (response, data, error) -> Void in\r\n                    if error == nil {\r\n                        \/\/ Convert the downloaded data in to a UIImage object\r\n                        let image = UIImage(data: data)\r\n                        \/\/ Store the image in to our cache\r\n                        self.imageCache[urlString] = image\r\n                        \/\/ Update the cell\r\n                        dispatch_async(dispatch_get_main_queue(), {\r\n                            if let cellToUpdate = tableView.cellForRowAtIndexPath(indexPath) {\r\n                                cellToUpdate.imageView?.image = image\r\n                            }\r\n                        })\r\n                    }\r\n                    else {\r\n                        println(\"Error: \\(error.localizedDescription)\")\r\n                    }\r\n                })\r\n            }\r\n            \r\n    }\r\n    return cell\r\n}\r\n<\/pre>\n<p>So what&#8217;s all this code mean? Let&#8217;s run through the changes real quick&#8230;<\/p>\n<h1>IMPORTANT!<\/h1>\n<p>Before we download the real image we set the cell&#8217;s placeholder image. This is required if you want the cell to actually include an image view. Otherwise even loading in our image later will not show up! Create a blank image (I&#8217;m using 52&#215;52 pixels, but it doesnt matter much) and Import it in to your project by click+dragging a file from finder in to you Xcode project, name it Blank52, and then set our cell to use this image. You can just <a href='http:\/\/jamesonquave.com\/tutImg\/Blank52.png' target='_new'>grab my image file here<\/a> (right-click and save as&#8230;)<\/p>\n<pre class=\"brush: js; first-line: 10;\">cell.imageView?.image = UIImage(named: \"Blank52\")<\/pre>\n<p>Now our app should be less prone to crashing, and will now always have an image cell.<\/p>\n<p><strong>Put Image Downloading On A Background Thread<\/strong><\/p>\n<p>Now we need to check our image cache to see if this image has been downloaded before. We use the optional binding to check for the existence of our image in the cache:<\/p>\n<pre class=\"brush: js; first-line: 24;\">\r\nif let img = imageCache[urlString] {\r\n    cell.imageView?.image = img\r\n}<\/pre>\n<p>If the image doesn&#8217;t exist (and initially it won&#8217;t) we need to download it. There are a couple of ways to initiate a download. Previously we used NSData&#8217;s dataWithContentsOfFile, but here we&#8217;re going to switch to NSURLConnection&#8217;s sendAsynchronousRequest, more similar to how our API works. The reason being is that we want to send off lots of small requests for images real quick, and we want to do it in the background. So let&#8217;s do that.<\/p>\n<p>Look at the line with a call to NSURLConnection&#8217;s static method sendAsynchronousRequest, which takes a function\/closure as a parameter for completionHandler. The lines after this call represent a function that is executed only *after* the async request returns.<\/p>\n<pre class=\"brush: js;\">\r\nNSURLConnection.sendAsynchronousRequest(request, queue: mainQueue, completionHandler: { (response, data, error) -> Void in\r\n    if error == nil {\r\n        \/\/ Convert the downloaded data in to a UIImage object\r\n        let image = UIImage(data: data)\r\n        \/\/ Store the image in to our cache\r\n        self.imageCache[urlString] = image\r\n        \/\/ Update the cell\r\n        dispatch_async(dispatch_get_main_queue(), {\r\n            if let cellToUpdate = tableView.cellForRowAtIndexPath(indexPath) {\r\n                cellToUpdate.imageView?.image = image\r\n            }\r\n        })\r\n    }\r\n    else {\r\n        println(\"Error: \\(error.localizedDescription)\")\r\n    }\r\n})\r\n<\/pre>\n<p>Inside the block we will get back a few variables: response, data, and error.<\/p>\n<p>If an error exists, proceed to create a UIImage from the data using the UIImage(data: data) constructor.<\/p>\n<p>Next, we set the image cache to save our fancy new image with a key of the image URL. Using the URL as the key means we can find the image in our dictionary any time it pops up, even in a completely different context.<\/p>\n<pre class=\"brush: js; first-line: 38;\">\r\nself.imageCache[urlString] = image\r\n<\/pre>\n<p>Finally, we set the cell image, on the UI thread (main queue):<\/p>\n<pre class=\"brush: js; first-line: 39;\">\r\ndispatch_async(dispatch_get_main_queue(), {\r\n    if let cellToUpdate = tableView.cellForRowAtIndexPath(indexPath) {\r\n        cellToUpdate.imageView?.image = image\r\n    }\r\n})\r\n<\/pre>\n<p>You&#8217;ll notice we&#8217;re also using the cellForRowAtIndexPath() method here. The reason we use this is because sometime&#8217;s the cell that this code was running for may no longer be visible, and will have been re-used. So, to avoid setting an image on an unintended cell, we retrieve the cell from the tableView, based on the index path. If this comes back nil, then we know the cell is no longer visible and can skip the update.<\/p>\n<p>Okay! Give the project a run and see our amazing new blazingly fast, silky smooth table view!<\/p>\n<p>The complete code up to this point is available on <a title=\"Code for the Swift Tutorial on jamesonquave.com  http:\/\/jamesonquave.com Part 5\" href=\"https:\/\/github.com\/jquave\/Swift-Tutorial\/tree\/Part5\">Github as a branch &#8216;Part5<\/a>&#8216;.<\/p>\n<p>To get updates on this tutorial in your email, <a title=\"Sign Up\" href=\"http:\/\/jamesonquave.us6.list-manage.com\/subscribe\/post?u=1d2576bf288fe2fd7fa71bd20&amp;id=6c787ed58a\">subscribe here<\/a>. I&#8217;m also working on a book filled with practical tips and tutorials on working with Swift to develop iOS 8 apps. It&#8217;s now available for <a href='http:\/\/jamesonquave.com\/swiftebook'>pre-order here<\/a>.<\/p>\n<p><a href='http:\/\/jamesonquave.com\/blog\/developing-ios-8-apps-using-swift-interaction-with-multiple-views\/'>Part 6<\/a> will focus on adding a new View Controller that we can open up to, loading it with iTunes data.<\/p>\n<p><a href='http:\/\/jamesonquave.com\/blog\/developing-ios-8-apps-using-swift-interaction-with-multiple-views\/'>Go to part 6 now -><\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>This section completely updated to reflect changes in Xcode 6.3, as of April 16, 2015 In parts 1 through 4 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 inside of them. If you haven&#8217;t read that&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_links_to":"","_links_to_target":""},"categories":[26,10,32],"tags":[15,34,33,36],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v19.13 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Developing iOS Apps Using Swift Part 5 - Async image loading and caching - Jameson Quave<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/jamesonquave.com\/blog\/developing-ios-apps-using-swift-part-5-async-image-loading-and-caching\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Developing iOS Apps Using Swift Part 5 - Async image loading and caching - Jameson Quave\" \/>\n<meta property=\"og:description\" content=\"This section completely updated to reflect changes in Xcode 6.3, as of April 16, 2015 In parts 1 through 4 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 inside of them. If you haven&#8217;t read that...\" \/>\n<meta property=\"og:url\" content=\"https:\/\/jamesonquave.com\/blog\/developing-ios-apps-using-swift-part-5-async-image-loading-and-caching\/\" \/>\n<meta property=\"og:site_name\" content=\"Jameson Quave\" \/>\n<meta property=\"article:published_time\" content=\"2014-06-04T17:41:23+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2015-04-16T20:56:24+00:00\" \/>\n<meta name=\"author\" content=\"Jameson Quave\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Jameson Quave\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"7 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/jamesonquave.com\/blog\/developing-ios-apps-using-swift-part-5-async-image-loading-and-caching\/\",\"url\":\"https:\/\/jamesonquave.com\/blog\/developing-ios-apps-using-swift-part-5-async-image-loading-and-caching\/\",\"name\":\"Developing iOS Apps Using Swift Part 5 - Async image loading and caching - Jameson Quave\",\"isPartOf\":{\"@id\":\"https:\/\/jamesonquave.com\/blog\/#website\"},\"datePublished\":\"2014-06-04T17:41:23+00:00\",\"dateModified\":\"2015-04-16T20:56:24+00:00\",\"author\":{\"@id\":\"https:\/\/jamesonquave.com\/blog\/#\/schema\/person\/db6184f355c7f4e3b876d0f228c2fcfc\"},\"breadcrumb\":{\"@id\":\"https:\/\/jamesonquave.com\/blog\/developing-ios-apps-using-swift-part-5-async-image-loading-and-caching\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/jamesonquave.com\/blog\/developing-ios-apps-using-swift-part-5-async-image-loading-and-caching\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/jamesonquave.com\/blog\/developing-ios-apps-using-swift-part-5-async-image-loading-and-caching\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/jamesonquave.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Developing iOS Apps Using Swift Part 5 &#8211; Async image loading and caching\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/jamesonquave.com\/blog\/#website\",\"url\":\"https:\/\/jamesonquave.com\/blog\/\",\"name\":\"Jameson Quave\",\"description\":\"Using computer technology to educate, and improve lives.\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/jamesonquave.com\/blog\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/jamesonquave.com\/blog\/#\/schema\/person\/db6184f355c7f4e3b876d0f228c2fcfc\",\"name\":\"Jameson Quave\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/jamesonquave.com\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/d9786c83345117d560bbeab0e1f26814?s=96&d=retro&r=pg\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/d9786c83345117d560bbeab0e1f26814?s=96&d=retro&r=pg\",\"caption\":\"Jameson Quave\"},\"sameAs\":[\"http:\/\/jamesonquave.com\"],\"url\":\"https:\/\/jamesonquave.com\/blog\/author\/jquave\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Developing iOS Apps Using Swift Part 5 - Async image loading and caching - Jameson Quave","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/jamesonquave.com\/blog\/developing-ios-apps-using-swift-part-5-async-image-loading-and-caching\/","og_locale":"en_US","og_type":"article","og_title":"Developing iOS Apps Using Swift Part 5 - Async image loading and caching - Jameson Quave","og_description":"This section completely updated to reflect changes in Xcode 6.3, as of April 16, 2015 In parts 1 through 4 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 inside of them. If you haven&#8217;t read that...","og_url":"https:\/\/jamesonquave.com\/blog\/developing-ios-apps-using-swift-part-5-async-image-loading-and-caching\/","og_site_name":"Jameson Quave","article_published_time":"2014-06-04T17:41:23+00:00","article_modified_time":"2015-04-16T20:56:24+00:00","author":"Jameson Quave","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Jameson Quave","Est. reading time":"7 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/jamesonquave.com\/blog\/developing-ios-apps-using-swift-part-5-async-image-loading-and-caching\/","url":"https:\/\/jamesonquave.com\/blog\/developing-ios-apps-using-swift-part-5-async-image-loading-and-caching\/","name":"Developing iOS Apps Using Swift Part 5 - Async image loading and caching - Jameson Quave","isPartOf":{"@id":"https:\/\/jamesonquave.com\/blog\/#website"},"datePublished":"2014-06-04T17:41:23+00:00","dateModified":"2015-04-16T20:56:24+00:00","author":{"@id":"https:\/\/jamesonquave.com\/blog\/#\/schema\/person\/db6184f355c7f4e3b876d0f228c2fcfc"},"breadcrumb":{"@id":"https:\/\/jamesonquave.com\/blog\/developing-ios-apps-using-swift-part-5-async-image-loading-and-caching\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/jamesonquave.com\/blog\/developing-ios-apps-using-swift-part-5-async-image-loading-and-caching\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/jamesonquave.com\/blog\/developing-ios-apps-using-swift-part-5-async-image-loading-and-caching\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/jamesonquave.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Developing iOS Apps Using Swift Part 5 &#8211; Async image loading and caching"}]},{"@type":"WebSite","@id":"https:\/\/jamesonquave.com\/blog\/#website","url":"https:\/\/jamesonquave.com\/blog\/","name":"Jameson Quave","description":"Using computer technology to educate, and improve lives.","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/jamesonquave.com\/blog\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/jamesonquave.com\/blog\/#\/schema\/person\/db6184f355c7f4e3b876d0f228c2fcfc","name":"Jameson Quave","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/jamesonquave.com\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/d9786c83345117d560bbeab0e1f26814?s=96&d=retro&r=pg","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/d9786c83345117d560bbeab0e1f26814?s=96&d=retro&r=pg","caption":"Jameson Quave"},"sameAs":["http:\/\/jamesonquave.com"],"url":"https:\/\/jamesonquave.com\/blog\/author\/jquave\/"}]}},"_links":{"self":[{"href":"https:\/\/jamesonquave.com\/blog\/wp-json\/wp\/v2\/posts\/649"}],"collection":[{"href":"https:\/\/jamesonquave.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/jamesonquave.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/jamesonquave.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/jamesonquave.com\/blog\/wp-json\/wp\/v2\/comments?post=649"}],"version-history":[{"count":47,"href":"https:\/\/jamesonquave.com\/blog\/wp-json\/wp\/v2\/posts\/649\/revisions"}],"predecessor-version":[{"id":1769,"href":"https:\/\/jamesonquave.com\/blog\/wp-json\/wp\/v2\/posts\/649\/revisions\/1769"}],"wp:attachment":[{"href":"https:\/\/jamesonquave.com\/blog\/wp-json\/wp\/v2\/media?parent=649"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/jamesonquave.com\/blog\/wp-json\/wp\/v2\/categories?post=649"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/jamesonquave.com\/blog\/wp-json\/wp\/v2\/tags?post=649"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}