I am using Flamelink as a headless CMS integrated with Firebase. All of my string fields are working just fine; I am just having a little trouble getting a URL of the media that was uploaded to Firebase Storage.
The collection that I'm getting my string fields from is fl_content
The fields are:
string1
string2
imageUpload
Within Firebase, I can see the data that gets saved from Flamelink:
string1: "Titanium Tee"
string2: "Lower your handicap by 50 with premium Titanium golf tees!"
imageUpload returns an array with a reference to fl_files (a different collection in Firebase)
imageUpload:
0 fl_files/ZqVeXI3vX0rFDuJVDzuR
Under fl_files > ZqVeXI3vX0rFDuJVDzuR, I'm able to see the full filename of the image I upload; the documents in fl_files have a file field. I need to get this filename sent to my object so that I'm able to use the images in my UI.
Here's my progress:
Task:
struct Task{
var string1:String
var string2:String
//var imageUpload:String
var counter:Int
var id: String
var dictionary: [String: Any] {
return [
"string1": string1,
"string2": string2,
//"imageUpload": imageUpload,
"counter": counter
]
}
}
extension Task{
init?(dictionary: [String : Any], id: String) {
guard let string1 = dictionary["string1"] as? String,
let string2 = dictionary["string2"] as? String,
//let imageUpload = dictionary["imageUpload"] as? String,
let counter = dictionary["counter"] as? Int
else { return nil }
self.init(string1:string1, string2: string2, /*imageUpload: imageUpload,*/ counter: counter, id: id)
}
}
VC:
private var documents: [DocumentSnapshot] = []
public var tasks: [Task] = []
private var listener : ListenerRegistration!
fileprivate func baseQuery() -> Query {
return Firestore.firestore().collection("fl_content").limit(to: 50)
}
fileprivate var query: Query? {
didSet {
if let listener = listener {
listener.remove()
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.query = baseQuery()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.listener.remove()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
listenerStuff()
}
func listenerStuff(){
self.listener = query?.addSnapshotListener { (documents, error) in
guard let snapshot = documents else {
print("Error fetching documents results: \(error!)")
return
}
let results = snapshot.documents.map { (document) -> Task in
if let task = Task(dictionary: document.data(), id: document.documentID) {
return task
}
else {
fatalError("Unable to initialize type \(Task.self) with dictionary \(document.data())")
}
}
self.tasks = results
self.documents = snapshot.documents
self.databaseTableView.reloadData()
}
}
How do I query fl_files so that I can populate the imageUpload property of Task with the URL of the uploaded image? Do I do another separate query? Or can I access fl_files from baseQuery()?
EDIT
Here's my attempt at getting to fl_files from fl_content. Trying to just simply populate 2 text fields an an image field (in a UITableViewCell) from Firebase. Is property what I need in getdocument?
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "databaseCellID", for: indexPath) as! DatabaseTableViewCell
let item = tasks[indexPath.row]
cell.label1.text = item.string1
cell.label2.text = item.string2
let docRef = Firestore.firestore().collection("fl_content").document(item.id)
print("PARENT \(docRef.parent)")
docRef.getDocument { (document, error) in
if let document = document, document.exists {
let property = document.get("imageUpload")
// TODO - use this property to get to fl_files?
print("Document data: \(property!)")
}
}
}
The simplest way to do this is probably to write a small Cloud Function that will respond to the uploaded files and place the image URL automatically in your desired collection, allowing for easier querying.
We will attach an
onCreatelistener to thefl_filescollection, then write the downloadURL to the correspondingfl_contentdocument when we see that a new file was created. Note that your actual content fields may be different from the examples I have used here (I'm not personally familiar with Flamelink).Now, you are able to perform just 1 query to
fl_contentand the new file's image URL will be included in the request. The tradeoff we make is we have to perform 1 additional Firestore write to save 50% on all future read requests.Alternatively, without a Cloud Function, we would have to perform 2 separate queries to get the content from
fl_contentandfl_files. As Doug mentioned, it is not possible to perform aJOIN-like query using Firestore due to the scalable way that it was designed.