Index out of range exception using DispatchQueue





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty{ height:90px;width:728px;box-sizing:border-box;
}







1















I have the following code which shows a list of quizzes in a UITableView. The problem is that to show the images I call my method prepareImages and I get an index out of range exception when filling the cells in tableView function because it appears that the quizzesImages array is empty (print(self.quizzesImages.count) shows 0), I know it has something to do with how I put the threads to work but I don't see where I am going wrong.



import UIKit
// Estructura del JSON que devuelve la URL
struct ResponseObject : Codable {
let quizzes : [Quiz]?
let pageno : Int?
let nextUrl : String?
}

class QuizzesTableViewController: UITableViewController {

// Aquí se guardan los quizzes cargados de la URL
var totalQuizzes = [Quiz]()
var quizzesImages = [UIImage]()

override func viewDidLoad() {
super.viewDidLoad()
self.tableView.rowHeight = 90.0
navigationController?.navigationBar.prefersLargeTitles = true
downloadQuizzes()
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}

func prepareImages(){
for i in 0...self.totalQuizzes.count-1{

let imageUrlString = self.totalQuizzes[i].attachment?.url
let imageUrl:URL = URL(string: imageUrlString!)!
print(imageUrl)

// Start background thread so that image loading does not make app unresponsive
DispatchQueue.global(qos: .userInitiated).async {
let imageData:NSData = NSData(contentsOf: imageUrl)!

// When from background thread, UI needs to be updated on main_queue
DispatchQueue.main.async {
let image = UIImage(data: imageData as Data)
print("hola")
self.quizzesImages.append(image!)
}
}
}

}

func downloadQuizzes(){
let QUIZZES_URL = "https://quiz2019.herokuapp.com/api/quizzes?token=945d3bf7d4c709d69940"
if let url = URL(string: QUIZZES_URL){
let queue = DispatchQueue(label: "download quizzes queue")
queue.async {
DispatchQueue.main.async {
UIApplication.shared.isNetworkActivityIndicatorVisible = true
}
defer{
DispatchQueue.main.async {
UIApplication.shared.isNetworkActivityIndicatorVisible = false
}
}
let data = try? Data(contentsOf: url, options: .alwaysMapped)
let decoder = JSONDecoder()
do{
let response = try decoder.decode(ResponseObject.self, from: data!)
DispatchQueue.main.async {
if (response.quizzes!.count != 0){
self.totalQuizzes.append(contentsOf: response.quizzes!)
self.prepareImages()
print(self.totalQuizzes.count)
print(self.quizzesImages.count)
self.tableView.reloadData()
}
}
}
catch {
print(error)
}
}
}
}
// MARK: - Table view data source

override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return totalQuizzes.count
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Quiz", for: indexPath) as! QuizTableViewCell

let quiz = totalQuizzes[indexPath.row]
let images = quizzesImages[indexPath.row]
cell.authorLabel?.text = quiz.author?.username
cell.quizLabel?.text = quiz.question
cell.quizImage?.image = images
return cell
}}


Thanks in advance for the help!










share|improve this question

























  • You check if esponse.quizzes!.count != 0 is true? Try print response.quizzes

    – Augusto
    Nov 23 '18 at 16:40











  • @Augusto yes, it is, totalQuizzes has 10 elements after running so the code below gets executed

    – martingoldthen
    Nov 23 '18 at 16:49











  • @martingoldthen can you give what is inside Quiz

    – Harshal Bhavsar
    Nov 23 '18 at 16:50











  • @martingoldthen give the entity class Defination of Quiz so can run and see it

    – Harshal Bhavsar
    Nov 23 '18 at 16:53






  • 1





    @HarshalBhavsar This is Quiz.swift file import Foundation struct Quiz : Codable { let id : Int? let question : String? let author : Author? let attachment : Attachment? let favourite : Bool? let tips : [String]? struct Author: Codable { let id: Int? let isAdmin : Bool? let username : String? } struct Attachment : Codable { let filename : String? let mime : String? let url : String? } }

    – martingoldthen
    Nov 23 '18 at 16:53




















1















I have the following code which shows a list of quizzes in a UITableView. The problem is that to show the images I call my method prepareImages and I get an index out of range exception when filling the cells in tableView function because it appears that the quizzesImages array is empty (print(self.quizzesImages.count) shows 0), I know it has something to do with how I put the threads to work but I don't see where I am going wrong.



import UIKit
// Estructura del JSON que devuelve la URL
struct ResponseObject : Codable {
let quizzes : [Quiz]?
let pageno : Int?
let nextUrl : String?
}

