Chris King
Chris King

Reputation: 225

TableView rows inconsistent with data in Swift

I am loading a UITableViewController with data from my Contact List. Everything works fine in simulator with the small set of data, but on my iPhone with 82 contacts, i get a varying number of rows loaded, from 0, to most of the data, but never all of the data, and always a different amount. It's almost as though the table is being loaded and displayed before the data array is complete. If i set the numberOfRowsInSection return manually to 82 it works fine, but when set to contactdatalist.count it gets a lesser number. Is there something i am doing wrong, or can i slow down the tableview load until all the data load is complete? It seems asynchronous in operation.

import UIKit
import AddressBook

class TableViewControllerContacts: UITableViewController {
    
    var contactdatalist = [ContactData]()
    
    override func viewDidLoad() {
        super.viewDidLoad()

        // Uncomment the following line to preserve selection between presentations
        // self.clearsSelectionOnViewWillAppear = false

        // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
        // self.navigationItem.rightBarButtonItem = self.editButtonItem()
            
        self.contactdatalist = []

        let addressBook : ABAddressBookRef? = ABAddressBookCreateWithOptions(nil, nil).takeRetainedValue()
        ABAddressBookRequestAccessWithCompletion(addressBook, { (granted : Bool, error: CFError!) -> Void in
            if granted == true {
                let allContacts : NSArray = ABAddressBookCopyArrayOfAllPeople(addressBook).takeRetainedValue()
                for contactRef:ABRecordRef in allContacts { // first name
                    let myPBfirstname = ABRecordCopyValue(contactRef, kABPersonFirstNameProperty)?.takeRetainedValue() as! NSString? ?? ""
                    let myPBlastname = ABRecordCopyValue(contactRef, kABPersonLastNameProperty)?.takeRetainedValue() as! NSString? ?? ""
                    let phonesRef: ABMultiValueRef = ABRecordCopyValue(contactRef, kABPersonPhoneProperty)?.takeRetainedValue() as ABMultiValueRef? ?? ""
                    var phonesArray  = Array<Dictionary<String,String>>()
                    for var i:Int = 0; i < ABMultiValueGetCount(phonesRef); i++ {
                        let myPhLabel = ABMultiValueCopyLabelAtIndex(phonesRef, i)?.takeRetainedValue() as NSString? ?? ""
                        let myPhValue = ABMultiValueCopyValueAtIndex(phonesRef, i)?.takeRetainedValue() as! NSString? ?? ""
                        if myPhLabel.containsString("Mobile") {
                            self.contactdatalist.append(ContactData(firstname:myPBfirstname as String, lastname:myPBlastname as String, phone:myPhValue as String))
                        }
                    }
                }
            }
        })
        self.tableView.reloadData()
    }

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

    // MARK: - Table view data source

    override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        // #warning Potentially incomplete method implementation.
        // Return the number of sections.
        return 1
    }

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        // #warning Incomplete method implementation.
        // Return the number of rows in the section.
        return self.contactdatalist.count
    }

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = self.tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! UITableViewCell
        // Configure the cell...
        let contactdata = self.contactdatalist[indexPath.row]
        cell.textLabel!.text = ("\(contactdata.firstname) \(contactdata.lastname)")
        return cell
    }

Upvotes: 0

Views: 352

Answers (2)

Aneeq Anwar
Aneeq Anwar

Reputation: 1332

Update your viewDidLoad, so that contacts are fetched in a separate thread, and tableview is loaded when the contacts are fetched.

override func viewDidLoad() {
        super.viewDidLoad()

        // Uncomment the following line to preserve selection between presentations
        // self.clearsSelectionOnViewWillAppear = false

        // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
        // self.navigationItem.rightBarButtonItem = self.editButtonItem()

        self.contactdatalist = []

dispatch_async(dispatch_get_global_queue(Int(QOS_CLASS_USER_INITIATED.value), 0)) { 
    let addressBook : ABAddressBookRef? = ABAddressBookCreateWithOptions(nil, nil).takeRetainedValue()
        ABAddressBookRequestAccessWithCompletion(addressBook, { (granted : Bool, error: CFError!) -> Void in
            if granted == true {
                let allContacts : NSArray = ABAddressBookCopyArrayOfAllPeople(addressBook).takeRetainedValue()
                for contactRef:ABRecordRef in allContacts { // first name
                    let myPBfirstname = ABRecordCopyValue(contactRef, kABPersonFirstNameProperty)?.takeRetainedValue() as! NSString? ?? ""
                    let myPBlastname = ABRecordCopyValue(contactRef, kABPersonLastNameProperty)?.takeRetainedValue() as! NSString? ?? ""
                    let phonesRef: ABMultiValueRef = ABRecordCopyValue(contactRef, kABPersonPhoneProperty)?.takeRetainedValue() as ABMultiValueRef? ?? ""
                    var phonesArray  = Array<Dictionary<String,String>>()
                    for var i:Int = 0; i < ABMultiValueGetCount(phonesRef); i++ {
                        let myPhLabel = ABMultiValueCopyLabelAtIndex(phonesRef, i)?.takeRetainedValue() as NSString? ?? ""
                        let myPhValue = ABMultiValueCopyValueAtIndex(phonesRef, i)?.takeRetainedValue() as! NSString? ?? ""
                        if myPhLabel.containsString("Mobile") {
                            self.contactdatalist.append(ContactData(firstname:myPBfirstname as String, lastname:myPBlastname as String, phone:myPhValue as String))
                        }
                    }
                }
            }
        })

    dispatch_async(dispatch_get_main_queue()) { 
      self.tableView.reloadData() 
    }
  }
}

Upvotes: 2

Pablo Carrillo Alvarez
Pablo Carrillo Alvarez

Reputation: 1182

Requesting the address book is an asynchronous operation. So you should reload the data in the table view inside that block at last step, and not at the end of viewDidLoad.

Upvotes: 0

Related Questions