2

I am building a Share Extension in Swift which saves a document to Firestore. So far I have been able to authenticate the correct user via keychain sharing and app groups. I can also get a documentID from a new document reference:

var ref = Firestore.firestore().collection("stuff").document()
print(ref.documentID) //prints the id

But when I try to save something to Firestore, nothing prints in the console, meaning I get neither a failure or success callback from Firebase (see below where I batch the updates). Here is my ShareController.swift file:

class ShareViewController: SLComposeServiceViewController {

  var sharedIdentifier = "asdf"

    override func viewDidLoad() {
      FirebaseApp.configure()
      setupKeychainSharing()
    }

    func setupKeychainSharing() {

      do {
        try Auth.auth().useUserAccessGroup(sharedIdentifier)
      } catch let error as NSError {

      }
    }

    override func isContentValid() -> Bool {
      return true
    }

    override func didSelectPost() {

      if let content = extensionContext!.inputItems[0] as? NSExtensionItem {
        if let contents = content.attachments {
          for attachment in contents {

            if attachment.hasItemConformingToTypeIdentifier(m4aType) {

              attachment.loadItem(forTypeIdentifier: m4aType, options: nil, completionHandler:  { (results, error) in
                if error == nil {
                  if let url = results as? URL {

                    if let audioData = NSData(contentsOf: url) {
                      let fileName = url.lastPathComponent
                      if Auth.auth().currentUser != nil {

                        guard let myId = Auth.auth().currentUser?.uid else { return }

                         let batch = Firestore.firestore().batch()

                         let ref = Firestore.firestore().collection("projects").document()

                         let project: [String: Any] = [
                           "ownerId": myId,
                           "type" : "audio",
                           "extensionUrl" : audioUrl.absoluteString
                         ]

                         batch.updateData(project, forDocument: ref)

                         let privateRef = Firestore.firestore().collection("user-private").document(myId)
                         let privateUpdate: [String: Any] = [
                           "projects" : FieldValue.arrayUnion([ref.documentID])
                          ]
                          batch.updateData(privateUpdate, forDocument: privateRef)
                          batch.commit(completion: { (error) in
                            if let error = error {
                              print("error updating database: \(error.localizedDescription)")
                            } else {
                              print("Database updated successfully!!!!!")
                              self.extensionContext!.completeRequest( returningItems: [], completionHandler: nil)
                            }
                          })
                        }
                      }
                    }
                  }
                })
              }
            }
          }
       } 
    }
}
Jay
  • 34,438
  • 18
  • 52
  • 81
mnearents
  • 646
  • 1
  • 6
  • 31
  • Wow. That's a whole lot of code and telling us that 'nothing happens' indicates you've not really done any troubleshooting. You should set a breakpoint and step through your code inspecting your variables along the way. When something isn't right, that's where your problem is. Also, please take a moment and review [How do I ask a good question?](https://stackoverflow.com/help/how-to-ask) and [How to create a Minimal, Complete, and Verifiable example](https://stackoverflow.com/help/mcve). Condense and update your question and we'll take a look! – Jay Jul 01 '19 at 18:03
  • what is the issue? share logs debug steps you alreayd tried will really help to understand the issue – yoga Jul 01 '19 at 18:15
  • I'm not sure how else to say it. The issue is nothing prints in the `batch.commit()` callback. Everything else prints just fine. I can get the correct currentUser's uid from Firebase Auth, so I know I'm authenticated. I can get a new documentID by calling `.document()` so I know I am communicating with Firebase, unless that id is generated locally. But once the `batch.commit()` command is called, no error or success message prints. It's like the ShareExtension is being terminated before the document can be saved, but I'm not sure how to verify that. – mnearents Jul 01 '19 at 18:19
  • I've tried creating a new app within my Firebase project using the Share Extension bundle identifier and importing the GoogleService-info.plist into the Share Extension, but it said the bundle Id didn't match. So I just used the same GoogleService-info.plist file from my base app. I've tried saving to Firestore without the batch command, just calling `ref.setData()` directly and still nothing printed. I would usually expect some kind of error that I could troubleshoot, but I'm not getting one. – mnearents Jul 01 '19 at 18:22
  • Did you manage to fix this? I am facing the same issue... Firestore won't perform the query in the Share Extension – Tal Barda May 02 '20 at 11:38

2 Answers2

0

It appears you're trying to create additional documents within the projects node and update the user-private node. If so, the code in the question won't work.

UpdateData: Updates fields in the document referred to by document. If document does not exist, the write batch will fail.

Here's a working batch write function that adds a document to a projects node with a Firestore generated document ID and child fields for extension, ownerId and type as well as a user_private collection with a document with a documentId of id_0.

func batchWrite() {
    let batch = self.db.batch()

    let ref = self.db.collection("projects").document()
    let project: [String: Any] = [
        "ownerId": "id_0",
        "type" : "audio",
        "extensionUrl" : "some url"
    ]
    batch.setData(project, forDocument: ref)

    let privateRef = self.db.collection("user-private").document("id_0")
    let privateUpdate: [String: Any] = [
        "projects" : "some projects"
    ]
    batch.setData(privateUpdate, forDocument: privateRef)

    batch.commit(completion: { (error) in
        if let error = error {
            print("error updating database: \(error.localizedDescription)")
        } else {
            print("Database updated successfully!!!!!")
        }
    })
}

Note that self.db is a class var reference to my Firestore. That makes it so you don't have to keep typing Firestore.firestore() and use self.db instead.

Also note that a batch is probably not needed in this case as it doesn't appear there are a significant number of writes occurring at the same time.

If you're not using batch, the .addDocument will add documents to collections.

Here's a function that writes a task to a tasks collection and auto-generates a documentId

func writeTask() {
    let collectionRef = self.db.collection("tasks")
    let data = [
        "task": "some task"]
    var ref: DocumentReference? = nil
    ref = collectionRef.addDocument(data: data, completion: { err in
        if let error = err {
            print(error.localizedDescription)
        }

        print(ref?.documentID)
    })
}
Jay
  • 34,438
  • 18
  • 52
  • 81
  • I simplified the code following your example and followed your suggestion of not using batch, so I called `ref.setData([])` to create the document, and `privateRef.updateData([])` and still nothing. It doesn't even print an error, which I would assume if it was a failed update, it would throw an error. – mnearents Jul 05 '19 at 02:01
  • @sharkonaut This `ref.setData([])` doesn't do anything. If you want to add a document to a collection then use .addDocument. I update the answer with an example. – Jay Jul 05 '19 at 16:16
  • I don’t think that’s true. The docs say: “if the document does not exist, it will be created. If the document does exist, it’s contents will be overwritten...” Although I will try it with ‘.addDocument’ and see if that makes a difference. – mnearents Jul 05 '19 at 16:22
  • @sharkonaut It is 100% true. A document that is empty does not exist - well it can but it's empty, and that's what you're doing with that line. Since you have data, write it out when the document is created. Check out my updated answer for some sample code. – Jay Jul 05 '19 at 16:24
0

It is likely Firebase wasn't configured for your Share extension. Print statements do not work for share extension. Instead you should make use of NSLog statements i.e NSLog("refDocId:\(ref.DocumentId)"). Then in your xcode, navigate to Window > Devices and Simulators > Open Console. To configure Firebase in your Share extension, use FirebaseApp.configure()

mechdon
  • 517
  • 1
  • 10
  • 23