class QuizzesTableViewController: UITableViewController {

// Aquí se guardan los quizzes cargados de la URL
var totalQuizzes = [Quiz]()
var quizzesImages = [UIImage]()

override func viewDidLoad() {
super.viewDidLoad()
self.tableView.rowHeight = 90.0
navigationController?.navigationBar.prefersLargeTitles = true
downloadQuizzes()
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}

func prepareImages(){
for i in 0...self.totalQuizzes.count-1{

let imageUrlString = self.totalQuizzes[i].attachment?.url
let imageUrl:URL = URL(string: imageUrlString!)!
print(imageUrl)

// Start background thread so that image loading does not make app unresponsive
DispatchQueue.global(qos: .userInitiated).async {
let imageData:NSData = NSData(contentsOf: imageUrl)!

// When from background thread, UI needs to be updated on main_queue
DispatchQueue.main.async {
let image = UIImage(data: imageData as Data)
print("hola")
self.quizzesImages.append(image!)
}
}
}

}

func downloadQuizzes(){
let QUIZZES_URL = "https://quiz2019.herokuapp.com/api/quizzes?token=945d3bf7d4c709d69940"
if let url = URL(string: QUIZZES_URL){
let queue = DispatchQueue(label: "download quizzes queue")
queue.async {
DispatchQueue.main.async {
UIApplication.shared.isNetworkActivityIndicatorVisible = true
}
defer{
DispatchQueue.main.async {
UIApplication.shared.isNetworkActivityIndicatorVisible = false
}
}
let data = try? Data(contentsOf: url, options: .alwaysMapped)
let decoder = JSONDecoder()
do{
let response = try decoder.decode(ResponseObject.self, from: data!)
DispatchQueue.main.async {
if (response.quizzes!.count != 0){
self.totalQuizzes.append(contentsOf: response.quizzes!)
self.prepareImages()
print(self.totalQuizzes.count)
print(self.quizzesImages.count)
self.tableView.reloadData()
}
}
}
catch {
print(error)
}
}
}
}
// MARK: - Table view data source

override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return totalQuizzes.count
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Quiz", for: indexPath) as! QuizTableViewCell

let quiz = totalQuizzes[indexPath.row]
let images = quizzesImages[indexPath.row]
cell.authorLabel?.text = quiz.author?.username
cell.quizLabel?.text = quiz.question
cell.quizImage?.image = images
return cell
}}


Thanks in advance for the help!










share|improve this question

























  • You check if esponse.quizzes!.count != 0 is true? Try print response.quizzes

    – Augusto
    Nov 23 '18 at 16:40











  • @Augusto yes, it is, totalQuizzes has 10 elements after running so the code below gets executed

    – martingoldthen
    Nov 23 '18 at 16:49











  • @martingoldthen can you give what is inside Quiz

    – Harshal Bhavsar
    Nov 23 '18 at 16:50











  • @martingoldthen give the entity class Defination of Quiz so can run and see it

    – Harshal Bhavsar
    Nov 23 '18 at 16:53






  • 1





    @HarshalBhavsar This is Quiz.swift file import Foundation struct Quiz : Codable { let id : Int? let question : String? let author : Author? let attachment : Attachment? let favourite : Bool? let tips : [String]? struct Author: Codable { let id: Int? let isAdmin : Bool? let username : String? } struct Attachment : Codable { let filename : String? let mime : String? let url : String? } }

    – martingoldthen
    Nov 23 '18 at 16:53
















1












1








1








I have the following code which shows a list of quizzes in a UITableView. The problem is that to show the images I call my method prepareImages and I get an index out of range exception when filling the cells in tableView function because it appears that the quizzesImages array is empty (print(self.quizzesImages.count) shows 0), I know it has something to do with how I put the threads to work but I don't see where I am going wrong.



import UIKit
// Estructura del JSON que devuelve la URL
struct ResponseObject : Codable {
let quizzes : [Quiz]?
let pageno : Int?
let nextUrl : String?
}

class QuizzesTableViewController: UITableViewController {

// Aquí se guardan los quizzes cargados de la URL
var totalQuizzes = [Quiz]()
var quizzesImages = [UIImage]()

override func viewDidLoad() {
super.viewDidLoad()
self.tableView.rowHeight = 90.0
navigationController?.navigationBar.prefersLargeTitles = true
downloadQuizzes()
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}

func prepareImages(){
for i in 0...self.totalQuizzes.count-1{

let imageUrlString = self.totalQuizzes[i].attachment?.url
let imageUrl:URL = URL(string: imageUrlString!)!
print(imageUrl)

// Start background thread so that image loading does not make app unresponsive
DispatchQueue.global(qos: .userInitiated).async {
let imageData:NSData = NSData(contentsOf: imageUrl)!

// When from background thread, UI needs to be updated on main_queue
DispatchQueue.main.async {
let image = UIImage(data: imageData as Data)
print("hola")
self.quizzesImages.append(image!)
}
}
}

}

func downloadQuizzes(){
let QUIZZES_URL = "https://quiz2019.herokuapp.com/api/quizzes?token=945d3bf7d4c709d69940"
if let url = URL(string: QUIZZES_URL){
let queue = DispatchQueue(label: "download quizzes queue")
queue.async {
DispatchQueue.main.async {
UIApplication.shared.isNetworkActivityIndicatorVisible = true
}
defer{
DispatchQueue.main.async {
UIApplication.shared.isNetworkActivityIndicatorVisible = false
}
}
let data = try? Data(contentsOf: url, options: .alwaysMapped)
let decoder = JSONDecoder()
do{
let response = try decoder.decode(ResponseObject.self, from: data!)
DispatchQueue.main.async {
if (response.quizzes!.count != 0){
self.totalQuizzes.append(contentsOf: response.quizzes!)
self.prepareImages()
print(self.totalQuizzes.count)
print(self.quizzesImages.count)
self.tableView.reloadData()
}
}
}
catch {
print(error)
}
}
}
}
// MARK: - Table view data source

override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return totalQuizzes.count
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Quiz", for: indexPath) as! QuizTableViewCell

let quiz = totalQuizzes[indexPath.row]
let images = quizzesImages[indexPath.row]
cell.authorLabel?.text = quiz.author?.username
cell.quizLabel?.text = quiz.question
cell.quizImage?.image = images
return cell
}}


Thanks in advance for the help!










share|improve this question
















I have the following code which shows a list of quizzes in a UITableView. The problem is that to show the images I call my method prepareImages and I get an index out of range exception when filling the cells in tableView function because it appears that the quizzesImages array is empty (print(self.quizzesImages.count) shows 0), I know it has something to do with how I put the threads to work but I don't see where I am going wrong.



import UIKit
// Estructura del JSON que devuelve la URL
struct ResponseObject : Codable {
let quizzes : [Quiz]?
let pageno : Int?
let nextUrl : String?
}

