I am observing using the FIRDataEventTypeValue, and according to the documentation a block will get triggered:
"Your block will be triggered for the initial data and again whenever the data changes."
I keep a local cache of the data in an NSMutatbleArray and when the event fires
I search the cache and if an entry is found, the data gets updated with new values.
And if the entry is not found in the cache, I add the data to the cache.
But how do I take care of deletion? I do not want to use a separate observer, or is this the only way.
[_myRef observeEventType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot * _Nonnull snapshot) {
NSDictionary *dict = snapshot.value;
if (dict == (id)[NSNull null]) {
[_cache removeAllObjects];
[self dataEvent];
return;
}
[dict enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
// extract values from obj and store it in a cache
The method observeEventType:withBlock will get called with all the data contained in the snapshot, so you can "delete" the data by rebuilding the entire cache, also no need to update the cache entries.
[_myRef observeEventType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot * _Nonnull snapshot) {
[_cache removeAllObjects];
NSDictionary *dict = snapshot.value;
if (dict == (id)[NSNull null]) {
[self dataEvent];
return;
}
[dict enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
// process the dictionary and its values and store in the cache
Related
I am using the following code to check for new contents in Firebase / Database.
It is basically working. But when the contents is empty the code crashes.
What is the proper way to handle the case where postDict is coming back empty?
Is there some error code that we can be checked.
I have tried to check the value of postDict.count, but with no result.
FIRDatabaseHandle resultHandle =
[dbRef observeEventType:FIRDataEventTypeValue
withBlock:^(FIRDataSnapshot * _Nonnull snapshot) {
NSDictionary *postDict = snapshot.value;
NSManagedObject *artWorkRcd;
for (NSString* artKey in postDict) {
// Some useful code.
.......
}
}];
FIRDataSnapshot has a method called exists that returns YES if there is actually data in the snapshot. You should check this before using snapshot.value.
I am building something like an "instagram" app with posts with pictures. I am using Firebase to store/retrieve data.
My issue comes because the posts are shown kind of in a random way. For example, in cellForRowAtIndexPath, the first post is shown at indexPath[0], the next post is [1], but the next one which should be at [2], it shows at [0].
I will copy paste the most relevant code for the issue:
Here is the class where I retrieve the Scores:
////// Arrays to store the data
nameArray = [[NSMutableArray alloc]init];
scoreArray = [[NSMutableArray alloc]init];
photomealArray = [[NSMutableArray alloc]init];
keysArray = [[NSMutableArray alloc]init]; // Reference to know what post should be scored.
// Reference to the Database
self.storageRef = [[FIRStorage storage] reference];
FIRDatabaseReference *rootRef= [[FIRDatabase database] referenceFromURL:#"https://XXXXXXX.firebaseio.com/"];
// Query all posts
[[rootRef child:#"Posts"] observeSingleEventOfType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot * _Nonnull snapshot) {
if(snapshot.exists){
NSDictionary *dict = snapshot.value;
for (NSString *key in dict) {
// Query all users to show the Name of the person who posts
[[rootRef child:#"Users"] observeEventType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot * _Nonnull Usersnapshot) {
if(Usersnapshot.exists){
NSDictionary *Userdict = Usersnapshot.value;
for (NSString *Userkey in Userdict) {
NSString *Users_UserID = [Userdict[Userkey] valueForKey:#"UserID"];
NSString *UserID = [dict[key] valueForKey:#"UserID"];
// If the userID matches with the person who posts, then add it to the array
if([Users_UserID isEqualToString:UserID]) [nameArray addObject:[NSString stringWithFormat:#"%#",[Userdict[Userkey] valueForKey:#"Name"]]];
}
}
}];
UIImage *mealPhoto = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:[dict[key] valueForKey:#"PostPhoto"]]]];
NSString *Score = [dict[key] valueForKey:#"Score"];
NSString *PostKeys = [dict[key] valueForKey:#"KeyforPost"];
if([Score isEqual:#"null"]) [scoreArray addObject:#"0"]; else [scoreArray addObject:Score];
[photomealArray addObject:mealPhoto];
[keysArray addObject:PostKeys];
}
}
} withCancelBlock:^(NSError * _Nonnull error) {
NSLog(#"%#", error.localizedDescription);
}];
If I quit and relaunch the app, the new post will be stored in whatever index is "empty" starting from [0], which is what it is expected.
This is how the didSelectRowAtIndexPath looks like:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath{
FeedTableViewCell *cell =[tableView cellForRowAtIndexPath:indexPath];
if(cell.selectionStyle != UITableViewCellSelectionStyleNone){
if([score.text intValue] >= -1 && [score.text intValue] <= 1 && ![score.text isEqual: #""]){
NSString *keyforcell = [keysArray objectAtIndex:indexPath.row];
NSLog(#"key a usar :%#", keyforcell);
ref = [[FIRDatabase database] referenceFromURL:#"https://XXXXX.firebaseio.com"];
[[[[self.ref child:#"Posts"] child:keyforcell] child:#"Score"] setValue:[NSNumber numberWithInt:[score.text intValue]]];
[[[[self.ref child:#"Posts"] child:keyforcell] child:#"Rated"] setValue:#"YES"];
[scoreArray insertObject:[NSNumber numberWithInt:[score.text intValue]] atIndex:indexPath.row];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
}
I have tried to play with observeSingleEventOfType and observeEventType because maybe this has to do with the fact that the single one is only called once. Also I've thought that viewDidLoad() is triggered after the cell is selected, but It shouldn't. Also, I think it has to do with the NSMutableArrays, but I am not completely sure
Can anyone help me out ?
UPDATE : I got to know that neither Firebase nor NSDictionary returns/stored sorted data, so my guess is that my issue has something to do with sorting the items. I got to know about orderbykey and orderbyvalue but they don't seem to work.
It is not clear to see what your issue is, but you should not forget there is no arrays in Firebase Database. The only collection type is dictionary, and dictionaries has no key order.
You should consider a sorting system on your objects, instead of directly adding them to arrays.
I have an array of custom object name finalBarListArray with entity BarCodeSKULists (refer to the attachment 1) which contains sales_sub_category_name (string type), count (int type) and an array of another custom object name arrayBarCodeSKUList (BarCodeSKUList) (refer to the attachment 2/3/4).
Please see the below screenshots:
Now I need to create an array and have to check if there will be same sales_sub_category_name value then need to add their arrayBarCodeSKUList in one.
Like in above array finalBarListArray, there are three elements with sales_sub_category_name 'ENVY 17', 'ENVY 15' & 'ENVY 15', so I need to merge second and third element into one for second index of array. It means Output should have only two elements, where first element should have (count = 1 & one arrayBarCodeSKUList) and the second element should have (count = 2 & two arrayBarCodeSKUList).
Use the following method to merge the array of custom objects, which has same sales_sub_category_names.
- (NSArray *)mergeDuplicate {
NSMutableDictionary *mergedDictionary = [[NSMutableDictionary alloc]init];
// Use sales_sub_category_name value as a key for the dictioanry.
[finalBarListArray enumerateObjectsUsingBlock:^(BarCodeSKULists * _Nonnull object, NSUInteger idx, BOOL * _Nonnull stop) {
id existingItem = [mergedDictionary valueForKey:object.sales_sub_category_name];
if (existingItem) {
// If object exist then check that type is NSMutableArray or not
if ([existingItem isKindOfClass:[NSMutableArray class]]) {
// If yes then append with existing array
[existingItem addObject:object];
mergedDictionary[object.sales_sub_category_name] = existingItem;
} else if ([existingItem isKindOfClass:[BarCodeSKULists class]]) {
// Else if the object is `BarCodeSKULists ` class then create array and added previous item and current item into one array
NSMutableArray *itemList = [NSMutableArray arrayWithObjects:existingItem, object, nil];
mergedDictionary[object.sales_sub_category_name] = itemList;
}
} else {
// If it is first time then add it to the dictionary
mergedDictionary[object.sales_sub_category_name] = object;
}
}];
NSLog(#"%#", mergedDictionary.allValues);
return mergedDictionary.allValues;
}
mergedDictionary.allValues will give the expected array of items
Update :
As per the discussion.
- (NSArray *)mergeDuplicate:(NSMutableArray *) list{
NSMutableDictionary *mergedDictionary = [[NSMutableDictionary alloc]init];
// Use sales_sub_category_name value as a key for the dictioanry.
[list enumerateObjectsUsingBlock:^(BarCodeSKUList * _Nonnull object, NSUInteger idx, BOOL * _Nonnull stop) {
BarCodeSKUList *existingItem = [mergedDictionary valueForKey:object.sales_sub_category_name];
if (existingItem) {
[existingItem.arrayBarCodeSKUList addObjectsFromArray:object.arrayBarCodeSKUList];
mergedDictionary[object.sales_sub_category_name] = existingItem;
} else {
// If it is first time then add it to the dictionary
mergedDictionary[object.sales_sub_category_name] = object;
}
}];
return mergedDictionary.allValues;
}
- (NSArray *)mergeObject{
NSMutableDictionary *dic = [NSMutableDictionary new];
[_originArray enumerateObjectsUsingBlock:^(BarCodeSKULists* _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
id item = [dic objectForKey:obj.sales_sub_category_name];
if (item) {
BarCodeSKULists *list = (BarCodeSKULists *)item;
[list.arrayBarCodeSKUList addObject:obj.BarCodeSKUList];
list.count = (int)list.arrayBarCodeSKUList.count;
dic[obj.sales_sub_category_name] = list;
}
else{
dic[obj.sales_sub_category_name] = obj;
}
}];
return dic.allValues;
}
I'm newbie with Firebase. I try to get data in Database, but my code is not working. Here is my code:
- (void) getData{
FIRDatabaseReference *ref = [[FIRDatabase database] reference];
//READ DATA
[[[ref child:#"buysell"] child:#"users"] observeEventType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot * _Nonnull snapshot) {
NSDictionary *dict = snapshot.value;
NSLog(#"%#",dict);
} withCancelBlock:^(NSError * _Nonnull error) {
}];
}
It cant jump in withBlock:^(FIRDataSnapshot * _Nonnull snapshot). What seems to be wrong? Please help me. Thanks.
Move this to viewDidLoad
FIRDatabaseReference *ref = [[FIRDatabase database] reference];
Check whether you configured everything according to the documentation.
Check the key names
If everything is fine then try changing this
Try with observeSingleEventOfType instead of observeEventType
[[[self.ref child:#"buysell"] child:#"users"] observeSingleEventOfType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot * _Nonnull snapshot) {
NSDictionary *dict = snapshot.value;
NSLog(#"%#",dict);
} withCancelBlock:^(NSError * _Nonnull error) {
NSLog(#"%#", error.localizedDescription);
}];
If above 3 not works check your error statement
You can do it easily. If you want to fetch all info using this you can do this. Make sure your db is public
#property (strong, nonatomic) FIRDatabaseReference *ref;
Define Property on interface then
self.ref = [[FIRDatabase database] reference];
[self.ref observeEventType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot * _Nonnull snapshot) {
NSDictionary *usersDict = snapshot.value;
NSLog(#"Information : %#",usersDict);
}];
If you want to fetch specific portion using this you can do this
[[self.ref child:#"results"] observeEventType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot * _Nonnull snapshot) {
NSDictionary *usersDict = snapshot.value;
NSLog(#"Info : %#",usersDict);
}];
My Fetched Json From Firebase
{
"results" : [
{
"name":"test 1",
"URL" : "URL STRING"
},
{
"name":"test 1",
"URL" : "URL STRING"
},
{
"name":"test 1",
"URL" : "URL STRING"
},
{
"name":"test 1",
"URL" : "URL STRING"
}
]
}
I am working on an iOS app, where I will be getting a JSON Object from server, which will be populated on a UITableView.
User can change values on tableview, Hence resulting in a new JSON.
Now I want to send only delta (Difference of Two JSON Objects) back to server.
I know I can traverse both Objects for finding delta. But just wish to know is there any easy solution for this problem.
Ex:
NSDictionary *dict1 = {#"Name" : "John", #"Deptt" : #"IT"};
NSDictionary *dict2 = {#"Name" : "Mary", #"Deptt" : #"IT"};
Delta = {#"Name" : "Mary"}
Considering new value is Mary for key name;
Thanks In Advance
isEqualToDictionary: Returns a Boolean value that indicates whether the contents of the receiving dictionary are equal to the contents of another given dictionary.
if ([NSDictionary1 isEqualToDictionary:NSDictionary2) {
NSLog(#"The two dictionaries are equal.");
}
Two dictionaries have equal contents if they each hold the same number of entries and, for a given key, the corresponding value objects in each dictionary satisfy the isEqual: test.
Here's how to get all the keys with non-matching values. What to do with those keys is app level question, but the most informative structure would include an array of mismatched values from both dictionaries, as well has handle keys from one that are not present in the other:
NSMutableDictionary *result = [#{} mutableCopy];
// notice that this will neglect keys in dict2 which are not in dict1
for (NSString *key in [dict1 allKeys]) {
id value1 = dict1[key];
id value2 = dict2[key];
if (![value1 equals:value2]) {
// since the values might be mismatched because value2 is nil
value2 = (value2)? value2 : [NSNull null];
result[key] = #[value1, value2];
}
}
// for keys in dict2 that we didn't check because they're not in dict1
NSMutableSet *set1 = [NSMutableSet setWithArray:[dict1 allKeys]];
NSMutableSet *set2 = [NSMutableSet setWithArray:[dict2 allKeys]];
[set2 minusSet:set1]
for (NSString *key in set2) {
result[key] = #[[NSNull null], dict2[key]];
}
There are certainly more economical ways to do it, but this code is optimized for instruction.
Just enumerate through and compare the dictionaries key-by-key. This will output any differences as well as any unmatched keys on either side, you can tweak the logic depending on exactly what you want to include.
- (NSDictionary *)delta:(NSDictionary *)dictionary
{
NSMutableDictionary *result = NSMutableDictionary.dictionary;
// Find objects in self that don't exist or are different in the other dictionary
[self enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
id otherObj = dictionary[key];
if (![obj isEqual:otherObj]) {
result[key] = obj;
}
}];
// Find objects in the other dictionary that don't exist in self
[dictionary enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
id selfObj = self[key];
if (!selfObj) {
result[key] = obj;
}
}];
return result;
}