iosswiftfirebasegoogle-cloud-firestore

Can you save attributed strings to Cloud Firestore?


I'm trying to create a collaborative note-taking app, where the notes are saved as NSAttributedStrings in the iOS app since they contain both images and text.

Can I save a NSAttributedString to Cloud Firestore? If I can, how do I convert the NSAttributedString to a format that's readable by an Android device and website? If it's not possible, what format do I save a note in that contains both images and text (similar to Evernote) that will work across platforms?

EDIT: Are there any markup languages (e.g. HTML, XML) that can be stored in Firestore?


Solution

  • As Doug mentioned in his answer, trying to use NSAttributedString cross platform is challenging as there is no direct Android equivalent so it's probably best to keep the data as primitives.

    ...But the short answer is: Yes, you can store an NSAttributedString in Firestore because Firestore supports NSData objects.

    If you really want to go cross platform with your string style, one thought is to understand that an NSAttributed string is a string with a dictionary of key: value pairs that define the strings look. So you could store primitives in Firestore and then use the appropriate platforms functions to re-assemble the string.

    So the string could be stored in Firestore like this

    string_0 (a document)
       text: "Hello, World"
       attrs: 
          font: "Helvetica"
          size: "12"
          color: "blue"
    

    You could then read that in as create an attributed string based on those attributes.

    That being said, I can get you 1/2 way there on the iOS/macOS side if you really want to store an NSAttributed string in Firestore.

    Here's a function that creates an NSAttributedString, archives it and stores the data in Firestore.

    func storeAttributedString() {
        let quote = "Hello, World"
    
        let font = NSFont.boldSystemFont(ofSize: 20)
        let color = NSColor.blue
    
        let intialAttributes: [NSAttributedString.Key: Any] = [
            .font: font,
            .foregroundColor: color,
        ]
    
        let attrString = NSAttributedString(string: quote, attributes: intialAttributes)
        let archivedData: Data = try! NSKeyedArchiver.archivedData(withRootObject: attrString, requiringSecureCoding: false)
    
        let dict: [String: Any] = [
            "attrString": archivedData
        ]
    
        let attrStringCollection = self.db.collection("attr_strings")
        let doc = attrStringCollection.document("string_0")
        doc.setData(dict)
    }
    

    then to read it back, here's the function that reads it and displays the attributed string an a macOS NSTextField.

    func readAttributedString() {
        self.myField.allowsEditingTextAttributes = true //allows rich text
        let attrStringCollection = self.db.collection("attr_strings")
        let doc = attrStringCollection.document("string_0")
        doc.getDocument(completion: { snapshot, error in
            if let err = error {
                print(err.localizedDescription)
                return
            }
    
            guard let snap = snapshot else { return }
            let archivedData = snap.get("attrString") as! Data
            let unarchivedData: NSAttributedString?  = try! NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(archivedData) as? NSAttributedString
            self.myField.attributedStringValue = unarchivedData!
        })
    }