class QuizzesTableViewController: UITableViewController {

// Aquí se guardan los quizzes cargados de la URL
var totalQuizzes = [Quiz]()
var quizzesImages = [UIImage]()

override func viewDidLoad() {
super.viewDidLoad()
self.tableView.rowHeight = 90.0
navigationController?.navigationBar.prefersLargeTitles = true
downloadQuizzes()
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}

func prepareImages(){
for i in 0...self.totalQuizzes.count-1{

let imageUrlString = self.totalQuizzes[i].attachment?.url
let imageUrl:URL = URL(string: imageUrlString!)!
print(imageUrl)

// Start background thread so that image loading does not make app unresponsive
DispatchQueue.global(qos: .userInitiated).async {
let imageData:NSData = NSData(contentsOf: imageUrl)!

// When from background thread, UI needs to be updated on main_queue
DispatchQueue.main.async {
let image = UIImage(data: imageData as Data)
print("hola")
self.quizzesImages.append(image!)
}
}
}

}

func downloadQuizzes(){
let QUIZZES_URL = "https://quiz2019.herokuapp.com/api/quizzes?token=945d3bf7d4c709d69940"
if let url = URL(string: QUIZZES_URL){
let queue = DispatchQueue(label: "download quizzes queue")
queue.async {
DispatchQueue.main.async {
UIApplication.shared.isNetworkActivityIndicatorVisible = true
}
defer{
DispatchQueue.main.async {
UIApplication.shared.isNetworkActivityIndicatorVisible = false
}
}
let data = try? Data(contentsOf: url, options: .alwaysMapped)
let decoder = JSONDecoder()
do{
let response = try decoder.decode(ResponseObject.self, from: data!)
DispatchQueue.main.async {
if (response.quizzes!.count != 0){
self.totalQuizzes.append(contentsOf: response.quizzes!)
self.prepareImages()
print(self.totalQuizzes.count)
print(self.quizzesImages.count)
self.tableView.reloadData()
}
}
}
catch {
print(error)
}
}
}
}
// MARK: - Table view data source

override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return totalQuizzes.count
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Quiz", for: indexPath) as! QuizTableViewCell

let quiz = totalQuizzes[indexPath.row]
let images = quizzesImages[indexPath.row]
cell.authorLabel?.text = quiz.author?.username
cell.quizLabel?.text = quiz.question
cell.quizImage?.image = images
return cell
}}


Thanks in advance for the help!







ios swift grand-central-dispatch






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 23 '18 at 20:26









zekel

5,17664991




5,17664991










asked Nov 23 '18 at 16:25









martingoldthenmartingoldthen

154




154













  • You check if esponse.quizzes!.count != 0 is true? Try print response.quizzes

    – Augusto
    Nov 23 '18 at 16:40











  • @Augusto yes, it is, totalQuizzes has 10 elements after running so the code below gets executed

    – martingoldthen
    Nov 23 '18 at 16:49











  • @martingoldthen can you give what is inside Quiz

    – Harshal Bhavsar
    Nov 23 '18 at 16:50











  • @martingoldthen give the entity class Defination of Quiz so can run and see it

    – Harshal Bhavsar
    Nov 23 '18 at 16:53






  • 1





    @HarshalBhavsar This is Quiz.swift file import Foundation struct Quiz : Codable { let id : Int? let question : String? let author : Author? let attachment : Attachment? let favourite : Bool? let tips : [String]? struct Author: Codable { let id: Int? let isAdmin : Bool? let username : String? } struct Attachment : Codable { let filename : String? let mime : String? let url : String? } }

    – martingoldthen
    Nov 23 '18 at 16:53





















  • You check if esponse.quizzes!.count != 0 is true? Try print response.quizzes

    – Augusto
    Nov 23 '18 at 16:40











  • @Augusto yes, it is, totalQuizzes has 10 elements after running so the code below gets executed

    – martingoldthen
    Nov 23 '18 at 16:49











  • @martingoldthen can you give what is inside Quiz

    – Harshal Bhavsar
    Nov 23 '18 at 16:50











  • @martingoldthen give the entity class Defination of Quiz so can run and see it

    – Harshal Bhavsar
    Nov 23 '18 at 16:53






  • 1





    @HarshalBhavsar This is Quiz.swift file import Foundation struct Quiz : Codable { let id : Int? let question : String? let author : Author? let attachment : Attachment? let favourite : Bool? let tips : [String]? struct Author: Codable { let id: Int? let isAdmin : Bool? let username : String? } struct Attachment : Codable { let filename : String? let mime : String? let url : String? } }

    – martingoldthen
    Nov 23 '18 at 16:53



















You check if esponse.quizzes!.count != 0 is true? Try print response.quizzes

– Augusto
Nov 23 '18 at 16:40





You check if esponse.quizzes!.count != 0 is true? Try print response.quizzes

– Augusto
Nov 23 '18 at 16:40













@Augusto yes, it is, totalQuizzes has 10 elements after running so the code below gets executed

– martingoldthen
Nov 23 '18 at 16:49





@Augusto yes, it is, totalQuizzes has 10 elements after running so the code below gets executed

– martingoldthen
Nov 23 '18 at 16:49













@martingoldthen can you give what is inside Quiz

– Harshal Bhavsar
Nov 23 '18 at 16:50





@martingoldthen can you give what is inside Quiz

– Harshal Bhavsar
Nov 23 '18 at 16:50













@martingoldthen give the entity class Defination of Quiz so can run and see it

– Harshal Bhavsar
Nov 23 '18 at 16:53





@martingoldthen give the entity class Defination of Quiz so can run and see it

– Harshal Bhavsar
Nov 23 '18 at 16:53




1




1





