I have a tableView in a ViewController containing an array of objects Channel
; the array is populated from a json string saved in my CoreData Database
Parsing the json and creating the objects Channel
I can have typical variable like id, the name of the channel and the members of the conversation; this is the object Channel
class Channel: NSObject {
var id: String?
var name: String?
var members = [Member]()
var profileImage: UIImage?
override init() {
super.init()
}
init(id: String, name: String, members: [String]) {
super.init()
self.id = id
self.name = name
for idMember in members {
let newMember = Member(id: idMember)
self.members.append(newMember)
}
}
func loadPhoto(id: Int, completion: @escaping (Bool) -> Void) {
Helper.getImage(id: id) { (imageResult) in
if let image = imageResult {
self.profileImage = image
completion(true)
} else {
completion(false)
}
}
}
}
The variable profileImage should be taken from a Server with an API, the problem is that when the object has been created parsing the json in the function getDataFromCoreData()
the Channel
objects in the array have the variable id, name and members but still don't have the images, so my tableView will have the rows for the various channels but without the profileImage, because of the async call.
In my case to reload the table to visualize the images a made a function loadImages()
to wait for async call to finish and reload the tableView
import UIKit
import Firebase
class ConversationsViewController: UIViewController {
var channelList = [Channel]()
override func viewDidLoad() {
super.viewDidLoad()
//I get the channelList from a JSON saved like a string in my coreData with a custom function
channelList = getDataFromCoreData()
loadImages()
}
func loadImages() {
for channel in self.channelList {
channel.loadPhoto(id: channel.id) { success in
if success {
self.chatTable.reloadData()
}
}
}
}
}
extension ConversationsViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! MessageChannelTableViewCell
var channel = channelList[indexPath.row]
cell.titleLabel.text = channel.name
if let profileImage = channel.profileImage {
cell.profileImage.image = profileImage
} else {
if channel.members.count > 2 {
cell.profileImage.image = theme.groupChatImage
} else {
cell.profileImage.image = UIImage(named: "profile-icon")!
}
}
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return channelList.count
}
}
I know that the question can be long and boring, but i want to know if there is a better way to do it, and if yes you can tell me to improve my code
Maybe you could reload a single cell instead of the whole UITableView every time:
You should find a way to get the indexPathRow of every cell that needs to download the image and then:
let indexPosition = IndexPath(row: indexPathRow, section: 0)
tableView.reloadRows(at: [indexPosition], with: .none)
...take a look at the enum options for the animation to choose the one that fits your app.
EDIT: you could test in the CellForRowAtIndexPath if the image is nil and then load it from your server. In the success closure reload the cell itself... you alredy have the indexPath. use weak self to avoid retained cycles.