1

I have a class to handle a simple note creator in my app. At the moment, notes are stored using an array of custom Note objects. How can I save the contents of this array when the app closes and load them again when the app is re-opened? I've tried NSUserDefaults, but I can't figure out how to save the array since it isn't just comprised of Strings.

Code:

Note.swift

class Note {
    var contents: String

    // an automatically generated note title, based on the first line of the note
    var title: String {
        // split into lines
        let lines = contents.componentsSeparatedByCharactersInSet(NSCharacterSet.newlineCharacterSet()) as [String]
        // return the first
        return lines[0]
    }

    init(text: String) {
        contents = text
    }


}

        var notes = [
    Note(text: "Contents of note"),]
Tobi Nary
  • 4,566
  • 4
  • 30
  • 50
Elan
  • 23
  • 5

1 Answers1

3

There are different approaches to this.

NSCoding

The easiest would be to adopt NSCoding, let Note inherit from NSObject and use NSKeyedArchiver and NSKeyedUnarchiver to write to/from files in the app's sandbox.

Here is a trivial example for this:

final class Feedback : NSObject, NSCoding {
    private static let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0]

    let content : String
    let entry : EntryId
    let positive : Bool
    let date : NSDate


    init(content: String, entry: EntryId, positive : Bool, date :NSDate = NSDate()) {
        self.content = content
        self.entry = entry
        self.positive = positive
        self.date = date

        super.init()
    }

    @objc init?(coder: NSCoder) {
        if let c = coder.decodeObjectForKey("content") as? String,
            let d = coder.decodeObjectForKey("date") as? NSDate {
                let e = coder.decodeInt32ForKey("entry")
                let p = coder.decodeBoolForKey("positive")
                self.content = c
                self.entry = e
                self.positive = p
                self.date = d
        }
        else {
            content = ""
            entry = -1
            positive = false
            date = NSDate()
        }
        super.init()
        if self.entry == -1 {
            return nil
        }
    }

    @objc func encodeWithCoder(aCoder: NSCoder) {
        aCoder.encodeBool(self.positive, forKey: "positive")
        aCoder.encodeInt32(self.entry, forKey: "entry")
        aCoder.encodeObject(content, forKey: "content")
        aCoder.encodeObject(date, forKey: "date")
    }

    static func feedbackForEntry(entry: EntryId) -> Feedback? {
        let path = Feedback.documentsPath.stringByAppendingString("/\(entry).feedbackData")
        if let success = NSKeyedUnarchiver.unarchiveObjectWithFile(path) as? Feedback {
            return success
        }
        else {
            return nil
        }

    }

    func save() {
        let path = Feedback.documentsPath.stringByAppendingString("/\(entry).feedbackData")
        let s = NSKeyedArchiver.archiveRootObject(self, toFile: path)
        if !s {
            debugPrint("Warning: did not save a Feedback for \(self.entry): \"\(self.content)\"")
        }
    }
}

Core Data

The more efficient but more complex solution is using Core Data, Apple's ORM-Framework - which's usage is way beyond the scope of a SO answer.

Further Reading

Tobi Nary
  • 4,566
  • 4
  • 30
  • 50
  • Excellent answer. Core Data is a bear to figure out though. It took me several days of intense study and experimentation before "the light bulb turned on" and I started it to get it. For something simple like an array of objects I'd say NSCoding is fine, and not worth investing in the Core Data learning curve. – Duncan C Feb 20 '16 at 02:55
  • @DuncanC that's why the `NSCoding` part is so extensive and the core data part is quite short. There may be multithreading issues that should be taken into consideration when there is multithreading; core data would be a better fit then. – Tobi Nary Feb 20 '16 at 09:22
  • Thanks for the great answer! Could you show me how I would save the data in my Note class? I'm fairly new to Swift and not entirely understanding your example. – Elan Feb 26 '16 at 17:34
  • Can you highlight particular understanding issues? I'm not writing your code for you, but am willing to clarify my answer, if there are any unclear bits. – Tobi Nary Feb 26 '16 at 17:36