@HarshalBhavsar This is Quiz.swift file import Foundation struct Quiz : Codable { let id : Int? let question : String? let author : Author? let attachment : Attachment? let favourite : Bool? let tips : [String]? struct Author: Codable { let id: Int? let isAdmin : Bool? let username : String? } struct Attachment : Codable { let filename : String? let mime : String? let url : String? } }

– martingoldthen
Nov 23 '18 at 16:53







@HarshalBhavsar This is Quiz.swift file import Foundation struct Quiz : Codable { let id : Int? let question : String? let author : Author? let attachment : Attachment? let favourite : Bool? let tips : [String]? struct Author: Codable { let id: Int? let isAdmin : Bool? let username : String? } struct Attachment : Codable { let filename : String? let mime : String? let url : String? } }

– martingoldthen
Nov 23 '18 at 16:53














2 Answers
2






active

oldest

votes


















1














See this screen shot form my playground



I tried to run your code in playground and managed to get it work with some changes please see the following.




Note




To make it run in playground i have to make some assumptions just replace your core logic with my one.



Happy Coding 😀



import UIKit
// Estructura del JSON que devuelve la URL

struct ResponseObject: Codable {
let quizzes: [Quiz]?
let pageno: Int?
let nextURL: String?

enum CodingKeys: String, CodingKey {
case quizzes, pageno
case nextURL = "nextUrl"
}
}

struct Quiz: Codable {
let id: Int?
let question: String?
let author: Author?
let attachment: Attachment?
let favourite: Bool?
let tips: [String]?
}

struct Attachment: Codable {
let filename: String?
let mime: MIME?
let url: String?
}

enum MIME: String, Codable {
case imageJPEG = "image/jpeg"
}

struct Author: Codable {
let id: Int?
let isAdmin: Bool?
let username: String?
}


class QuizzesTableViewController: UITableViewController {

// Aquí se guardan los quizzes cargados de la URL
var totalQuizzes = [Quiz]()
var quizzesImages = [UIImage]()

override func viewDidLoad() {
super.viewDidLoad()
self.tableView.rowHeight = 90.0
navigationController?.navigationBar.prefersLargeTitles = true
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Quiz")
self.tableView.dataSource = self
self.tableView.delegate = self
downloadQuizzes()
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}

func prepareImages(){
print("in prepare images")
// Start background thread so that image loading does not make app unresponsive
DispatchQueue.global(qos: .userInitiated).async {
for i in 0...self.totalQuizzes.count-1 {

let imageUrlString = self.totalQuizzes[i].attachment?.url
let imageUrl:URL = URL(string: imageUrlString!)!
print(imageUrl)

let imageData:NSData = NSData(contentsOf: imageUrl)!
let image = UIImage(data: imageData as Data)
print("hola (i)")
self.quizzesImages.append(image!)
print(self.quizzesImages.count)
// When from background thread, UI needs to be updated on main_queue
}
DispatchQueue.main.async {
print(self.quizzesImages.count)
self.tableView.reloadData()
UIApplication.shared.isNetworkActivityIndicatorVisible = false
}
}
}

func downloadQuizzes(){
let QUIZZES_URL = "https://quiz2019.herokuapp.com/api/quizzes?token=945d3bf7d4c709d69940"
if let url = URL(string: QUIZZES_URL){
let queue = DispatchQueue(label: "download quizzes queue")
queue.async {
DispatchQueue.main.async {
UIApplication.shared.isNetworkActivityIndicatorVisible = true
}
let data = try? Data(contentsOf: url, options: .alwaysMapped)
let decoder = JSONDecoder()
do{
let response = try decoder.decode(ResponseObject.self, from: data!)
DispatchQueue.main.async {
if (response.quizzes!.count != 0){
self.totalQuizzes.append(contentsOf: response.quizzes!)
self.prepareImages()
print(self.totalQuizzes.count)
}
}
}
catch {
print(error)
}
}
}
}
// MARK: - Table view data source

override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
print("num of rows (totalQuizzes.count)")
return totalQuizzes.count
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
print("in cellForRowAt (indexPath.row)")
let cell = tableView.dequeueReusableCell(withIdentifier: "Quiz")!
let quiz = totalQuizzes[indexPath.row]
let images = quizzesImages[indexPath.row]
cell.textLabel?.text = "(quiz.author?.username) (quiz.question)"
cell.imageView?.image = images
return cell
}}





share|improve this answer


























  • This worked, I had to self.tableView.reloadData() on prepareImages. Thank you very much

    – martingoldthen
    Nov 23 '18 at 17:47













  • Yes and that too after ending the for loop

    – Harshal Bhavsar
    Nov 23 '18 at 17:48











  • Feel free to upvote also if it helped. Once Again Happy coding

    – Harshal Bhavsar
    Nov 23 '18 at 17:49











  • I did, but my reputation is under 15 and it doesn't show, sorry

    – martingoldthen
    Nov 23 '18 at 17:53











  • No Worries Happy Coding. Keep Learning

    – Harshal Bhavsar
    Nov 23 '18 at 17:55



















1














Since you didn't say what line the exception is happening on, my best guess is that it's happening on this line.



let images = quizzesImages[indexPath.row]


The issue is in downloadQuizzes. In there you call prepareImages then a couple lines later call reloadData. Since prepareImages does an async task, it isn't blocking downloadQuizzes from finishing running. This causes reloadData to be called before the async part of prepareImages is finished.



You'll need to make sure prepareImages is finished running before calling reloadData.



Another option is to use a library like Kingfisher that will only download the images when they need to be displayed in a cell rather then downloading them all and slowing down showing the table.





Additional Note:



You shouldn't be using Data(contentsOf:) to load remote content, URLSession is the correct option for it.






