4

I have a Core Data entity (type) called NoteEntity. It has a managed variable called noteDocument, which is of the custom type NoteDocument (my subclass of NSDocument). I changed its auto-generated NoteEntity+Core Data Properties class so it reads

import Foundation
import CoreData

extension NoteEntity {

    @NSManaged var noteDocument: NoteDocument? // changed
    @NSManaged var belongsTo: NSSet?

}

so that noteDocument is of type NoteDocument instead of NSObject. The NoteDocument class does implement NSCoding, as follows:

required convenience init(coder theDecoder: NSCoder)
{
    let retrievedURL = theDecoder.decodeObjectForKey("URLKey") as! NSURL
    self.init(receivedURL: retrievedURL)
}

func encodeWithCoder(theCoder: NSCoder)
{
    theCoder.encodeObject(fileURL, forKey: "URLKey")
}

What I want to be able to do is find the noteEntity entities in the managed context with a given noteDocument value. So I run this code (passing it a parameter theNote that corresponds to a noteDocument that I know exists in the managed context):

    var request = NSFetchRequest(entityName: "NoteEntity")
    let notePredicate = NSPredicate(format: "noteDocument == %@", theNote)
    request.predicate = notePredicate

    print("Text in the NoteEntity with the NoteDocument for "+theNote.filename+":")

    do
    {
        let notesGathered = try context.executeFetchRequest(request) as? [NoteEntity]

        for n in notesGathered!
        {
            print (n.noteDocument!.filename)
            print (n.noteDocument!.noteText)
        }
    }
    catch let error as NSError
    {
        print("Could not run fetch request. \(error), \(error.userInfo)")
    }

but it returns no entries. If I comment out the predicate, I get all the NoteEntity values in the database, but with the predicate in there I get nothing. Clearly something is wrong with the search I'm trying to do in the predicate. I think it's because the value is a Transformable, but I'm not sure where to go from there. I know you can't run fetch requests on the members of Transformable arrays, but is it not possible to run fetch requests on single Transformable attributes? If it isn't, what alternatives exist?

EDIT: The NoteDocument class includes a lot more than the NSCoding. As I said, it's an NSDocument subclass. The NSCoding uses a URL as its key because that's the "primary key" for the NoteDocument class - it's what initializes the class. Here is the rest of the class, not including the NSCoding above:

import Cocoa

class NoteDocument: NSDocument, NSCoding
{
var filename: String
var noteText: String
var attributes: NSDictionary?
var dateCreated: NSDate?
var dateString: String?

init (receivedURL: NSURL)
{
    self.filename = ""
    self.noteText = ""
    super.init()

    self.fileType = "net.daringfireball.markdown"
    self.fileURL = receivedURL

    // Try to get attributes, most importantly date-created.
    let fileManager = NSFileManager.defaultManager()
    do
    {
        attributes = try fileManager.attributesOfItemAtPath(fileURL!.path!)
    }
    catch let error as NSError
    {
        print("The error was: "+String(error))
    }

    if let dateCreated = attributes?.fileCreationDate()
    {
        // print("dateCreated is "+String(dateCreated!))

        // Format the date-created to an appropriate string.
        dateString = String(dateCreated)
    }
    else
    {
        print("Did not find the attributes for "+filename)
    }

    if let name = self.fileURL?.lastPathComponent
    {
        filename = name
    }
    else
    {
        filename = "Unnamed File"
    }

    noteText = ""

    do
    {
        noteText = try NSString(contentsOfURL: self.fileURL!, encoding: NSUTF8StringEncoding) as String
    }
    catch let error as NSError
    {
        print("Error trying to get note file:"+String(error))
    }
}

// MARK: - Document functions

override class func autosavesInPlace() -> Bool
{
    // print ("autosavesInPlace ran.")
    return true
}

override func dataOfType(typeName: String) throws -> NSData
{        
    var outError: NSError! = NSError(domain: "Migrator", code: 0, userInfo: nil)

    // Post: Document is saved to a file specified by the user.

    outError = NSError(domain: NSOSStatusErrorDomain, code: unimpErr, userInfo: nil)

    if let value = self.noteText.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false) {
        // Convert noteText to an NSData object and return that.
        return value
    }
    print("dataOfType ran.")

    throw outError

}

override func readFromData(data: NSData, ofType typeName: String) throws
{
    // Currently unused; came free with NSDocument.
    throw NSError(domain: NSOSStatusErrorDomain, code: unimpErr, userInfo: nil)
}


}
Displaced Hoser
  • 871
  • 3
  • 13
  • 35

1 Answers1

1

In the code you show that the only thing you're coding is a URL. In this case it makes much more sense and provides more utility to use a plain string in the entity to store the URL and to add a transient attribute (or create a wrapper class to combine the entity and document) for the document. In this way you can use the URL in the predicate and it's easy to build the predicate from a document. Storing the document as a transformable isn't helping you in any way it seems.

Wain
  • 118,658
  • 15
  • 128
  • 151
  • See above edit. I am trying to store the whole document so that changes to the document (especially but not only the noteText variable) can be saved within the persistent store (and not just the filesystem). The URL was just the key I used for the NSCoding part. It's clear to me now that that's where my problem is, but I'm not sure what to do differently. Most examples I've seen of NSCoding do something like this: take a single attribute as the key that goes into the store and leave it like that. Is there a better way to implement the transformable? – Displaced Hoser Jul 28 '16 at 11:43
  • my answer stands - in core data transformable you only get what is saved by your `NSCoding` implementation. the whole document is not saved in the persistent store. why do you even want 2 different copies of the same thing saved ? – Wain Jul 28 '16 at 12:21
  • Well, my hope was that a relational-database-based persistent store would be faster to load from than the hard drive/secondary memory. So that I would check the date-modified on the version in the file system vs. the persistent store, and only load the document from the filesystem if it was more recent than the version in the store. I guess if I still wanted to do that, the way to do it would be to have the NSCoding encode the note-text and date-modified variables as keys alongside the URL? – Displaced Hoser Jul 29 '16 at 11:53
  • Yes, but the only benefit of the core data store is partial loading, loading won't be any faster for the whole data set. If you put all the data in the same transformable there will be basically zero difference. For a speed benefit you should look at what you can keep in memory and what data is used less frequently so you don't need to load it until explicitly requested. – Wain Jul 30 '16 at 08:12
  • Fair. Haven't had a chance to test this because there's also [a bug](http://stackoverflow.com/questions/38064830/proper-parameters-for-closealldocumentswithdelegate) that's crashing the program in the interim, but I want to make sure the bounty is awarded, and your answer makes sense. I'm going to try it just encoding the URL as you recommend, and if I decide I do need to put more elements of the Document in the database then I'll just encode more variables alongside the URL. – Displaced Hoser Aug 02 '16 at 11:54