How to download Firebase results as set? - firebase

I have the following wrapped in a completion block:
dbRef.child("Employees").queryOrdered(byChild: "deptid").queryEqual(toValue: "100").observe(.childAdded, with: {
snapshot in
//add each result to an array
The problem is that the completion fires for each result. I need it to fire after all results have downloaded, which means blocking. How can I go about doing this?

Try changing to :-
dbRef.child("Employees").queryOrdered(byChild: "deptid").queryEqual(toValue: "100").observeSingleEvent(of : .value, with:{ snapshot in
if let snapDict = snapshot.value as? [String:AnyObject]{
for each in snapDict{
let deptID = each.value["deptid"] as! String
}
}
})

Related

How to access workouts from HealthKit on WatchOS?

I'm trying to read workouts from a WatchOS App. Unfortunately, the returned workouts are always empty when I read the workouts on WatchOS.
This is how it's currently implemented:
func fetchWorkouts(limit: WorkoutLimit) async throws -> [HKWorkout] {
let activityType = PulseConfiguration.activityType
// 1. Get all workouts with the configured activity type.
let walkingPredictate = HKQuery.predicateForWorkouts(with: activityType)
// 2. Get all workouts that only came from this app.
let sourcePredicate = HKQuery.predicateForObjects(from: .default())
// 3. Combine the predicates into a single predicate.
let compound = NSCompoundPredicate(andPredicateWithSubpredicates:
[walkingPredictate, sourcePredicate])
let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)
typealias WorkoutsContinuation = CheckedContinuation<[HKWorkout], Error>
return try await withCheckedThrowingContinuation { (continuation: WorkoutsContinuation) in
let query = HKSampleQuery(
sampleType: .workoutType(),
predicate: compound,
limit: limit.count,
sortDescriptors: [sortDescriptor]
) { _, samples, error in
guard let samples = samples as? [HKWorkout], error == nil else {
if let error = error {
continuation.resume(throwing: error)
}
return
}
continuation.resume(returning: samples)
}
healthStore.execute(query)
}
}
I noticed that the HKSource returned by .default (WatchOS) is different than the source associated to the saved workouts (Which contains the bundle identifier from the iPhone App).
Is there some other way to access the workouts from the WatchOS App?
Thanks in advance. Josh.
Watch has a limited subset of health data available. You can use HKHealthStore.earliestPermittedSampleDate to see how far back data you can query for.

How to get data from firebase query based on value from another firebase query in FutureBuilder in Flutter?

I am new to flutter and I am sure there is a simple way of doing this. Let me first give you a background. I have 2 tables(collections). The first one store a mapping. Therefore it returns a key based on an id which will be used to query the second table and retrieve the data from firebase.
I have written 2 data models and 2 functions which return Future<> data. They are as follows-
Future<SpecificDevice> getSpecificDevice(String deviceId) {
Future<SpecificDevice> obj =_database.reference().child("deviceMapping").orderByChild("deviceId").equalTo(deviceId).once().then((snapshot) {
SpecificDevice specificDevice = new SpecificDevice(deviceId, "XXXX", new List<String> ());
if(snapshot.value.isNotEmpty){
print(snapshot.value);
snapshot.value.forEach((key,values) {
if(values["deviceId"] == deviceId) {
specificDevice.deviceKey = values["deviceDesc"];
specificDevice.vendorList = List.from(values["vendorList"]);
}
});
}
return specificDevice;
});
return obj;
}
This function gets the mapping deviceId -> deviceKey.
This is the key of record stored in another table. Following is the function for it.
Future<Device> getDeviceDescription(String deviceKey) {
Future<Device> device = _database.reference().child("deviceDescription").once().then((snapshot) {
Device deviceObj = new Device("YYYY", "YYYY", "YYY", "YYYY", "YYYY");
if(snapshot.value.isNotEmpty){
print(snapshot.value);
//Future<SpecificDevice> obj = getSpecificDevice(deviceId);
//obj.then((value) {
snapshot.value.forEach((key,values) {
if(key == deviceKey) { // compare with value.deviceKey instead
print(values["deviceDescription"]); // I get the correct data here.
deviceObj.manual = values["deviceManual"];
deviceObj.deviceType = values["deviceType"];
deviceObj.description = values["deviceDescription"];
deviceObj.brand = values["deviceBrand"];
deviceObj.picture = values["devicePicture"];
}
// });
});
}
return deviceObj;
});
return device;
}
Now both of these functions work. I want to make it work one after the other. In the above function, if I uncomment the lines of code, the data is retrieved properly in the inner function but it returns initial default values set because the values get returned before setting the obj of SpecificDevice.
Here is where I am getting the error. I am calling the second function in FutureBuilder<> code with the above lines uncommented and taking input param as deviceId.
return FutureBuilder<Device>(
future: getDeviceDescription(deviceId),
builder:(BuildContext context,AsyncSnapshot snapshot){... // using snapshot.data in its child.
Here in snapshot.data. would give me YYYY. But it should get me the value from the database.
I am stuck with this for a while. Any help in fixing this? or if what I am trying to do is clear then please suggest me a better way to approach this. Thanks in advance!
The answer is rather simple:
first and foremost - you forgot to use async / await keywords, which will guarantee synchronous data retrieval from the database. Always use them, if you are connecting to any network service
to make one command work after another - use .then((value) {}). It will get data from the first function (which you pass using return) and use it in the second function.
Solved the problem by changing the calling function to -
return FutureBuilder<Device>(
future: getSpecificDevice(deviceId).then((value){
return getDeviceDescription(value.deviceKey);
}),
builder:(BuildContext context,AsyncSnapshot snapshot){

Firestore Pagination query on collection group data

Description: I want to implement infinite scroll in React-Native with firebase and i want to paginate on collection group data for instant result in my app. Error is shown in screenshot.
My code:
var ref = firestore().collectionGroup('user_posts').orderBy('time_stamp', 'desc').limit(5);
ref.get().then(snapshot => {
var posts = []
var lastVisible = snapshot.docs[snapshot.docs.length - 1];
this.setState({ last_Visible: lastVisible });
});
var next = firestore().collectionGroup('user_posts').orderBy('time_stamp', 'desc')
.startAfter(this.state.last_Visible)
.limit(5);
next.get()
.then(FirestoreDocumentSnapshot => {
var lastVisible = FirestoreDocumentSnapshot.docs[FirestoreDocumentSnapshot.docs.length - 1];
this.setState({ last_Visible: lastVisible });
})
Please help, what i am doing wrong?
From a first look at the error and your code it seems like either the fields that you are referring to are empty, or the lastVisible should be an even number.
Try checking that you don't have empty fields in your docs and try removing the -1 from lastVisible.
Here you can check the proper way to use startAfter() which is the cause of your issue.
As you're sorting by time_stamp, you should specify that field in the query like so:
.startAfter(this.state.last_Visible.data().time_stamp)
Here is an example from the official documentation. (From the language tabs choose Node.js)

Getting Depth Data from UIImagePickerController

I am trying to get the depth data associated with an image in the PhotoLibrary.
I can get the image, and the URL, but I can't seem to get the aux data associated with it. The call to CGImageSourceCreateWithURL returns a source, but the call to CGImageSourceCopyAuxiliaryDataInfoAtIndex returns nil for both kCGImageAuxiliaryDataTypeDisparity and kCGImageAuxiliaryDataTypeDepth.
Is there something I am missing here?
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
let image = info[UIImagePickerControllerOriginalImage]
let url = info[UIImagePickerControllerImageURL]
print("url=",url)
guard let source = CGImageSourceCreateWithURL(url as! CFURL, nil) else {
return
}
guard let auxDataInfo = CGImageSourceCopyAuxiliaryDataInfoAtIndex(source, 0, kCGImageAuxiliaryDataTypeDisparity) as? [AnyHashable : Any] else {
return
}
}
I struggled with this for a whole day! I finally figured it out, though, after watching the first half of the WWDC Video titled "Editing Images With Depth."
My problem was using a URL for the image that was not from the PHAsset.
Here is the link:
LINK TO WWDC VIDEO
If you don't feel like watching it, check out this function that I wrote that does pretty much exactly what is done in the video.
You have to provide the function the [info] that is returned from the DID_FINISH_PICKING_IMAGE_WITH_INFO function from the UIImagePickerDelegate.
Before using this function - note that is actually doesn't work! It is great to look at though, because it shows the steps clearly. But due to asynchronous behavior, the function will always return nil before it has a chance to set the local depth variable to the AVDepthData.
My solution was to break the function apart and use Grand Central Dispatch to create a Dispatch Group, enter it, retrieve the imageURL from the PHAsset, and then leave the Dispatch Group. Upon leaving the Dispatch Group, the DispatchGroup.NOTIFIED function then proceeded with the rest of the process.
I hope this helps!!!
func returndepthdata(usingimageinfo: [UIImagePickerController.InfoKey : Any]) -> AVDepthData? {
var depthdata: AVDepthData! = nil
if let photoasset = usingimageinfo[.phAsset] as? PHAsset {
let input = photoasset.requestContentEditingInput(with: nil, completionHandler: { (input, info) in
if let imageurl = input?.fullSizeImageURL {
if let source = CGImageSourceCreateWithURL(imageurl as CFURL, nil) {
if let imageproperties = CGImageSourceCopyProperties(source, nil) {
if let disparityinfo = CGImageSourceCopyAuxiliaryDataInfoAtIndex(source, 0, kCGImageAuxiliaryDataTypeDisparity) {
if let truedepthdata = try? AVDepthData(fromDictionaryRepresentation: disparityinfo as! [AnyHashable : Any]) {
depthdata = truedepthdata
}
}
}
}
}
})
}
return depthdata
}
The image URL supplied by UIImagePickerController does not include any of the metadata associated with depth. To get this information, you must access the PHAsset using the PhotoBook API.
First, import the API:
import Photos
Before you display your image picker, request user access to the photo book. You'll need to add an info dictionary key for Photo Library Usage for this to work:
switch PHPhotoLibrary.authorizationStatus() {
case .notDetermined:
PHPhotoLibrary.requestAuthorization { (status) in
if status == .authorized {
DispatchQueue.main.async {
// Display image picker here
}
}
}
case .authorized: // Display image picker here
case .denied, .restricted: // Display appropriate error here
}
Now, in your image picker delegate you can do this:
if let asset = info[.phAsset] as? PHAsset {
PHImageManager.default().requestImageData(for: asset, options: nil) { (imageData, dataType, orientation, info) in
let url = info?["PHImageFileURLKey"] as? URL
// Pass this URL to your existing code.
}
}
Note that the file may contain depth or disparity information. You can convert between these easily enough, but you may need to check which one you have using CGImageSourceCopyProperties(). Also look out for the new supplementary depth data, kCGImageAuxiliaryDataTypePortraitEffectsMatte, which gives a much higher resolution mask for just the subject person in portrait images and is great for doing greenscreen-style effects.

Swift 3, Firebase completion Handler is executed twice

My Use case -
I have list of items which I fetch from Firebase. Below is loadItems() function that I call from HomeViewController -
viewDidLoad() and updating tableView with the fetched data.
func loadItems() {
Database.database().reference().child("items").observe(.value, with: { snapshot in
var fetchedItems = [Item]()
guard let receivedvalue = snapshot.value as? [String: Any] else {
print("Received null")
return
}
print(receivedvalue)
for (key, value) in receivedvalue {
let item = Item(id: Int(key)!, json: value as! [String : Any])
fetchedItems.append(item!)
}
self.items = fetchedItems
self.tableView.reloadData()
})
}
I am saving an item and coming back from CreateViewController to HomeViewController, I am - Saving the item in Firebase, Appending the item to prefetched array, reloading tableView.
func addItem(item: Item?) {
rootRef = Database.database().reference()
let id = String(describing: item.id!)
let itemRef = self.rootRef.child("items").child(id)
itemRef.setValue(["name": item.name!, "type": item.type!])
items.append(item!)
self.tableView.reloadData()
}
After reloading tableView, its is going in the Firebase GET Call handler which is present in loadItems().
The handler is executed once when I am getting all items during viewDidLoad(). Is there any reason why the Firebase GET call handler is executed the second time even though I am not calling loadItems() in create workflow?
When .observe(.value is used, it adds an observer to that node and any changes to that node (add, change, remove) will fire the code in the closure.
If you want to leave the observer so you can be notified of changes, the the proper flow is to simply write the data to Firebase and let the closure load the data and populate the tableView.
However, the downside to this is that .value loads ALL of the data in the node. You may want to take a look at adding separate observers for .childAdded, .childChanged and .childRemoved. Those will only load the node that was modified.
If you want to only load the data once (to populate a dataSource on startup for example), use observeSingleEvent which fires once and does not leave an observer.
Then store the data in Firebase and manually add it to the array and reload the tableView.
See the documentation Read Data Once section.

Resources