share|improve this answer
























  • Yes! Taking out all the async tasks in prepareImages makes it work! How would I do it so reloadData isn't called until prepareImages is done? Thank you so much!

    – martingoldthen
    Nov 23 '18 at 17:10












Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53450085%2findex-out-of-range-exception-using-dispatchqueue%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























2 Answers
2






active

oldest

votes








2 Answers
2






active

oldest

votes









active

oldest

votes






active

oldest

votes









1














See this screen shot form my playground



I tried to run your code in playground and managed to get it work with some changes please see the following.




Note




To make it run in playground i have to make some assumptions just replace your core logic with my one.



Happy Coding 😀



import UIKit
// Estructura del JSON que devuelve la URL

struct ResponseObject: Codable {
let quizzes: [Quiz]?
let pageno: Int?
let nextURL: String?

enum CodingKeys: String, CodingKey {
case quizzes, pageno
case nextURL = "nextUrl"
}
}

struct Quiz: Codable {
let id: Int?
let question: String?
let author: Author?
let attachment: Attachment?
let favourite: Bool?
let tips: [String]?
}

struct Attachment: Codable {
let filename: String?
let mime: MIME?
let url: String?
}

enum MIME: String, Codable {
case imageJPEG = "image/jpeg"
}

struct Author: Codable {
let id: Int?
let isAdmin: Bool?
let username: String?
}


class QuizzesTableViewController: UITableViewController {

// Aquí se guardan los quizzes cargados de la URL
var totalQuizzes = [Quiz]()
var quizzesImages = [UIImage]()

override func viewDidLoad() {
super.viewDidLoad()
self.tableView.rowHeight = 90.0
navigationController?.navigationBar.prefersLargeTitles = true
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Quiz")
self.tableView.dataSource = self
self.tableView.delegate = self
downloadQuizzes()
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}

func prepareImages(){
print("in prepare images")
// Start background thread so that image loading does not make app unresponsive
DispatchQueue.global(qos: .userInitiated).async {
for i in 0...self.totalQuizzes.count-1 {

let imageUrlString = self.totalQuizzes[i].attachment?.url
let imageUrl:URL = URL(string: imageUrlString!)!
print(imageUrl)

let imageData:NSData = NSData(contentsOf: imageUrl)!
let image = UIImage(data: imageData as Data)
print("hola (i)")
self.quizzesImages.append(image!)
print(self.quizzesImages.count)
// When from background thread, UI needs to be updated on main_queue
}
DispatchQueue.main.async {
print(self.quizzesImages.count)
self.tableView.reloadData()
UIApplication.shared.isNetworkActivityIndicatorVisible = false
}
}
}

func downloadQuizzes(){
let QUIZZES_URL = "https://quiz2019.herokuapp.com/api/quizzes?token=945d3bf7d4c709d69940"
if let url = URL(string: QUIZZES_URL){
let queue = DispatchQueue(label: "download quizzes queue")
queue.async {
DispatchQueue.main.async {
UIApplication.shared.isNetworkActivityIndicatorVisible = true
}
let data = try? Data(contentsOf: url, options: .alwaysMapped)
let decoder = JSONDecoder()
do{
let response = try decoder.decode(ResponseObject.self, from: data!)
DispatchQueue.main.async {
if (response.quizzes!.count != 0){
self.totalQuizzes.append(contentsOf: response.quizzes!)
self.prepareImages()
print(self.totalQuizzes.count)
}
}
}
catch {
print(error)
}
}
}
}
// MARK: - Table view data source

override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
print("num of rows (totalQuizzes.count)")
return totalQuizzes.count
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
print("in cellForRowAt (indexPath.row)")
let cell = tableView.dequeueReusableCell(withIdentifier: "Quiz")!
let quiz = totalQuizzes[indexPath.row]
let images = quizzesImages[indexPath.row]
cell.textLabel?.text = "(quiz.author?.username) (quiz.question)"
cell.imageView?.image = images
return cell
}}





share|improve this answer


























  • This worked, I had to self.tableView.reloadData() on prepareImages. Thank you very much

    – martingoldthen
    Nov 23 '18 at 17:47













  • Yes and that too after ending the for loop

    – Harshal Bhavsar
    Nov 23 '18 at 17:48











  • Feel free to upvote also if it helped. Once Again Happy coding

    – Harshal Bhavsar
    Nov 23 '18 at 17:49











  • I did, but my reputation is under 15 and it doesn't show, sorry

    – martingoldthen
    Nov 23 '18 at 17:53











  • No Worries Happy Coding. Keep Learning

    – Harshal Bhavsar
    Nov 23 '18 at 17:55
















1














See this screen shot form my playground



I tried to run your code in playground and managed to get it work with some changes please see the following.




Note




To make it run in playground i have to make some assumptions just replace your core logic with my one.



Happy Coding 😀



import UIKit
// Estructura del JSON que devuelve la URL

struct ResponseObject: Codable {
let quizzes: [Quiz]?
let pageno: Int?
let nextURL: String?

enum CodingKeys: String, CodingKey {
case quizzes, pageno
case nextURL = "nextUrl"
}
}

struct Quiz: Codable {
let id: Int?
let question: String?
let author: Author?
let attachment: Attachment?
let favourite: Bool?
let tips: [String]?
}

struct Attachment: Codable {
let filename: String?
let mime: MIME?
let url: String?
}

enum MIME: String, Codable {
case imageJPEG = "image/jpeg"
}

struct Author: Codable {
let id: Int?
let isAdmin: Bool?
let username: String?
}


