String vs. Number for Firebase Keys?

1,433 views
Skip to first unread message

Zack Morris

unread,
Apr 10, 2014, 3:15:59 PM4/10/14
to fireba...@googlegroups.com
Hi I’ve run into a problem where NSDictionary stores separate entries under an NSString like @“42” and an NSNumber like @42. I’m thinking of refactoring my Firebase rules so that user id keys must be numbers with:

".write": "newData.isNumber()”

I’m concerned though that some auth ids in the future might be strings, because Firebase Simple Login’s FAUser’s userId is an NSString. I can easily convert the userId NSString to an NSNumber with @([user.userId intValue]) and that should work for Facebook but some of the other logins like Anonymous look like they could be alphanumeric.

The gist of the problem is that I might have relationships like:

users/42/John
something/“42”/true

And Firebase seems to go along with this just fine (probably because javascript’s 42 ==“42” is true) but it blows up in iOS. I’m having a hard time visualizing how to enforce the same type but still allow for future ids as strings. I could go as far as writing a wrapper over NSDictionary that overrides key insertion to make them all NSString or possibly override the isEqual operator to be more lax like javascript, but would like to avoid doing that.

Any ideas would be greatly appreciated.

Thanks,

Zack Morris

Zack Morris

unread,
Apr 10, 2014, 5:06:01 PM4/10/14
to fireba...@googlegroups.com
To answer my own question, I noticed that Firebase keys are always strings, so for example if I set a key to "123", the url for that node becomes:

https://xxxxx.firebaseio.com/%22123%22

So it simplified my objective-c code quite a bit to follow the key-as-string convention with my NSDictionaries. I also decided to store numeric values representing user ids as strings in Firebase because it simplifies using those values later for lookups like:

[[myFirebase childByAppendingPath:snapshot.value] observeSingleEventOfType:FEventTypeValue withBlock:]

The only tricky part is that if I make a call to Facebook like:

[[FBRequest requestForMe] startWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
NSLog(@"%@", [result[@"id"] class]);
}];

The result is:

__NSCFString

But if I make a call to Facebook with FQL like:

[[FBRequest requestWithGraphPath:@"fql" parameters:@{@"q": @"SELECT uid, name FROM user WHERE uid = me()"} HTTPMethod:nil] startWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
NSLog(@"%@", [result[@"data"][0][@"uid"] class]);
}];

The result is:

__NSCFNumber

Notice that Facebook returns some ids as strings and others as numbers! Also there is no “as” clause in FQL so we can’t return uid as id, which is annoying, but I digress.

So it’s important to convert these types to NSString before storing them like:

[myFirebase setValue:@{@"test" : result[@"data"][0][@"uid"]}];

Otherwise when you retrieve them and try to use them with childByAppendingPath:snapshot.value, it barfs. So just use:

[myFirebase setValue:@{@"test" : [result[@"data"][0][@"uid"] stringValue]}];

You can write a category on NSString to avoid annoying “unrecognized selector sent to instance” exceptions:

@implementation NSString(stringValue)

- (NSString *)stringValue
{
return self;
}

@end

I’m not super comfortable with this because it would be more efficient to use numbers directly, but, it allows for future expandability with alphanumeric ids, and also since most ids are on the order of 8 digits anyway, they take up about the same amount of room as binary or text in the Firebase stream regardless. For other things like timestamps, scores, etc, I’ve decided to store numbers directly since they generally aren’t used later for lookups.

Just thought this might help someone save some time. I’m still curious about alternatives.

Zack Morris
Reply all
Reply to author
Forward
0 new messages