Sending data to Firebase as an array is giving me bugs
I think I understand the question and there are a few ways to go about it.
1) Group within the post
PlanIt
plans
-LETR-XJvQsZCOpG-T1N
Uid: "ZjtJdkjzuxc0mZn4u9TfWsXa9jh2"
date: "20180609"
images:
image_0: "https://img.evbuc.com/https%3A%2F%2Fcdn.evbuc.c.."
image_1: "https://img.evbuc.com/https%3A%2F%2Fcdn.evbuc.c.."
plans
plan_0: "Bike Rodeo, Safety Presentation and Riding Tour o"
plan_1: "Bike Rodeo, Safety Presentation and Riding Tour o"
title: "This weekend Plans?"
-Yjijiioimoifijjiuee
Uid: "asdaasdas"
etc
The keys of image_0, image_0, plan_0, plan_1 etc would be created with childByAutoId.
With this, you can load a post once and you get all of the needed child data with it.
Note that while this structure appears 'deep' which is usually discouraged in Firebase NoSQL, you won't be performing any queries on the deep data and that data is specific to the post so it's fine in this case.
2) Denormalize/Flatten the structure
PlanIt
plans
-LETR-XJvQsZCOpG-T1N
Uid: "ZjtJdkjzuxc0mZn4u9TfWsXa9jh2"
date: "20180609"
title: "This weekend Plans?"
-UYijs09as9jiosdijfi
Uid: "some uid"
date: "20180609"
title: "some title"
all_images:
-LETR-XJvQsZCOpG-T1N
image_0: "https://img.evbuc.com/https%3A%2F%2Fcdn.evbuc.c.."
image_1: "https://img.evbuc.com/https%3A%2F%2Fcdn.evbuc.c.."
all_plan_details
-LETR-XJvQsZCOpG-T1N
detail_0: "Bike Rodeo, Safety Presentation and Riding Tour o"
detail_1: "Bike Rodeo, Safety Presentation and Riding Tour o"
the second option would require an initial loading of the post node to obtain it's key and then one call to the all_images node to load the images and a call to all_plans to get it's plans. Note that I am using the posts key to reference the all_images and all_plans child node.
The idea here is that we use the key of the PlanIt child as a reference to that node within the all_images and all_plans node. image_0, image_1, plan_0, plan_1 are keys generated with .childByAutoId
The second option is kind cool because for example, say you have a tableview with a list of just titles and dates and then when the user taps it, you can load images or plans into another view... This option would be loading far less data at a time which makes it a bit more manageable memory wise.
EDIT
To clarify the process, here's the code to read in the plan, images and plan details and print them to the console based on the structure above.
let mainRef = self.ref.child("PlanIt")
let plansRef = mainRef.child("plans")
let allImagesRef = mainRef.child("all_images")
let allPlanDetailsRef = mainRef.child("all_plan_details")
plansRef.observeSingleEvent(of: .value, with: { snapshot in
for child in snapshot.children {
let childSnap = child as! DataSnapshot
let dict = childSnap.value as! [String: Any]
let planKey = childSnap.key
let title = dict["title"] as! String
let thisPlansImagesRef = allImagesRef.child(planKey)
let thisPlansDetailsRef = allPlanDetailsRef.child(planKey)
thisPlansImagesRef.observeSingleEvent(of: .value, with: { imageSnapshot in
thisPlansDetailsRef.observeSingleEvent(of: .value, with: { planDetailSnapshot in
//printing is done here because firebase is asynchronous and all data
// will be valid for each node ONLY at this point
print("plan: \(planKey) \(title)")
for imageChild in imageSnapshot.children {
let imageChildSnap = imageChild as! DataSnapshot
let image = imageChildSnap.value as! String
print(" image: \(image)")
}
for planDetailChild in planDetailSnapshot.children {
let planDetailChildSnap = planDetailChild as! DataSnapshot
let planDetail = planDetailChildSnap.value as! String
print(" planDetail: \(planDetail)")
}
})
})
}
})
and the output will be:
plan: plan_0 This weekend plans
image: some image for plan_0
image: another_image for plan_0
planDetail: some plan detail for plan_0
planDetail: another plan detail for plan_0
plan: plan_1 Next weekend plans
I added some text values within my Firebase so it would be more clear what's going on in the output.
How to store an array of images in Firebase Storage all under 1 path using iOS Swift
It turns Firebase won't let you store arrays of anything. After you get the metadata url for each image your going to have to save each one individually inside FBDatabase and FBStorage.
How to finish the upload of images to Firebase Storage and then save the imageUrl to the Firebase Database
Its happen because success block of your .child("post_Images").child(fileName).put
call asynchronously. Rest of code go sync. So your for
start uploading photos and after that you are saving URLs to database but urls are empty because you don't wait for finish uploading.
I give you a perfect solution based on DispathGroup
//Create DispatchGroup
let fetchGroup = DispatchGroup()
for i in imagesArray {
guard let uploadData = UIImageJPEGRepresentation(i, 0.3) else { return }
let fileName = NSUUID().uuidString
//Before every iteration enter to group
fetchGroup.enter()
FIRStorage.storage().reference().child("post_Images").child(fileName).put(uploadData, metadata: nil) { (metadata, err) in
if let err = err {
fetchGroup.leave()
return
}
guard let profileImageUrl = metadata?.downloadURL()?.absoluteString else { return }
self.imageUrls.append(profileImageUrl)
//after every completion asynchronously task leave the group
fetchGroup.leave()
}.resume()
}
And know id magic
fetchGroup.notify(queue: DispatchQueue.main) {
//this code will call when number of enter of group will be equal to number of leaves from group
//save your url here
saveToDatabaseWithImageUrl(imageUrls: imageUrls)
}
This solution don't block a thread, everything works asynchronously here.
Upload multiple images to firebase storage
It is working fine.
if (requestCode == Constants.REQUEST_CODE && resultCode == RESULT_OK) {
ArrayList<Image> images = data.getParcelableArrayListExtra(Constants.INTENT_EXTRA_IMAGES);
Uri[] uri=new Uri[images.size()];
for (int i =0 ; i < images.size(); i++) {
uri[i] = Uri.parse("file://"+images.get(i).path);
storageRef = storage.getReference("photos");
final StorageReference ref = storageRef.child(uri[i].getLastPathSegment());
ref.putFile(uri[i])
.addOnSuccessListener(this, new OnSuccessListener<UploadTask.TaskSnapshot>() {
public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
Uri downloadUrl = taskSnapshot.getDownloadUrl();
String content = downloadUrl.toString();
if (content.length() > 0) {
editWriteMessage.setText("");
Message newMessage = new Message();
newMessage.text = content;
newMessage.idSender = StaticConfig.UID;
newMessage.idReceiver = roomId;
newMessage.timestamp = System.currentTimeMillis();
FirebaseDatabase.getInstance().getReference().child("message/" + roomId).push().setValue(newMessage);
}
}
});
}
}
Closure returning data before async work is done
You need dispatch group as the for loop contains multiple asynchronous calls
public func SetupArticleListener(completion: @escaping ([Article]) -> Void) {
var returnArray = [Article]()
let db = FIRdb.articles.reference()
let listener = db.addSnapshotListener() { (querySnapshot, error) in
returnArray = [] // nil this out every time
if let error = error {
printLog("Error retrieving documents while adding snapshotlistener, Error: \(error.localizedDescription)")
} else {
let g = DispatchGroup() /// 1
querySnapshot?.documents.forEach {
var dataDict = $0.data() //mutable copy of the dictionary data
let id = $0.documentID
if let imageURL = $0.data()["image"] as? String {
let reference = FIRStorage.articles.referenceForFile(filename: imageURL)
g.enter() /// 2
self.getURL(reference: reference){ result in
switch result {
case .success(let url) :
print("Success in getting url from reference \(url)")
dataDict["image"] = url.absoluteString
print("Dictionary XFORM")
case .failure(let error):
print("Error retrieving URL from reference \(error)")
}
if let newArticle = Article(id: id, dictionary: dataDict) {
printLog("Success in creating Article with xformed url")
returnArray.append(newArticle)
}
g.leave() /// 3
}
}
}
g.notify(queue:.main) { /// 4
print(" sending back completion array \(returnArray))
completion(returnArray)
}
}
}
updateListeners(for: listener)
}
Related Topics
Xcode 7.1 Beta: Content of File Error
Handle Single Click and Double Click While Updating the View
Different Path Url for Filemanager Everytime I Open the App
How to Use Dispatchgroup/Gcd to Execute Functions Sequentially in Swift
How Assistant Model Works in Swift
Receiving Data from Nsinputstream in Swift
Which Measuring Unit Is Used in Scnvector3 Position for X, Y and Z in Arkit
Uitextview Linktextattributes Font Attribute Not Applied to Nsattributedstring
Nsurl Fail Able Initialiser Initwithstring: Does Not Return Nil on Empty String in Swift
How to Pass Closure as a Parameter in Perform(Selector, Withobject)
How to Handle Async Requests in Swift
Realm Data Doesn't Show Up on a Physical Device
How to Create an Operator to Implement Error Chaining
How to Procedurally Draw Rectangle/Lines in Swift Using Cgcontext