class QuizzesTableViewController: UITableViewController {

// Aquí se guardan los quizzes cargados de la URL
var totalQuizzes = [Quiz]()
var quizzesImages = [UIImage]()

override func viewDidLoad() {
super.viewDidLoad()
self.tableView.rowHeight = 90.0
navigationController?.navigationBar.prefersLargeTitles = true
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Quiz")
self.tableView.dataSource = self
self.tableView.delegate = self
downloadQuizzes()
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}

func prepareImages(){
print("in prepare images")
// Start background thread so that image loading does not make app unresponsive
DispatchQueue.global(qos: .userInitiated).async {
for i in 0...self.totalQuizzes.count-1 {

let imageUrlString = self.totalQuizzes[i].attachment?.url
let imageUrl:URL = URL(string: imageUrlString!)!
print(imageUrl)

let imageData:NSData = NSData(contentsOf: imageUrl)!
let image = UIImage(data: imageData as Data)
print("hola (i)")
self.quizzesImages.append(image!)
print(self.quizzesImages.count)
// When from background thread, UI needs to be updated on main_queue
}
DispatchQueue.main.async {
print(self.quizzesImages.count)
self.tableView.reloadData()
UIApplication.shared.isNetworkActivityIndicatorVisible = false
}
}
}

func downloadQuizzes(){
let QUIZZES_URL = "https://quiz2019.herokuapp.com/api/quizzes?token=945d3bf7d4c709d69940"
if let url = URL(string: QUIZZES_URL){
let queue = DispatchQueue(label: "download quizzes queue")
queue.async {
DispatchQueue.main.async {
UIApplication.shared.isNetworkActivityIndicatorVisible = true
}
let data = try? Data(contentsOf: url, options: .alwaysMapped)
let decoder = JSONDecoder()
do{
let response = try decoder.decode(ResponseObject.self, from: data!)
DispatchQueue.main.async {
if (response.quizzes!.count != 0){
self.totalQuizzes.append(contentsOf: response.quizzes!)
self.prepareImages()
print(self.totalQuizzes.count)
}
}
}
catch {
print(error)
}
}
}
}
// MARK: - Table view data source

override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
print("num of rows (totalQuizzes.count)")
return totalQuizzes.count
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
print("in cellForRowAt (indexPath.row)")
let cell = tableView.dequeueReusableCell(withIdentifier: "Quiz")!
let quiz = totalQuizzes[indexPath.row]
let images = quizzesImages[indexPath.row]
cell.textLabel?.text = "(quiz.author?.username) (quiz.question)"
cell.imageView?.image = images
return cell
}}





share|improve this answer


























  • This worked, I had to self.tableView.reloadData() on prepareImages. Thank you very much

    – martingoldthen
    Nov 23 '18 at 17:47













  • Yes and that too after ending the for loop

    – Harshal Bhavsar
    Nov 23 '18 at 17:48











  • Feel free to upvote also if it helped. Once Again Happy coding

    – Harshal Bhavsar
    Nov 23 '18 at 17:49











  • I did, but my reputation is under 15 and it doesn't show, sorry

    – martingoldthen
    Nov 23 '18 at 17:53











  • No Worries Happy Coding. Keep Learning

    – Harshal Bhavsar
    Nov 23 '18 at 17:55














1












1








1







See this screen shot form my playground



I tried to run your code in playground and managed to get it work with some changes please see the following.




Note




To make it run in playground i have to make some assumptions just replace your core logic with my one.



Happy Coding 😀



import UIKit
// Estructura del JSON que devuelve la URL

struct ResponseObject: Codable {
let quizzes: [Quiz]?
let pageno: Int?
let nextURL: String?

enum CodingKeys: String, CodingKey {
case quizzes, pageno
case nextURL = "nextUrl"
}
}

struct Quiz: Codable {
let id: Int?
let question: String?
let author: Author?
let attachment: Attachment?
let favourite: Bool?
let tips: [String]?
}

struct Attachment: Codable {
let filename: String?
let mime: MIME?
let url: String?
}

enum MIME: String, Codable {
case imageJPEG = "image/jpeg"
}

struct Author: Codable {
let id: Int?
let isAdmin: Bool?
let username: String?
}


class QuizzesTableViewController: UITableViewController {

// Aquí se guardan los quizzes cargados de la URL
var totalQuizzes = [Quiz]()
var quizzesImages = [UIImage]()

override func viewDidLoad() {
super.viewDidLoad()
self.tableView.rowHeight = 90.0
navigationController?.navigationBar.prefersLargeTitles = true
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Quiz")
self.tableView.dataSource = self
self.tableView.delegate = self
downloadQuizzes()
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}

func prepareImages(){
print("in prepare images")
// Start background thread so that image loading does not make app unresponsive
DispatchQueue.global(qos: .userInitiated).async {
for i in 0...self.totalQuizzes.count-1 {

let imageUrlString = self.totalQuizzes[i].attachment?.url
let imageUrl:URL = URL(string: imageUrlString!)!
print(imageUrl)

let imageData:NSData = NSData(contentsOf: imageUrl)!
let image = UIImage(data: imageData as Data)
print("hola (i)")
self.quizzesImages.append(image!)
print(self.quizzesImages.count)
// When from background thread, UI needs to be updated on main_queue
}
DispatchQueue.main.async {
print(self.quizzesImages.count)
self.tableView.reloadData()
UIApplication.shared.isNetworkActivityIndicatorVisible = false
}
}
}

func downloadQuizzes(){
let QUIZZES_URL = "https://quiz2019.herokuapp.com/api/quizzes?token=945d3bf7d4c709d69940"
if let url = URL(string: QUIZZES_URL){
let queue = DispatchQueue(label: "download quizzes queue")
queue.async {
DispatchQueue.main.async {
UIApplication.shared.isNetworkActivityIndicatorVisible = true
}
let data = try? Data(contentsOf: url, options: .alwaysMapped)
let decoder = JSONDecoder()
do{
let response = try decoder.decode(ResponseObject.self, from: data!)
DispatchQueue.main.async {
if (response.quizzes!.count != 0){
self.totalQuizzes.append(contentsOf: response.quizzes!)
self.prepareImages()
print(self.totalQuizzes.count)
}
}
}
catch {
print(error)
}
}
}
}
// MARK: - Table view data source

override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
print("num of rows (totalQuizzes.count)")
return totalQuizzes.count
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
print("in cellForRowAt (indexPath.row)")
let cell = tableView.dequeueReusableCell(withIdentifier: "Quiz")!
let quiz = totalQuizzes[indexPath.row]
let images = quizzesImages[indexPath.row]
cell.textLabel?.text = "(quiz.author?.username) (quiz.question)"
cell.imageView?.image = images
return cell
}}





