0

SOMETIMES THE REFRESH WORKS SOMETIMES IT DOESN'T

I have a UITableViewController which is basically a news feed. I have also implemented a pull to refresh feature. However sometimes when I pull to refresh it gives me the error

'Array index out of range'.

I know this means an item it is trying to get does not exist but can you tell me why? Here is my code:

override func viewDidLoad() {
    super.viewDidLoad()

    refresher = UIRefreshControl()
    refresher.attributedTitle = NSAttributedString(string: "Pull to refresh")
    refresher.addTarget(self, action: "refresh", forControlEvents: UIControlEvents.ValueChanged)
    self.tableView.addSubview(refresher)
    refresh()

    tableView.delegate = self
    tableView.dataSource = self
}

and the refresh() function:

func refresh() {

    //disable app while it does stuff
    UIApplication.sharedApplication().beginIgnoringInteractionEvents()

    //get username and match with userId
    let getUser = PFUser.query()
    getUser?.findObjectsInBackgroundWithBlock({ (objects, error) -> Void in

        if let users = objects {

            //clean arrays and dictionaries so we dont get indexing error???
            self.messages.removeAll(keepCapacity: true)
            self.users.removeAll(keepCapacity: true)
            self.usernames.removeAll(keepCapacity: true)

            for object in users {

                if let user = object as? PFUser {

                    //make userId = username
                    self.users[user.objectId!] = user.username!
                }
            }
        }
    })

    let getPost = PFQuery(className: "Posts")
    getPost.findObjectsInBackgroundWithBlock { (objects, error) -> Void in

        if error == nil {
        if let objects = objects {

            self.messages.removeAll(keepCapacity: true)
            self.usernames.removeAll(keepCapacity: true)

            for object in objects {

                self.messages.append(object["message"] as! String)
                self.usernames.append(self.users[object["userId"] as! String]!)

                self.tableView.reloadData()
            }
        }
        }
    }

    self.refresher.endRefreshing()
    UIApplication.sharedApplication().endIgnoringInteractionEvents()
}

and:

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let myCell = tableView.dequeueReusableCellWithIdentifier("SinglePostCell", forIndexPath: indexPath) as! PostCell


    //ERROR GETS REPORTED ON THE LINE BELOW
    myCell.usernamePosted.text = usernames[indexPath.row]
    myCell.messagePosted.text = messages[indexPath.row]

    return myCell
}

4 Answers 4

1

You have a race condition given you are doing two background tasks, where the second depends on values returned from the first. getUser?.findObjectsInBackgroundWithBlockwill return immediately, and getPost.findObjectsInBackgroundWithBlock will start executing. The getPost should be inside the block for getUser, to ensure the sequence is correct. Similarly, the following two lines should be inside the second block:

self.refresher.endRefreshing()
UIApplication.sharedApplication().endIgnoringInteractionEvents()

Given the error line, you probably also have a race condition between the two background tasks and displaying the tableView. I would be inclined to try:

func tableView(tableView:UITableView!, numberOfRowsInSection section:Int) {
   return self.refresher.refreshing ? 0 : self.usernames.count
}

This way you won't touch self.usernames until the background refresh is finished (as long as you remember to put endRefreshing inside the second block, which is also put inside the first block).

Sign up to request clarification or add additional context in comments.

Comments

0

I Believe that in self.users[user.objectId!] = user.username! the user.ObjectId is some random value assigned by parse which looks like this: "34xcf4". This is why you might be getting 'Array index out of range'.

Comments

0

There are two required methods for configuring a UITableView:

  • tableView(_:cellForRowAtIndexPath:) and
  • tableView(_:numberOfRowsInSection:)

In your code you are presenting only one required method, if you don't implement the second method then it that may cause errors.

Check the documentation at: https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableViewDataSource_Protocol/#//apple_ref/occ/intfm/UITableViewDataSource/tableView:cellForRowAtIndexPath:

Comments

0

You are calling self.tableView.reloadData() on every addition to your array and doing so in a background thread.

As a general rule, you should not do UI updates in a background thread. When you clear self.messages and self.usernames, because you are in background thread, nothing prevents the tableview from trying to get a cell at an index that no longer has any data in the array.

If you want to keep your code in the background thread (risky as it may be), you should at least call .beginUpdates before reloading your arrays and wait until they're all done before calling reload and endUpdates.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.