James Parsons
James Parsons

Reputation: 6057

Realm data not showing in table view

I am relatively new to iOS trying to develop a simple note app using Realm. I have a few view controllers.

To Create a new note:

//
//  ViewController.swift
//  Note It
//
//  Created by James Parsons on 2/19/16.
//  Copyright © 2016 James Parsons. All rights reserved.
//

import UIKit
import RealmSwift
import Realm

class NewNoteViewController: UIViewController {
    // MARK: - Properties
    var save: Bool?
    var realm: Realm?

    // MARK: - Outlets
    @IBOutlet weak var txtView: UITextView!

    override func viewDidLoad() {
        super.viewDidLoad()

        // Initialize realm.
        realm = try! Realm()

        // Set up insets.
        txtView.contentInset = UIEdgeInsetsMake(-50, 10, 0, 0)

        // Do we have any data from the last time?
        let data: String? = NSUserDefaults.standardUserDefaults().objectForKey("lastNoteData") as? String

        if (data != nil) {
            txtView.text = data
        }
    }

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

    override func viewWillDisappear(animated: Bool) {
        // Do we want to save the data?
        if save! {
            if txtView.hasText() {
                // Save the current note data
                let noteData = txtView.text
                NSUserDefaults.standardUserDefaults().setValue(noteData, forKey: "lastNoteData")
            }
        } else {
            // No need to save note data. Clear the setting.
            NSUserDefaults.standardUserDefaults().setValue(nil, forKey: "lastNoteData")
        }

    }

    // MARK: - Actions
    @IBAction func btnCancel(sender: UIBarButtonItem) {
        // Save note content
        save = true
        self.navigationController!.presentingViewController?.dismissViewControllerAnimated(true, completion: nil)
    }

    @IBAction func btnSave(sender: UIBarButtonItem) {
        var alert: UIAlertController

        if txtView.hasText() {
            let textToSerialize = txtView.text

            alert = UIAlertController(title: "Save", message: "Enter a name for this note", preferredStyle: .Alert)

            let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel, handler: nil)
            let saveAction = UIAlertAction(title: "Save", style: .Default, handler: { (action: UIAlertAction) -> Void in
                // DEBUG: See if this is firing.
                // By the way it works.
                print("Yay, it worked")

                // Don't serialize data.
                self.save = false

                // Get the note name.
                let name = alert.textFields!.first!.text!

                // Build the note.
                let noteToSave = Note(name: name, content: textToSerialize, date: NSDate())

                // Save the note into Realm.
                dispatch_async(dispatch_get_main_queue(), { () -> Void in
                    try! self.realm!.write {
                        self.realm!.add(noteToSave)
                    }
                })

                // Go back.
                self.navigationController!.presentingViewController!.dismissViewControllerAnimated(true, completion: nil)
            })

            alert.addTextFieldWithConfigurationHandler {
                (textField: UITextField) -> Void in
            }

            alert.addAction(cancelAction)
            alert.addAction(saveAction)

            presentViewController(alert, animated: true, completion: nil)
        } else {
            // Notes cannot be blank.
            let alert = UIAlertController(title: "Whoops", message: "This note needs some content", preferredStyle: .Alert)

            let okAction = UIAlertAction(title: "Ok", style: .Default, handler: nil)
            alert.addAction(okAction)

            // Show the alert.
            presentViewController(alert, animated: true, completion: nil)

        }
    }
}

The Main View:

//
//  NotesViewController.swift
//  Note It
//
//  Created by James Parsons on 2/19/16.
//  Copyright © 2016 James Parsons. All rights reserved.
//

import UIKit
import RealmSwift
import Realm

class NotesViewController: UITableViewController {
    // MARK: - Properties
    var notes: Results<Note>?
    var realm: Realm?

    // MARK: ViewController methods.
    override func viewDidLoad() {
        super.viewDidLoad()

        // Refresh the table.
        dispatch_async(dispatch_get_main_queue(), { () -> Void in
            self.tableView.reloadData()
        })

        // Set up our Realm.
        realm = try! Realm()

        // Get all the notes.
        notes = realm!.objects(Note)
    }

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

    // MARK: - Table view data source

    override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return 1
    }

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        // Return number of objects.
        return realm!.objects(Note).count
    }

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("note", forIndexPath: indexPath)

        // Set up the cell with our data
        if let note = notes?[indexPath.row] {
            cell.textLabel!.text = note.name
            cell.detailTextLabel!.text = dateToString(note.dateCreated)
        }

        return cell
    }


    /*
    // Override to support conditional editing of the table view.
    override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
        // Return false if you do not want the specified item to be editable.
        return true
    }
    */

    /*
    // Override to support editing the table view.
    override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
        if editingStyle == .Delete {
            // Delete the row from the data source
            tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
        } else if editingStyle == .Insert {
            // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
        }    
    }
    */

    /*
    // Override to support rearranging the table view.
    override func tableView(tableView: UITableView, moveRowAtIndexPath fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath) {

    }
    */

    /*
    // Override to support conditional rearranging of the table view.
    override func tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool {
        // Return false if you do not want the item to be re-orderable.
        return true
    }
    */

    // MARK: - Functions
    func dateToString(date: NSDate) -> String {
        let formatter = NSDateFormatter()
        formatter.dateStyle = .ShortStyle

        return formatter.stringFromDate(date)

    }

}

And the model

//
//  Note.swift
//  Note It
//
//  Created by James Parsons on 2/19/16.
//  Copyright © 2016 James Parsons. All rights reserved.
//

import Foundation
import RealmSwift

// Realm model for notes.
class Note: Object {
    // MARK: - Properties
    dynamic var name = ""
    dynamic var content = ""
    dynamic var dateCreated = NSDate()

    // MARK: - Intializers
    required init() {
        super.init()
    }

    required init(name: String, content: String, date: NSDate) {
        self.name = name
        self.content = content
        self.dateCreated = date

        super.init()
    }

}

I know the handler on the save action fires, but no data ever shows in the table. What am I doing wrong?

Upvotes: 0

Views: 830

Answers (1)

pmick
pmick

Reputation: 469

You are going to need code that will tell your tableview to update when you add a note. There are many approaches that will work for this.

One possible approach would be to use realms notification feature.

First add the following variable to your NotesViewController:

var token: NotificationToken?

In viewDidLoad of NotesViewController add:

// Get all the notes.
notes = realm!.objects(Note)

token = notes!.addNotificationBlock({ (notification, realm) -> Void in
    self.tableView.reloadData()
})

I am not completely familiar with realm, but you will then probably also need to add a deinit implementation that removes this token:

deinit {
    if let t = token {
        t.stop()
    }
}

edit taking into consideration bdash's comment

Upvotes: 1

Related Questions