share|improve this answer















See this screen shot form my playground



I tried to run your code in playground and managed to get it work with some changes please see the following.




Note




To make it run in playground i have to make some assumptions just replace your core logic with my one.



Happy Coding 😀



import UIKit
// Estructura del JSON que devuelve la URL

struct ResponseObject: Codable {
let quizzes: [Quiz]?
let pageno: Int?
let nextURL: String?

enum CodingKeys: String, CodingKey {
case quizzes, pageno
case nextURL = "nextUrl"
}
}

struct Quiz: Codable {
let id: Int?
let question: String?
let author: Author?
let attachment: Attachment?
let favourite: Bool?
let tips: [String]?
}

struct Attachment: Codable {
let filename: String?
let mime: MIME?
let url: String?
}

enum MIME: String, Codable {
case imageJPEG = "image/jpeg"
}

struct Author: Codable {
let id: Int?
let isAdmin: Bool?
let username: String?
}


class QuizzesTableViewController: UITableViewController {

// Aquí se guardan los quizzes cargados de la URL
var totalQuizzes = [Quiz]()
var quizzesImages = [UIImage]()

override func viewDidLoad() {
super.viewDidLoad()
self.tableView.rowHeight = 90.0
navigationController?.navigationBar.prefersLargeTitles = true
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Quiz")
self.tableView.dataSource = self
self.tableView.delegate = self
downloadQuizzes()
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}

func prepareImages(){
print("in prepare images")
// Start background thread so that image loading does not make app unresponsive
DispatchQueue.global(qos: .userInitiated).async {
for i in 0...self.totalQuizzes.count-1 {

let imageUrlString = self.totalQuizzes[i].attachment?.url
let imageUrl:URL = URL(string: imageUrlString!)!
print(imageUrl)

let imageData:NSData = NSData(contentsOf: imageUrl)!
let image = UIImage(data: imageData as Data)
print("hola (i)")
self.quizzesImages.append(image!)
print(self.quizzesImages.count)
// When from background thread, UI needs to be updated on main_queue
}
DispatchQueue.main.async {
print(self.quizzesImages.count)
self.tableView.reloadData()
UIApplication.shared.isNetworkActivityIndicatorVisible = false
}
}
}

func downloadQuizzes(){
let QUIZZES_URL = "https://quiz2019.herokuapp.com/api/quizzes?token=945d3bf7d4c709d69940"
if let url = URL(string: QUIZZES_URL){
let queue = DispatchQueue(label: "download quizzes queue")
queue.async {
DispatchQueue.main.async {
UIApplication.shared.isNetworkActivityIndicatorVisible = true
}
let data = try? Data(contentsOf: url, options: .alwaysMapped)
let decoder = JSONDecoder()
do{
let response = try decoder.decode(ResponseObject.self, from: data!)
DispatchQueue.main.async {
if (response.quizzes!.count != 0){
self.totalQuizzes.append(contentsOf: response.quizzes!)
self.prepareImages()
print(self.totalQuizzes.count)
}
}
}
catch {
print(error)
}
}
}
}
// MARK: - Table view data source

override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
print("num of rows (totalQuizzes.count)")
return totalQuizzes.count
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
print("in cellForRowAt (indexPath.row)")
let cell = tableView.dequeueReusableCell(withIdentifier: "Quiz")!
let quiz = totalQuizzes[indexPath.row]
let images = quizzesImages[indexPath.row]
cell.textLabel?.text = "(quiz.author?.username) (quiz.question)"
cell.imageView?.image = images
return cell
}}






share|improve this answer














share|improve this answer



share|improve this answer








edited Nov 23 '18 at 17:35

























answered Nov 23 '18 at 17:19









Harshal BhavsarHarshal Bhavsar

861931




861931













  • This worked, I had to self.tableView.reloadData() on prepareImages. Thank you very much

    – martingoldthen
    Nov 23 '18 at 17:47













  • Yes and that too after ending the for loop

    – Harshal Bhavsar
    Nov 23 '18 at 17:48











  • Feel free to upvote also if it helped. Once Again Happy coding

    – Harshal Bhavsar
    Nov 23 '18 at 17:49











  • I did, but my reputation is under 15 and it doesn't show, sorry

    – martingoldthen
    Nov 23 '18 at 17:53











  • No Worries Happy Coding. Keep Learning

    – Harshal Bhavsar
    Nov 23 '18 at 17:55



















  • This worked, I had to self.tableView.reloadData() on prepareImages. Thank you very much

    – martingoldthen
    Nov 23 '18 at 17:47













  • Yes and that too after ending the for loop

    – Harshal Bhavsar
    Nov 23 '18 at 17:48











  • Feel free to upvote also if it helped. Once Again Happy coding

    – Harshal Bhavsar
    Nov 23 '18 at 17:49











  • I did, but my reputation is under 15 and it doesn't show, sorry

    – martingoldthen
    Nov 23 '18 at 17:53











  • No Worries Happy Coding. Keep Learning

    – Harshal Bhavsar
    Nov 23 '18 at 17:55

















