I have a problem with my UITableView. It is always empty.
Abstract: I have got a model class. After the instantiation of the model in the method viewDidLoad in my UITableViewController it will grab asynchronously a JSON-struct from my server. This data will be parse to my object.
My UITableViewController also has a NSNotification Observer with a selector method. This selector method will be triggered if the model class post the notification (post notification if the model is done with requesting and parsing the data from server).
The selector method will execute a "self.tableView.reloadData()" in a dispatch main-queue block.
The debugger shows that the object is not nil but the tableView is empty. I do not have an idea why the tableview is empty.
Parts from my UITableViewController:
import UIKit
class BillingPlanTableViewController: UITableViewController {
@IBOutlet weak var menuButton: UIBarButtonItem!
var budgetBillingPlansModel: BudgetBillingPlanModel!
// MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
//Menu gesture recognizer
if self.revealViewController() != nil {
menuButton.target = self.revealViewController()
menuButton.action = "revealToggle:"
self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
}
//initialize BudgetBillingPlansModel
NSNotificationCenter.defaultCenter().addObserver(self, selector: "initBbPDone:",name:"initBbPDone", object: nil)
budgetBillingPlansModel = BudgetBillingPlanModel()
println(budgetBillingPlansModel)
}
// MARK: - Custom Methods
func initBbPDone(notification: NSNotification){
//reload tableview: from in thread: Zu diesem Zeitpunkt ist mein Objekt gefüllt.
dispatch_async(dispatch_get_main_queue(),{
self.tableView.reloadData()
});
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
if(self.budgetBillingPlansModel.checkBbPisReady()){
return self.budgetBillingPlansModel.getBudgetBillingPlanModel()!.getBbpArray().count
}else{
return 0
}
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if(self.budgetBillingPlansModel.checkBbPisReady()){
var numberOfRowsInSection: Int = 0
for(var index = 0; index < self.budgetBillingPlansModel.getBudgetBillingPlanModel()!.getBbpArray().count; index++){
if(index == section){
numberOfRowsInSection = self.budgetBillingPlansModel.getBudgetBillingPlanModel()!.getBbpArray()[index].getSubPlan().count
}
}
return numberOfRowsInSection
}else{
return 0
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("budgetBillingPlans", forIndexPath: indexPath) as! BudgetBillingPlanTableViewCell
if(self.budgetBillingPlansModel.checkBbPisReady()){
//section == bbP Array
for(var i = 0; i < self.budgetBillingPlansModel.getBudgetBillingPlanModel()!.getBbpArray().count; i++){
if(i == indexPath.section){
//row == subPlans Array
let subPlans: [SubPlan] = self.budgetBillingPlansModel.getBudgetBillingPlanModel()!.getBbpArray()[i].getSubPlan()
for(var j = 0; j < subPlans.count; j++){
if(j == indexPath.row){
cell.ibo_header.text = "\(self.budgetBillingPlansModel.getBudgetBillingPlanModel()!.getBbpArray()[i].getSubPlan()[j].getSbbpDivisionName())"
cell.ibo_budgetAmount.text = "\(self.budgetBillingPlansModel.getBudgetBillingPlanModel()!.getBbpArray()[i].getSubPlan()[j].getSbbpActAmount())"
cell.ibo_contract.text = "\(self.budgetBillingPlansModel.getBudgetBillingPlanModel()!.getBbpArray()[i].getSubPlan()[j].getSbbpContractNumber())"
cell.ibo_date.text = (self.budgetBillingPlansModel.getBudgetBillingPlanModel()!.getBbpArray()[i].getFirstAdjustmentDate()) + " " + (self.budgetBillingPlansModel.getBudgetBillingPlanModel()!.getBbpArray()[i].getbbpCycleName())
}
}
}
}
}else{
}
return cell
}
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 150
}
}
Parts from my model class:
class BudgetBillingPlanModel: NSObject {
var budgetBillingPlan: BudgetBillingPlan!
var bbPisReady: Bool!
//getBudgetBillingPlans
private func getBudgetBillingPlans(){
Alamofire.request(.GET, Constants.service_getBudgetBillingPlans)
.responseString { (request, response, jsonString, error) in
println(error)// error ist im debugger-Zeitpunkt == nil
self.budgetBillingPlan = self.parseServerResponse(jsonString!)
self.setbbPisReady()
}
}
//Notifications
private func setbbPisReady(){
self.bbPisReady = true
//object controller has to implement the notification method with "initBbPDone"
NSNotificationCenter.defaultCenter().postNotificationName("initBbPDone", object: nil)
}
//check status
func checkBbPisReady()->Bool{
if(self.bbPisReady == true){
return true
}else{
return false
}
}
//getter
func getBudgetBillingPlanModel()->BudgetBillingPlan?{
if(self.checkBbPisReady()){
return self.budgetBillingPlan
}else{
return nil
}
}
Debugger Screenshot:
Addition: If I try to init the tableView with static cells, it will shows the static cells. But after the instantiation in viewDidLoad, when the notification is fired the tableview is empty. If I interact with the empty tableView like a pan or tap gesture I will get the error: EXC_bad_Access in the appdelegate ( thread1 )
I didn't find out the problem with the Notification-pattern for the mvc communication, but I think it was an issue with the threads. The solution was that I switch to the kvc-pattern, now my tableView is not longer empty.