Yes
CocoaPodsIf I would like to call map on a documents array from firestore, the SwiftUI canvas in Xcode crashes.
Note that the same code works flawlessly if I run the app in the simulator. Only the canvas view is affected. With canvas I refer to the PreviewProvider feature in SwiftUI.
Please also note that https://github.com/firebase/firebase-ios-sdk/issues/4271 is somehow related - but in my case I don't have multiple internal frameworks.
Precondition failed: NSArray element failed to match the Swift Array Element type
Expected FIRQueryDocumentSnapshot but found FIRQueryDocumentSnapshot: file /BuildRoot/Library/Caches/com.apple.xbs/Sources/swiftlang/swiftlang-1100.2.280/swift/stdlib/public/core/ArrayBuffer.swift, line 354
self.db.collectionGroup("Foo").addSnapshotListener { querySnapshot, err in
guard let documents = querySnapshot?.documents else {
return
}
let data = documents.map { Foo(document: $0) } // canvas crash
[...]
}
To clarify, when you run your app in simulator or on device there aren't warnings printed to console about duplicated class definitions, right? If this is the case, it may be an Xcode (SwiftUI preview) bug.
Yes, no logs when I run my app in the simulator / on device. I also thought it may be a SwiftUI bug, but I'm not sure.
I don't even rely on data from firestore for previews, because I feed the preview with mock data. However, the code does (of course) run for previews as well.
Does the error go away if you replace the map with a for loop?
Remains, exact same crashlog.
Can you share your project (or is it reproducible with just the code snippet in the original issue)?
Should be reproducible, but I'm going to create a minimum project that I can share.
Here you go: https://github.com/adriano-s/BugReportDemo
Thank you very much for doing that - can you please delete the project and re-upload it without a GoogleService-Info.plist? Just want to make sure it's not easily accessible.
Done. Thanks for the hint. I googled before to make sure it is okay to push (https://stackoverflow.com/a/44937513/4041516). I was uncomfortable though, and made the demo db at least readonly :)
But now, even better - no GoogleService-Info.plist anymore! Of course you now have to setup your own db.
Great defensive planning! Yeah it's not really a big deal but always easier to just exclude it entirely and have any person using it provide their own project 馃槃 I'll take a look and see what I can figure out.
I was able to reproduce the crash, thank you so much for a simple repro. It helps a ton.
Good news: it looks like it's not a Firebase specific crash (thankfully)! Seems like a bug with the SwiftUI preview.
Bad news: I haven't found a way around the bug.
I changed the contents of addCoffeeListener to this:
func addCoffeeListener() {
// Delay it a second just to simulate an async call.
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
let fakeCoffees: [String] = ["Frappuccino", "Tea"]
self.coffees = fakeCoffees.map { name in
return Coffee(id: "some_id", name: name, grown: Date())
}
}
}
And I got the same error.
It could have to do with the flurry of logs appearing beforehand, which includes this:
objc[61517]: Class FIRQueryDocumentSnapshot is implemented in both /Users/<MY_HOME_DIR>/Library/Developer/Xcode/UserData/Previews/Simulator Devices/95075E0C-9B2F-4CB3-8983-BFA735553F40/data/Containers/Bundle/Application/2CEB4E00-DE84-4432-8FF7-E144CFC4328D/BugReproDemo.app/BugReproDemo (0x11068e750) and /Users/<MY_HOME_DIR>/Library/Developer/Xcode/DerivedData/BugReproDemo-dtkgkyszbbflmzhaqlmyngkwtqic/Build/Intermediates.noindex/Previews/BugReproDemo/Intermediates.noindex/BugReproDemo.build/Debug-iphonesimulator/BugReproDemo.build/Objects-normal/x86_64/ContentView.6.preview-thunk.dylib (0x14327a460). One of the two will be used. Which one is undefined.
but even when using a plain Swift type, we still see the same thing.
I'm going to close this for now since it's not a Firestore issue, but feel free to continue the conversation and link any Feedback Reports if you file one (which I do recommend).
Oh also - one more tip that I forgot to mention: All @State variables should be private based on Apple's recommendation. You can't inject coffees in the view directly if it's @State. If you want to do that, you should use @Binding instead and have a @State in the parent view.
"Only access a state property from inside the view鈥檚 body (or from functions called by it). For this reason, you should declare your state properties as private, to prevent clients of your view from accessing it." (source: Apple Documentation)
Thank you for having a look into this issue @ryanwilson , and as well for the tip ! Much appreciated.
I'm going to file a Feedback Report, and will link it here.
Hi @ryanwilson
I tried your plain Swift type example - and have no errors in the preview.
For me, it's only crashing if I iterate over an array of FIRQueryDocumentSnapshot respectively QueryDocumentSnapshot.
I've pushed your change to the demo repo.
Could you may have a look into this again?
Apologies for the delay - you're totally right and it's not reproducing for me either. I'lll reopen this to keep track!
I had the same problem with cocoapods umbrella frameworks and I solve problem with loop "for i in 0.. Maybe it's not the better way but it worksdatabase.collection("<Your_Collection>").document("<Your_ID_DOC_IF_HAVE>").collection("<Your_ANOTHER_COLLECTION_IF_HAVE>").getDocuments()
{ [weak self] (snapshot,err) in
guard let self = self else {return}
if let error = err {
print("Couldn't read sessionsDocument \(error)")
} else {
if (snapshot?.documents.count ?? 0) > 1 {
let countDocs = snapshot!.documents.count
for i in 0..<countDocs{
print("DATA: \(snapshot!.documents[i].data())")
let data = snapshot!.documents[i].data()
// use data here
}
}else{
// Error handler empty data
}
}
}
This error it's very common when you work with umbrella framework technique.
I was getting " _Precondition failed: NSArray element failed to match the Swift Array Element type
Expected FIRQueryDocumentSnapshot but found FIRQueryDocumentSnapshot bla bla bla bla..._ " crash error in spite of having just one firebase instance reference to static framework working.
So I had to use the powerful NSObject features in particular Key-Value Coding protocol.
Check this....
let db = Firestore.firestore(app: firebase_instance)
defaultsHelper.write(value: true, key: .isReceivingProspects)
prospectosListener = db.collection(<-collection_name->)
.document(<-document_name->)
.collection(<-collection_name->)
.whereField(<-paremeter_name->, arrayContains: <-conditions->)
.addSnapshotListener { querySnapshot, error in
weak var _self = self
guard let snapshot = querySnapshot else {
return
}
//snapshot.documents or snapshot.documentChanges in a loop produces crash
guard let documents = (snapshot as NSObject).value(forKey: "documentChanges") as? NSArray else { return }
for document in documents {
guard let object = document as? NSObject else { debugPrint("object was nil"); return }
guard let type = object.value(forKey: "type") as? Int else { debugPrint("type was nil"); return }
guard let docs = object.value(forKey: "document") as? NSObject else { debugPrint("document was nil"); return }
guard let data = docs.value(forKey: "data") as? [String: Any] else { debugPrint("data was nil"); return }
guard let fbModel = _self?.documentConverter.convertToNotificationModel(documentData: data) else {
debugPrint("fbModel was nil")
return
}
switch type {
case 0: // Added
_self?.onAddedOrModifiedNotificationEvent(fbModel: fbModel)
case 1: // Modified
_self?.onAddedOrModifiedNotificationEvent(fbModel: fbModel)
case 2: // Removed
_self?.onDeleteNotificationEvent(fbModel: fbModel)
default:
debugPrint("Another option")
}
}
Good luck 馃憤 馃崁