This worked, I had to self.tableView.reloadData() on prepareImages. Thank you very much

– martingoldthen
Nov 23 '18 at 17:47







This worked, I had to self.tableView.reloadData() on prepareImages. Thank you very much

– martingoldthen
Nov 23 '18 at 17:47















Yes and that too after ending the for loop

– Harshal Bhavsar
Nov 23 '18 at 17:48





Yes and that too after ending the for loop

– Harshal Bhavsar
Nov 23 '18 at 17:48













Feel free to upvote also if it helped. Once Again Happy coding

– Harshal Bhavsar
Nov 23 '18 at 17:49





Feel free to upvote also if it helped. Once Again Happy coding

– Harshal Bhavsar
Nov 23 '18 at 17:49













I did, but my reputation is under 15 and it doesn't show, sorry

– martingoldthen
Nov 23 '18 at 17:53





I did, but my reputation is under 15 and it doesn't show, sorry

– martingoldthen
Nov 23 '18 at 17:53













No Worries Happy Coding. Keep Learning

– Harshal Bhavsar
Nov 23 '18 at 17:55





No Worries Happy Coding. Keep Learning

– Harshal Bhavsar
Nov 23 '18 at 17:55













1














Since you didn't say what line the exception is happening on, my best guess is that it's happening on this line.



let images = quizzesImages[indexPath.row]


The issue is in downloadQuizzes. In there you call prepareImages then a couple lines later call reloadData. Since prepareImages does an async task, it isn't blocking downloadQuizzes from finishing running. This causes reloadData to be called before the async part of prepareImages is finished.



You'll need to make sure prepareImages is finished running before calling reloadData.



Another option is to use a library like Kingfisher that will only download the images when they need to be displayed in a cell rather then downloading them all and slowing down showing the table.





Additional Note:



You shouldn't be using Data(contentsOf:) to load remote content, URLSession is the correct option for it.






share|improve this answer
























  • Yes! Taking out all the async tasks in prepareImages makes it work! How would I do it so reloadData isn't called until prepareImages is done? Thank you so much!

    – martingoldthen
    Nov 23 '18 at 17:10
















1














Since you didn't say what line the exception is happening on, my best guess is that it's happening on this line.



let images = quizzesImages[indexPath.row]


The issue is in downloadQuizzes. In there you call prepareImages then a couple lines later call reloadData. Since prepareImages does an async task, it isn't blocking downloadQuizzes from finishing running. This causes reloadData to be called before the async part of prepareImages is finished.



You'll need to make sure prepareImages is finished running before calling reloadData.



Another option is to use a library like Kingfisher that will only download the images when they need to be displayed in a cell rather then downloading them all and slowing down showing the table.





Additional Note:



You shouldn't be using Data(contentsOf:) to load remote content, URLSession is the correct option for it.






share|improve this answer
























  • Yes! Taking out all the async tasks in prepareImages makes it work! How would I do it so reloadData isn't called until prepareImages is done? Thank you so much!

    – martingoldthen
    Nov 23 '18 at 17:10














1












1








1







Since you didn't say what line the exception is happening on, my best guess is that it's happening on this line.



let images = quizzesImages[indexPath.row]


The issue is in downloadQuizzes. In there you call prepareImages then a couple lines later call reloadData. Since prepareImages does an async task, it isn't blocking downloadQuizzes from finishing running. This causes reloadData to be called before the async part of prepareImages is finished.



You'll need to make sure prepareImages is finished running before calling reloadData.



Another option is to use a library like Kingfisher that will only download the images when they need to be displayed in a cell rather then downloading them all and slowing down showing the table.





Additional Note:



You shouldn't be using Data(contentsOf:) to load remote content, URLSession is the correct option for it.






share|improve this answer













Since you didn't say what line the exception is happening on, my best guess is that it's happening on this line.



let images = quizzesImages[indexPath.row]


The issue is in downloadQuizzes. In there you call prepareImages then a couple lines later call reloadData. Since prepareImages does an async task, it isn't blocking downloadQuizzes from finishing running. This causes reloadData to be called before the async part of prepareImages is finished.



You'll need to make sure prepareImages is finished running before calling reloadData.



Another option is to use a library like Kingfisher that will only download the images when they need to be displayed in a cell rather then downloading them all and slowing down showing the table.





Additional Note:



You shouldn't be using Data(contentsOf:) to load remote content, URLSession is the correct option for it.







share|improve this answer












share|improve this answer



share|improve this answer










answered Nov 23 '18 at 17:02









Craig SiemensCraig Siemens

9,54612042




9,54612042













  • Yes! Taking out all the async tasks in prepareImages makes it work! How would I do it so reloadData isn't called until prepareImages is done? Thank you so much!

    – martingoldthen
    Nov 23 '18 at 17:10



















  • Yes! Taking out all the async tasks in prepareImages makes it work! How would I do it so reloadData isn't called until prepareImages is done? Thank you so much!

    – martingoldthen
    Nov 23 '18 at 17:10

















Yes! Taking out all the async tasks in prepareImages makes it work! How would I do it so reloadData isn't called until prepareImages is done? Thank you so much!

– martingoldthen
Nov 23 '18 at 17:10





Yes! Taking out all the async tasks in prepareImages makes it work! How would I do it so reloadData isn't called until prepareImages is done? Thank you so much!

– martingoldthen
Nov 23 '18 at 17:10


















draft saved

draft discarded




















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53450085%2findex-out-of-range-exception-using-dispatchqueue%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







這個網誌中的熱門文章

Tangent Lines Diagram Along Smooth Curve

Yusuf al-Mu'taman ibn Hud

Zucchini