Problems saving document with CBModel

#1

I have the UI mechanics working, however it does not seem to be saving items to the database. It brings up the alert to enter a value, but upon saving I cannot see anything going in. I’m using the latest Couchbase Lite release (1.1.1-18) and writing classes in Swift.

Logs are showing me things like:

Saving document: –i`
Document: Optional(CBLDocument[-tHj..zWph])

Anything I might have missed here?

My MasterViewController:

    import UIKit
    
    class MasterViewController: UITableViewController, CBLUITableDelegate, UIAlertViewDelegate
    {
        var detailViewController: DetailViewController?
        @IBOutlet var dataSource: CBLUITableSource!
        @IBOutlet var thingTitleField: UITextField?
        var objects = NSMutableArray()
        var db: CBLDatabase!
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            // Do any additional setup after loading the view.
            // navigation stuff
          
            let addButton = UIBarButtonItem(barButtonSystemItem: .Add, target: self, action: "insertNewObject:")
            self.navigationItem.rightBarButtonItem = addButton
            if let split = self.splitViewController {
                let controllers = split.viewControllers
                self.detailViewController = controllers[controllers.count-1].topViewController as? DetailViewController
            }
        }
    
        override func didReceiveMemoryWarning() {
            super.didReceiveMemoryWarning()
            // Dispose of any resources that can be recreated.
        }
        
        func useDatabase(database: CBLDatabase) {
            db = database
            dataSource = CBLUITableSource() //?
            
            
            // Now we can set up db related stuff

            let query = Thing.queryThingsInDatabase(db)?.asLiveQuery()
            query!.descending = true
            
            // Plug the query into the CBLUITableSource, which will use it to drive the table view.
            // (The CBLUITableSource uses KVO to observe the query's .rows property.)
            
            self.dataSource = CBLUITableSource()
            self.dataSource.tableView = self.tableView
            self.tableView.dataSource = self.dataSource
            
            self.dataSource.query = query!
            self.dataSource.labelProperty = "title"    // Document property to display in the cell label
            
            self.title = "Things"
            self.tableView.delegate = self
        }
        
        func insertNewObject(sender: AnyObject) {
            
            // Create the alert controller
            var alertController = UIAlertController(title: "New Thing", message: "Title for new thing:", preferredStyle: .Alert)
            
            // Create the actions
            var okAction = UIAlertAction(title: "Create", style: UIAlertActionStyle.Default) {
                UIAlertAction in
                NSLog("OK Pressed")
                self.createThingWithTitle(self.thingTitleField!.text)
            }
            var cancelAction = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel) {
                UIAlertAction in
                NSLog("Cancel Pressed")
            }
            
            // Add the actions
            alertController.addAction(okAction)
            alertController.addAction(cancelAction)
            
            // Add text field
            alertController.addTextFieldWithConfigurationHandler ({(textField: UITextField!) in
                textField.placeholder = "Title"
                self.thingTitleField = textField
            })
            
            // Present the controller
            self.presentViewController(alertController, animated: true, completion: nil)
            
        }
        
        func createThingWithTitle(title: String) -> Thing? {
            var thing = Thing(forNewDocumentInDatabase: db)
            thing.title = title
            thing.created_at = NSDate(timeIntervalSinceNow: 0)

            var error: NSError?
            
            NSLog("Saving document: %s", thing.propertiesToSave().debugDescription)
            if thing.save(&error) == false {
                let alert = UIAlertView(title: "Error", message: "Unable to create a new thing.", delegate: nil, cancelButtonTitle: "OK")
                alert.show()
                return nil
            }
            println("Document: " + thing.document.debugDescription)
            return thing
        }
    }

And my model:

    import Foundation

    let kThingDocType = "thing"

    @objc(Thing)
    class Thing: CBLModel {
        @NSManaged var title: NSString
        @NSManaged var created_at: NSDate

        override func awakeFromInitializer() {
            // initialise things
        }

        class func queryThingsInDatabase(db: CBLDatabase) -> CBLQuery? {
            let view  = db.viewNamed("things")
            if view.mapBlock == nil {
                view.setMapBlock({doc, emit in
                    if (doc["type"] as? NSString == kThingDocType) {
                        emit(doc["title"]!, nil)
                    }
                    },
                    reduceBlock: {keys, values, rereduce in return 0}, version: "1")
            }
            return view.createQuery()
        }

    }
#2
            NSLog("Saving document: %s", thing.propertiesToSave().debugDescription)

The %s should be %@; that’s why it was logging garbage there.

Also, you haven’t set the type property of the Thing you created, so it gets ignored by your map function.

#3

Ah ok thanks. Indeed, I forgot I was in Objective-C world there with the NSLog statement. I changed it to

println("Saving document: " + scale.propertiesToSave().description)

and it outputs the properties fine.

I also changed the other debug statement so it prints the properties rather than the document decription:

        thing.setValue(NSStringFromClass(Thing), ofProperty: "type")
        
        
        println("Saving document: " + thing.propertiesToSave().debugDescription)
        if thing.save(&error) == false {
            let alert = UIAlertView(title: "Error", message: "Unable to create a new thing.", delegate: nil, cancelButtonTitle: "OK")
            alert.show()
            return nil
        }
        println("Document: " + thing.document!.properties.debugDescription)

and I’m now seeing the document properties as I’d expect.
So my issue is most likely to do with my view / query as its always returning 0 rows.

#4

It was the type property that was the issue with regard to saving.
Using NSStringFromClass didnt work as its title cased.

thing.setValue("thing", ofProperty: "type")

Next issue was items not appearing in the tableView. This was due to me copying a chunk of code without understanding it fully. The reduceBlock was causing the query to return 0 each time. Removing it got it working:

class func queryThingsInDatabase(db: CBLDatabase) -> CBLQuery? {
            let view  = db.viewNamed("things")
            if view.mapBlock == nil {
                view.setMapBlock({doc, emit in
                    if (doc["type"] as? NSString == kThingDocType) {
                        emit(doc["title"]!, nil)
                    }
                    },
                    version: "1")
            }
            return view.createQuery()
        }