The images we'll display alongside our inventory items are not stored within our Xcode app bundle, rather, if you recall, they are served up by our custom API along with item id's, names, and descriptions. We will therefore download the inventory item images and display them to the left of our item names. You can download the image assets from our Git repository for the project here, so as not to have to create mock image files with the appropriate file names yourself. After you've downloaded the files, place them in an appropriate folder alongside the other files for your inventory API.
Multithreading in Swift
All user interface related events, touches as well as view updates, occur on the main thread. Thus, if we perform operations that take a long time to complete, this will block or prevent the user interface from responding and or updating view elements—this is not desired behavior since it makes for a terrible user experience. To prevent blocking the user interface from receiving touch events while we download an image, we will download images on a secondary thread.
To get images we need their URLs. Recall that within our item model object (i.e. our PlumbingSupplyItem class) we have a property called 'bsn_image'. This property holds a URL to a unique image that corresponds to the item name in the same model object.
In our PlumbingSupplyInventoryTableViewController class, we'll thus create an operation queue that will run on a thread other than the main thread. Toward the top of that class type the following line of code:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
let backgroundQueue : dispatch_queue_t = dispatch_queue_create("com.aGupieWare.SwiftNetworking.backgroundQueue", nil) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// endpoint of corresponding supply item image // | |
let urlString : String = supplyItem.bsn_image! | |
dispatch_async(self.backgroundQueue, { () -> Void in | |
/* capture the index of the cell that is requesting this image download operation */ | |
var err : NSError? | |
/* get url for image and download raw data */ | |
if err == nil { | |
dispatch_sync(dispatch_get_main_queue(), { () -> Void in | |
/* create a UIImage object from the downloaded data */ | |
/* get the index of one of the cells that is currently being displayed */ | |
// compare the captured cell index to some current cell index // | |
// if the captured cell index is equal to some current cell index // | |
// then the cell that requested the image is still on the screen so // | |
// we present the downloaded image else we do nothing // | |
if /* compare indices */ { | |
cell.imageView.image = itemImage | |
cell.setNeedsLayout() | |
} | |
}) | |
} | |
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var capturedIndex : NSIndexPath? = indexPath.copy() as? NSIndexPath |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
let url = NSURL(string: urlString)! | |
var imageData : NSData? = NSData(contentsOfURL: url, options: NSDataReadingOptions.DataReadingMappedIfSafe, error: &err) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
let itemImage = UIImage(data:imageData!) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
let currentIndex = tableView.indexPathForCell(cell) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
currentIndex?.item == capturedIndex!.item |
When scrolling the table view, the cells are reused to improve performance. When a cell is scrolled off the screen, instead of being purged from memory it is instead reused with a new index assigned to it. The issue is that when a cell is reused and subsequently redisplayed onto the screen, the cell spawns another image download operation which puts us in a state where we have more than one operation associated with any given cell.
To understand the issue here, suppose that the user scrolled the table view enough times that a single cell has four associated image download operations. When the individual operations complete their task, they message the cell by attempting to load an image into it. The visual result of four operations loading an image into one cell is a rapid succession of images changing as each operation loads its image. This behavior is not desired. Also, since the images may not download in the same order in which they were requested, we may wind up displaying the wrong image—this is bad!
The next two lines of code set the image on the current cell and tells the cell to lay out its cell for the newly provided image. Run the app and select one of the two plumbing supply categories. You should now see a table view with images and associated text as depicted below:
Success! We've displayed the relevant thumbnail images alongside the names of the items in our inventory lists. We're almost done! In the next and final article in the series, we will construct our individual item detail views. But before we conclude, let's return to the image-display issue mentioned above.
To see the effect of allowing each operation to set a cell's image by not checking cell indices do the following. Comment out the if-statement: if currentIndex?.item == capturedIndex!.item). Now locate this line of code: self.dataSource = self.inventoryItems(data). Just below that line, load the data source multiple items by adding this line of code: for index in 1...5 { self.dataSource += self.dataSource; }.
Run the app and select one of the two categories. On an inventory items list page, quickly scroll down to the last element, you should see the images changing in rapid succession! To fix this effect, uncomment the if statement code only. Run the application again and quickly scroll to the last element. The issue should have been fixed.
It's good practice to cache data that is expensive to download. In our example, every time we scroll to a cell with an image that has already been shown, we don't request it from a local memory cache but instead download it every time. Our accompanying project repository contains sample code with a cache implementation. Simply comment out existing code and uncomment 'Version Two' sample code for a caching implementation—located in two places. If you run this implementation you will notice that the images load much faster when scrolling. This is highly desired behavior.
That's it for the present piece. In the final article in the series, we will construct our individual item detail views.
Index
Introduction
Introduction and Overview: From the Back End API to the End User Application
The Web API
Building a RESTful API in PHP, Part 1
Building a RESTful API in PHP, Part 2
The Swift Client App
Networking in Swift: Building the Swift Client, Part 1
Networking in Swift: Building the Swift Client, Part 2
Networking in Swift: Building the Swift Client, Part 3
Networking in Swift: Building the Swift Client, Part 4
Networking in Swift: Building the Swift Client, Part 5
This tutorial was authored by Stefan Agapie, a Senior iOS Software Engineer, and adapted for the present piece.
Source:http://blog.agupieware.com/2014/11/networking-in-swift-building-swift_15.html
Tidak ada komentar:
Posting Komentar