Retrieving data in firebase - ios

I am a beginner in firebase and in iOS. I am trying to change a project from parse to firebase.I successfully created and updated the firebase database. But while trying to retrieve data from firebase its just not working for me.
Here's the code I used.
self.ref = [[FIRDatabase database] reference];
NSString *userID = [FIRAuth auth].currentUser.uid;
[[[ref child:#"users"] child:userID] observeSingleEventOfType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot * _Nonnull snapshot) {
NSLog(#"%#",snapshot.value);
// Get user value
}withCancelBlock:^(NSError * _Nonnull error) {
NSLog(#"%#", error.localizedDescription);
}];
The control just skips the whole block. I can't figure out the problem.
The firebase guide is very hard to understand. Please tell me how to get values to a dictionary from a firebase child.

DatabaseReference database =
FirebaseDatabase.getInstance().getReference();
database.child("notes").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
List notes = new ArrayList<>();
for (DataSnapshot noteDataSnapshot : dataSnapshot.getChildren()) {
Note note = noteDataSnapshot.getValue(Note.class);
notes.add(note);
}
adapter.updateList(notes);
}
...
});

Related

Firestore iOS queryWhereField and queryOrderedByField is not returning specific data

In my iOS project, I'm using Firestore. I'm in need to use queryWhereField and queryOrderedByField, I've implemented as follows but it's not working:
defFirestore = [FIRFirestore firestore];
colRef = [defFirestore collectionWithPath:#"walks"];
[[[colRef queryWhereField:#"requestId" isEqualTo:#(self.requestId)] queryWhereField:#"taskId" isEqualTo:#(self.taskId)] queryOrderedByField:#"createdDate"];
[colRef getDocumentsWithCompletion:^(FIRQuerySnapshot * _Nullable snapshot, NSError * _Nullable error) {
if (error != nil) {
NSLog(#"Error getting documents: %#", error);
} else {
for (FIRDocumentSnapshot *document in snapshot.documents) {
NSLog(#"%# => %#", document.documentID, document.data);
}
}
}];
Above query is not returning where specific record rather returning all records also not ordered by createdDate field. Firestore database screenshot as follows:
Okay, I've already figured that out. First of all need to create an index in Firestore for those fields we want to query from. then need to define a FIRQuery to run the query with those fields. So the query will look like as follows:
FIRQuery *walks = [[[colRef queryWhereField:#"requestId" isEqualTo:#(self.requestId)] queryWhereField:#"taskId" isEqualTo:#(self.taskId)] queryOrderedByField:#"createdDate"];
[walks addSnapshotListener:^(FIRQuerySnapshot *snapshot, NSError *error) {
if (snapshot == nil) {
NSLog(#"Error fetching documents: %#", error);
return;
}
if (error != nil) {
NSLog(#"Error getting documents: %#", error);
} else {
for (FIRDocumentChange *diff in snapshot.documentChanges) {
if (diff.type == FIRDocumentChangeTypeAdded) {
NSLog(#"%# => %#", diff.document.documentID, diff.document.data);
}
}
}
}];
I used snapshot because I want to get realtime update.

firebase - get all chat rooms for a member

I'm integrating multi-room chat in my app and I'm stuck with getting a list of all chat room a specific user is member of.
The structure looks like this:
The rooms and members are created with childByAutoId like this:
FIRUser *user = [FIRAuth auth].currentUser;
mdata[#"uid"] = user.uid;
roomKey = [[_rootRef child:#"messages"] childByAutoId].key;
NSString *memberID = [[[_rootRef child:#"members"] child: roomKey ] child: user.uid ].key;
// set Meta data for member to be able to recognize later
[[[[_rootRef child:#"members"] child: roomKey ] child: memberID ] setValue:mdata];
Now I'm trying the get the list of rooms for a (hard coded) user like that:
-(void) getRoomKeys {
[[[[_rootRef child: #"members"] queryOrderedByChild:#"uid" ] queryEqualToValue:#"QLfRoGpoCjWpzira7fljBj8g3EJ3"]
observeEventType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) {
NSDictionary<NSString *, NSString *> *message = snapshot.value;
}];
}
This return 0 key/value pairs.
When I take out the query I get all room keys. When I change the structure to this
I will get all rooms for that user but with this structure I can't add other members to that room. So I need to dig one level deeper.
So how to set the query for nodes that have been created by childByAutoId?
Thanks for your help!
Try considering this data structure.
{
"rooms": {
"-KQgDxLIt88yPt6nacDu": { ... }
},
"members": {
"QLfRoGpoCjWpzira7fljBj8g3EJ3": { ... }
},
"member-rooms": {
"QLfRoGpoCjWpzira7fljBj8g3EJ3": {
"-KQgDxLIt88yPt6nacDu": true
}
}
}
You can retrieve the keys of the rooms a member is part of by observing member-rooms/{uid}:
[[_rootRef child: #"member-rooms/QLfRoGpoCjWpzira7fljBj8g3EJ3"] observeEventType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) {
NSLog("%#", snapshot.value); // the keys of the rooms of the member
}];

Parse LiveQuery not working

I have a Parse server with a LiveQuery.
I can connect to the live query by the log info: Create new client: 1, and websocket.org confirms the connection, however none of the completion blocks are being called.
Here is the full code:
self.pfclient = [[PFLiveQueryClient alloc] init];
PFQuery* query = [PFQuery queryWithClassName:#"Reqs"];
[query whereKey:#"objectId" notEqualTo:#"asdfas"];
self.subscription = [self.pfclient subscribeToQuery:query];
[self.subscription addSubscribeHandler:^(PFQuery * _Nonnull query) {
NSLog(#"Subscribed");
}];
[self.subscription addUpdateHandler:^(PFQuery * _Nonnull query, PFObject * _Nonnull obj) {
NSLog(#"Update");
}];
[self.subscription addErrorHandler:^(PFQuery * _Nonnull query, NSError * _Nonnull error) {
NSLog(#"Error");
}];
Swift 3.0 Code that is working:
let liveQueryClient = ParseLiveQuery.Client(server: "...", applicationId: ..., clientKey: ..)
...
var subscription: Subscription<PFObject>?
let query: PFQuery<PFObject> = PFQuery(className: "className").whereKey("objectId", equalTo: "168sdf8438")
subscription = liveQueryClient.subscribe(query).handle(Event.created) { _, message in
print("Object created")
}

Firebase iOS SDK - Count connected users

I'm using the Firebase iOS SDK to build a chat system that will let my users to connect to some random "rooms" where they can chat together. Inside the room I want to display to them the total number of people connected at the moment. The problem is that I don't know how to do that count. The number of users connected should be updated on connects and disconnects of particular users. I don't know where to start and what to do.
This is simple :)
Whenever a user authenticates/joins a room save them to the list of active users.
Swift
let ref = Firebase(url: "<your-firebase-db>")
ref.observeAuthEventWithBlock { authData in
if authData != nil {
// 1 - Get the ref
let activeUsersRef = Firebase(url: '<your-firebase-db>/activeUsers')
// 2 - Create a unique ref
let singleUserRef = activeUsersRef.childByAutoId()
// 3 - Add them to the list of online users
singleUserRef.setValue(authData.providerData["email"])
// 4 - When they drop their connection, remove them
singleUserRef.onDisconnectRemoveValue()
}
}
Objective-C
Firebase *ref = [[Firebase alloc] initWithUrl: #"<your-firebase-db>"];
[ref observeAuthEventWithBlock: ^(FAuthData *authData) {
Firebase *activeUsersRef = [[Firebase alloc] initWithUrl: #"<your-firebase-db>/activeUsers"];
Firebase *singleUserRef = [activeUsersRef childByAutoId];
[singleUserRef setValue: #"Whatever-the-key-is"];
[singleUserRef onDisconnectRemoveValue];
}];
The snippet above will maintain a list of active users.
All you need to do now is display the count.
Swift
// Listen to the same ref as above
let activeUsersRef = Firebase(url: 'firebase-db.firebaseio.com/activeUsers')
activeUsersRef.observeEventType(.Value, withBlock: { (snapshot: FDataSnapshot!) in
var count = 0
// if the snapshot exists, get the children
if snapshot.exists() {
count = snapshot.childrenCount
}
})
Objective-C
Firebase *activeUsersRef = [[Firebase alloc] initWithUrl: #"<your-firebase-db>/activeUsers"];
[activeUsersRef observeEventType:FEventTypeValue withBlock:^(FDataSnapshot *snapshot) {
NSUInteger count = 0;
if ([snapshot exists]) {
count = snapshot.childrenCount;
}
}];

Prevent a closure from running until another has completed

Here is code for two closures in two different IBAction button presses. The desired outcome is for the button press to turn on/off an LED, then to access a light sensor and read the light value after the change in LED status.
What happens is a race condition where the function getVariable runs and returns before the callFunction has implemented the change. The result is that the value displayed in getLightLabel.text is that of the prior condition, not the current condition.
My question is how to rewrite the code below so that myPhoton!.getVariable does not execute until after the myPhoton!.callFunction has returned (completed its task).
I have tried placing getVariable inside callFunction, both before and after the } closing if (error == nil), but the result was identical to the code shown here.
#IBAction func lightOn(sender: AnyObject) {
let funcArgs = [1]
myPhoton!.callFunction("lightLed0", withArguments: funcArgs) { (resultCode : NSNumber!, error : NSError!) -> Void in
if (error == nil) {
self.lightStateLabel.text = "LED is on"
}
}
myPhoton!.getVariable("Light", completion: { (result:AnyObject!, error:NSError!) -> Void in
if let e = error {
self.getLightLabel.text = "Failed reading light"
}
else {
if let res = result as? Float {
self.getLightLabel.text = "Light level is \(res) lumens"
}
}
})
}
#IBAction func lightOff(sender: AnyObject) {
let funcArgs = [0]
myPhoton!.callFunction("lightLed0", withArguments: funcArgs) { (resultCode : NSNumber!, error : NSError!) -> Void in
if (error == nil) {
self.lightStateLabel.text = "LED is off"
}
}
myPhoton!.getVariable("Light", completion: { (result:AnyObject!, error:NSError!) -> Void in
if let e = error {
self.getLightLabel.text = "Failed reading light"
}
else {
if let res = result as? Float {
self.getLightLabel.text = "Light level is \(res) lumens"
}
}
})
}
Here is the callFunction comments and code from the .h file. This SDK is written in Objective C. I am using it in Swift with a bridging header file.
/**
* Call a function on the device
*
* #param functionName Function name
* #param args Array of arguments to pass to the function on the device. Arguments will be converted to string maximum length 63 chars.
* #param completion Completion block will be called when function was invoked on device. First argument of block is the integer return value of the function, second is NSError object in case of an error invoking the function
*/
-(void)callFunction:(NSString *)functionName withArguments:(NSArray *)args completion:(void (^)(NSNumber *, NSError *))completion;
/*
-(void)addEventHandler:(NSString *)eventName handler:(void(^)(void))handler;
-(void)removeEventHandler:(NSString *)eventName;
*/
Here is the .m file code
-(void)callFunction:(NSString *)functionName withArguments:(NSArray *)args completion:(void (^)(NSNumber *, NSError *))completion
{
// TODO: check function name exists in list
NSURL *url = [self.baseURL URLByAppendingPathComponent:[NSString stringWithFormat:#"v1/devices/%#/%#", self.id, functionName]];
NSMutableDictionary *params = [NSMutableDictionary new]; //[self defaultParams];
// TODO: check response of calling a non existant function
if (args) {
NSMutableArray *argsStr = [[NSMutableArray alloc] initWithCapacity:args.count];
for (id arg in args)
{
[argsStr addObject:[arg description]];
}
NSString *argsValue = [argsStr componentsJoinedByString:#","];
if (argsValue.length > MAX_SPARK_FUNCTION_ARG_LENGTH)
{
// TODO: arrange user error/codes in a list
NSError *err = [self makeErrorWithDescription:[NSString stringWithFormat:#"Maximum argument length cannot exceed %d",MAX_SPARK_FUNCTION_ARG_LENGTH] code:1000];
if (completion)
completion(nil,err);
return;
}
params[#"args"] = argsValue;
}
[self setAuthHeaderWithAccessToken];
[self.manager POST:[url description] parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) {
if (completion)
{
NSDictionary *responseDict = responseObject;
if ([responseDict[#"connected"] boolValue]==NO)
{
NSError *err = [self makeErrorWithDescription:#"Device is not connected" code:1001];
completion(nil,err);
}
else
{
// check
NSNumber *result = responseDict[#"return_value"];
completion(result,nil);
}
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
if (completion)
completion(nil,error);
}];
}
One solution is to put the second closure inside the first, where the first returns and provides and Error value. If no error,then execuet the second closure. That is one way to tightly couple the two closures without resorting to semaphores or other messaging schemes.
In this application, the problem I was encountering cannot be solved on the IOS/Swift side of the stack. The cloud API and embedded uP are not tightly coupled, so the cloud returns to the IOS with a completion before the full function code has run on the Particle uP.
The solution to this overall problem actually lies in either modifying the cloud API or adding some additional code to the uP firmware to tightly couple the process to the IOS app with additional communication.

Resources