How to Group an Array of Image Urls Together in Firebase Realtime Database? (Swift)

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



Leave a reply



Submit