Updating Firebase deletes already existing data - ios

I'm building an app with Firebase, and I recently used this code to update a list/dictionary of "posts" inside a user dictionary. Take a look:
Firebase *baseRef = [[Firebase alloc] initWithUrl:#"https://<MY-APP>.firebaseio.com"];
NSString *userPostsPath = [NSString stringWithFormat:#"users/%#/posts", userId];
NSDictionary *postRef = #{ postId : postDescription };
[baseRef updateChildValues:#{
userPostsPath : postRef
// here I'd have a similar update
}withCompletionBlock:^(NSError *error, Firebase *ref) {
handler(error, ref);
}];
This works on the first update, but the second time I do this, all the existing posts are removed. However, changing the code to this:
Firebase *baseRef = [[Firebase alloc] initWithUrl:#"https://tonightdb.firebaseio.com"];
NSString *usersPostsPath = [NSString stringWithFormat:#"users/%#/posts/%#/", userId, postId];
[baseRef updateChildValues:#{
usersPostsPath : postDescription,
// similar update path
}withCompletionBlock:^(NSError *error, Firebase *ref) {
handler(error, ref);
}];
Updates Firebase correctly.
What is the difference between the two? How come the first didn't work?
Edit
Is it possible to do:
Firebase *baseRef = [[Firebase alloc] initWithUrl:#"https://<MY-APP>.firebaseio.com"];
NSString *newKey = [baseRef childByAutoId];
Then use that key do do an update that looks like this:
[baseRef updateChildValues:#{
[NSString stringWithFormat:#"/posts/%#/", newKey] : // something
[NSString stringWithFormat:#"/posts/%#/members/<something>", newKey] : // something
withCompletionBlock:^(NSError *error, Firebase *ref) {
handler(error, ref, task);
}];
Basically to issue multiple updates to the same path in the same request, that doesn't exist beforehand while avoiding overwrite issues

Your first example translate into an instruction to update:
"users/posts" = { "postid1": postDescription }
The second example translates to:
"users/posts/postid1" = postDescription
Being a computer program, the Firebase server takes a quite literal interpretation of the instructions you give it. It takes each update instruction and replaces the data at the path (the part before the =) with the value (the part after the =).
Knowing that, you can see that in the second example it will write the postDescription at users/posts/postid1. This will replace the existing description for that post, but that is probably what you had in mind.
The second example writes { "postid1": postDescription } at users/posts. This replaces the existing value at that location, so you're essentially replacing all existing posts with the new/updated one. That is probably not what you had in mind.
Update
If you're creating a new object and fanning out the key to multiple locations, you can make use of the fact that childByAutoId is a pure client-side operation:
let newRef = ref.childByAutoId()
let newKey = newRef.key
ref.childByAppendingPath("child1").childByAppendingPath(newKey).setValue("one")
ref.childByAppendingPath("child2").childByAppendingPath(newKey).setValue("two")

Related

How to check contact source in CNContact swift?

In Contact apps there's group like "iCloud", "yahoo", "gmail". In swift, is it possible to fetch contact from gmail source only?
Tested code. Hope it will solve your problem...
func getAppropriateName(for container: CNContainer?) -> String? {
var name = ""
if (container?.name == "Card") || container?.name == nil {
name = "iCloud"
}
else if (container?.name == "Address Book") {
name = "Google"
}
else if (container?.name == "Contacts") {
name = "Yahoo"
}
else {
name = "Facebook"
}
return name
}
iCloud/yahoo/gmail etc are CNContainer. Gmail/iCloud is of type CNContainerTypeCardDAV. So first you need to fetch all contacts, and then filter the array based on the CNContainerType of that contact. But unfortunately, we cannot identify which CardDav is it, i.e iCloud/Gmail.
Please see more details here: How do we know which CNContainer represents iCloud?
You can achieve this by looking at Contacts framework runtime headers here: https://github.com/JaviSoto/iOS10-Runtime-Headers/tree/master/Frameworks/Contacts.framework
You can call them by performSelector message. It's a bit messy, but works.
Generally what you have to do is following:
CNContactStore* store = [CNContactStore new];
// fetch accounts that sync contacts with your device (array of CNAccount)
// since CNAccount class isn't available by default, we treat it as NSObject for our puproses
NSArray* accounts = [store performSelector:#selector(accountsMatchingPredicate:error:) withObject:nil withObject:nil];
// you can iterate through this array, I just use first one for this example
NSObject* account = [accounts firstObject];
// get identifier of the account for NSPredicate we use next
NSString* accountId = [account performSelector:#selector(identifier)];
// Display name of the account (aka Yahoo, Gmail etc.)
NSString* accountName = [account performSelector:#selector(_cnui_displayName)];
// NSPredicate that help us to get corresponding CNContainer
NSPredicate* containerPredicate = [[CNContainer class] performSelector:#selector(predicateForContainersInAccountWithIdentifier:) withObject:accountId];
// Fetching CNContainer
CNContainer* container = [[store containersMatchingPredicate:containerPredicate error:nil] firstObject];
After that it's all about general usage of CNContainers.
Hope it will help.
PS. It works on iOS 10, for future versions you should check for Contacts.framework runtime changes.
PPS. I didn't check on swift, but should work either.
Sorry for my english.
Good luck :)

How to retrieve an specific child in Firebase?

The childByAutoId would be useful if you want to save in a node multiple children of the same type, that way each children will have its own unique identifier.
pet:{
KJHBJJHB:{
name:fluffy,
owner:John Smith,
},
KhBHJBJjJ:{
name:fluffy,
owner:Jane Foster,
}
}
Therefore, once I have that uID, and I want to retrieve an specific user using the his/her uID. How do I tell Firebase that I want that specific user? Because I create it and store it in Firebase, but then to read it, don't I need to know the value of the uID? Where do I get it from?
What function do I use to retrieve for example the second user using the uID?
Thanks in advance..
in the title u ask how to get rid so:: get the new ref's key property to get the aid created
FIRDatabaseReference *ref = parent.childByAutoID
NSString *uid = ref.key
BUT thats not what you want id say, so:
to filter out all children where owner = XY which you want I think:
FIRDatabaseReference *ref = your pets node
FIRDatabaseQuery *allPets = [ref queryOrderedByChild:#"owner"];
FIRDatabaseQuery *specificPet = [allPets queryEqualToValue:#"XY"];
[specificPet observeEventType:FEventTypeChildAdded withBlock:^(FIRDataSnapshot *snapshot) {
NSDictionary *dict = snapshot.value;
NSString *key = snapshot.key;
NSLog(#"key = %# for child %#", key, dict);
}];
see: Query users by name or email address using Firebase (Swift)

Core Data returns nil for relationship with valid object

I have a weird error. I have a relationship between a call object and status object. the relationship name from the call object is statusForCall.
the data is imported into core data programmatically from JSON. it will not create a call without a status. the user has no way of changing the statusForCall to nil.
however after a period of time (seems to be days) when accessing calls via a fetch request into an array, when I access aCall.StatusForcall.statusID it starts returning nil. There is no way the code can have updated the statusForCall to nil.
Any ideas what might be causing this and where to start looking?
the cachename is nil throughout the application.
interestingly, if the user redownload the application the problem is solved. The code doesn't change and the data doesn't change (there is no migration - it's the same version) - but for some reason this ALWAYS fixes the issue.
i'm really struggling to know what to look at to get to the bottom of this.
code for setting the status below (edited down for conciseness). As i say the job initially has a status but seems to lose the relationship after a period od of time (i can't tell how long this is as the user is unreliable on this)
Call *theCall;
//see if we have the call on the device - if not create a new one, if we do update that one
BOOL contentIsInCoreData = NO;
for (Call *thisCall in existingCalls)
{
if ([thisCall.callID isEqualToNumber:[checkDic objectForKey:#"callID"]])
{
theCall=thisCall;
contentIsInCoreData = YES;
}
}
NSError * error = nil;
NSDateFormatter *dateFormat =[[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"dd-MMM-yyyy"];
if (contentIsInCoreData==NO)
{
[self.postDelegate updateGetJobsAndTimesProgress:[NSString stringWithFormat:#"Adding new job: %#",[checkDic objectForKey:#"woRef"]]];
//new call add bits that will not change
theCall = (Call*)[NSEntityDescription insertNewObjectForEntityForName:#"Call" inManagedObjectContext:self.managedObjectContext];
}
//set various properties from the JSON dictrionary – these have been cut down for the example code
theCall.woRef=[checkDic objectForKey:#"woRef"];
theCall.shortAddress=[checkDic objectForKey:#"shortAddress"];
theCall.address=[[checkDic objectForKey:#"address"] stringByReplacingOccurrencesOfString:#"\\n" withString:#"\n"];
theCall.postCode=[checkDic objectForKey:#"postCode"];
theCall.callID=[checkDic objectForKey:#"callID"];
//****THIS IS WHERE THE STATUS IS SET – PLEASE NOTE THE STATUS WILL EXIST IN CD ALREADY and the JSON WILL have a valid statusID
NSFetchRequest *request2=[NSFetchRequest fetchRequestWithEntityName:#"CallStatus"];
request2.predicate=[NSPredicate predicateWithFormat:#"callStatusID ==%#",[checkDic objectForKey:#"callStatusID"]];
error = nil;
NSArray * existingStatus = [self.managedObjectContext executeFetchRequest:request2 error:&error];
CallStatus *selectedStatus;
selectedStatus=[existingStatus firstObject];
theCall.statusForCall = selectedStatus;
error = nil;
if ([self.managedObjectContext save:&error])
{
//NSLog(#"saved job");
}
else
{
NSLog (#"***BROKEN GET JSON!!!\r\r%#",error);
}
turns out that a relationship property had been accidently changed - it was cascading a delete from the many end - which resulted in the one end being deleted.

Retrieve SAVED objectID from Parse

Im trying to retrieve the objectId of my parse records. I can save and retrieve data ok, but having gone through all parse documents, Google, and SO, I can't seem to get a clear answer on how to get the id for accessing records before the main block without hard coding the objectId as in the tutorial from Parse.
getObjectInBackgroundWithId:#"SS8Cw7rT1h" <-------- Trying to retrieve object id to go here
Im saving data to parse inc objectId here (all good)
PFObject *scheme = [PFObject objectWithClassName:#"SchemeProvider"];
//Sections of class
scheme[#"schemeName"] = self.schemeName.text;
scheme[#"schemeRegistrationNumber"] = self.registrationNumber.text;
//Save then get object id of saved row
[scheme saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
[scheme objectId];
NSLog(#"SCHEME ROW ID %#",[scheme objectId]);
}];
Retrieving from parse (all good) - except - can't get objectID before main block
PFQuery *queryScheme = [PFQuery queryWithClassName:#"SchemeProvider"];
//NSString *myobjectId = [queryScheme valueForKey:#"objectId"]; <------trying to retrive object id
[queryScheme getObjectInBackgroundWithId:#"SS8Cw7rT1h" block:^(PFObject *retrivedData, NSError *error) {
NSLog(#"All data = %#", retrivedData);
}];
I have just hit this problem and have an example for you in swift
There is a function that saves with a block in background
You can access the objectId while saving in the block thats run after saving it to the cloud
var foo = PFObject(className:"Foo")
foo["bar"] = "something"
foo.saveInBackgroundWithBlock(){(succeeded: Bool!, error:NSError!) in
if succeeded
{
self.fooLabel.text = foo.objectId
}
}
You could store the objectId for a certain object e.g in a data structure or with core data if you need it for later access
Otherwise you need to query the object with property values i guess
like seen here: https://www.parse.com/docs/ios_guide#queries-basic/iOS
Hope this helps

setObject:forKey: of NSMutableDictionary overwrites all data in dictionary

for ( int cnt = 0 ; cnt < nPeople ; cnt++ )
{
ABRecordRef ref = CFArrayGetValueAtIndex(allPeople, cnt);
NSString *firstName = (NSString *)ABRecordCopyValue(ref, kABPersonFirstNameProperty);
NSString *lastName = (NSString *)ABRecordCopyValue(ref, kABPersonLastNameProperty);
NSString *fullName;
/* skipped code at here : code to merge firstName and lastName to fullName. In my country, many of us don't separate first name and last name */
// tempKeyString : NSString variable that has key of fullNameArray value for nameDictionary.
// fullNameArray : to keep some fullName variables for tempKeyString.
if (!tempKeyString) // there's no tempKeyString, a.k.a. it's the first fullName.
{
// it's not important to know about GetUTF8String:fullName. It's for my own language.
tempKeyString = [self GetUTF8String:fullName];
[fullNameArray addObject:fullName];
}
else
{
if ([tempKeyString characterAtIndex:0] == [[self GetUTF8String:fullName] characterAtIndex:0]) // if fullName has the same tempKey with fullNameArray.
{
[fullNameArray addObject:fullName];
}
else // if fullName has different tempKey with fullNameArray.
{
//tempKey : key data for fullNameArray
NSString *tempKey = [tempKeyString substringToIndex:1];
// tempDict : to keep the deep copy of nameDictionary before adding new key.
NSDictionary *tempDict = [nameDictionary mutableDeepCopy];
// add new key (tempKey) with new value (fullNameArray)
// PROBLEM : ALL values (including previous values) in dictionary(nameDictionary) are overwritten to a new value(fullNameArray).
[nameDictionary setObject:fullNameArray forKey:tempKey];
//empties fullNameArray so that it can get the new fullName of the new tempKey.
[fullNameArray removeAllObjects];
//refresh tempKeyString, and add the new fullName.
tempKeyString = [self GetUTF8String:fullName];
[fullNameArray addObject:fullName];
...
}
}
}
I'm trying to make a NSMutableDictionary object from the contacts of my iPhone. Why I make a NSMutableDictionary typed object is that I need indexes for contacts, and it doesn't look easy to make indexes from ABAddressRef typed object directly. I also need to make searching function..
There was no problem when I just coded, but after debugging the only problem makes me crazy. After I apply the array named fullNameArray with the key named tempKey to the namedDictionary, I can find the nameDictionary has all values with them of fullNameArray. All previous data were overwritten! I tried to make a deep copied version of previous nameDictionary before applying fullNameArray and copy it to the newer nameDictionary. However, when I checked the breakpoint at the third line, I can't find the previous data at the tempDict.
I added more codes and comments. It might help more than my explanation.. any questions are pleased!
I tried to find the reason from here - StackOverflow -, and other webpages all the night, but I couldn't find any similar problems.. please help me! Thank you so much in advance!!
The reason why it get emptied is because
[nameDictionary setObject:fullNameArray forKey:tempKey];
here, you set up your dictionary with the object "fullNameArray", then
[fullNameArray removeAllObjects];
remove all the values inside this array, effectively, removing your object in the "nameDictionary", they are the same object, it's not a deep copy of fullNameArray that you store inside your dictionary. Why did you need to store anything into your array anyway? You're only storing 1 value.
[nameDictionary setObject:fullName forKey:tempKey];
will do what you need. Sorry if I mistaken your question, it's quite hard to understand

Resources