Fogmeister Fogmeister - 6 months ago 8
Swift Question

Updating a property in a struct inside an array

In my app I download a load of JSON.

I then store that as an array of structs and use that to populate a

UITableView
.

One of the properties of the struct is an
NSURL
for an image. Another property is an optional
UIImage
.

The struct has a mutating function
downloadImage
which uses the URL to download the image and store it in its property.

Like this...

struct SearchItem {
// other properties...
let iconURL: NSURL
var icon: UIImage?

mutating func downloadImage() -> Task<UIImage> {
let tsc = TaskCompletionSource<UIImage>()

NSURLSession.sharedSession().downloadTaskWithURL(iconURL) {
(location, response, error) in
if let location = location,
data = NSData(contentsOfURL: location),
image = UIImage(data: data) {
self.icon = image
tsc.setResult(image)
return
}

tsc.setError(NSError(domain: "", code: 1, userInfo: nil))
}.resume()

return tsc.task
}
}


The problem I'm having is this. (and I have been stumped by this in the past).

I have an array
[SearchItem]
that I use to populate the tableview.

In
cellForRow
I have the code...
if let searchItem = items[indexPath.row]...


It then checks if the image is nil and downloads...

if let image = searchItem.icon {
cell.imageView.image = image
} else {
searchItem.downloadImage().continueOnSuccessWith(Executor.MainThread) {
_ in
tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: .None)
}
}


But this never goes through to put the image into the cell. This is because the
SearchItem
is struct and so
pass-by-value
. So the search item that I am downloading the image for is not the same
SearchItem
as the one stored in the array.

How can I ensure that the image that is downloaded is then stored into the
SearchItem
inside the actual array?

Answer

Use classes.

You're getting a copy of searchItem in your cellForRow method. Whatever you do to this, will be done only to that copy. What you actually want is for the changes you make to that copy to be applied to the version in the array.

Therefore you want reference semantics, therefore use classes.

You could dance around re-inserting the updated copy into the original array if you liked, but what does that gain you besides a line of extra code and probably some other problems.