jakehawken jakehawken - 1 month ago 5
iOS Question

Firebase: Is there a way to see the JSON payload being sent from the SDK?

I've been developing an iOS app using Firebase for a few months, and I've run into an issue where a call to my backend is failing. To test the issue, I open up the simulator on Firebase and simulate the call, and it succeeds with no issues. All other calls work as expected.

The only conclusion I can come to is that the JSON payload differs, in some way, from what I expect it to be.

Some details:
The view controller I'm in is doing continuous observing of a data structure on the backend (using observeEventOfType:withBlock:withCancelBlock:), and checking boxes on that screen manipulates child values of that same data structure. So each time a box is checked or unchecked, it does a setValue: call with the new value. Then, since the parent data structure is being observed, it gets updated, and the screen is refreshed.

The problem I'm having is that the setValue: call get rejected, which in turn hits the cancelBlock of the other method. When I try to simulate that setValue: on my Firebase dashboard, it succeeds.

I've added the relevant backend rules and the failing method in question in a github gist here:
https://gist.github.com/jakehawken/4a4bb8d2f58c651d7310b3a1737bf11e

//RELEVANT BACKEND RULES FOR THE FAILING CALL (I'm writing to the "completed" path):
"subtasks": {
"$list_item": {
".validate": "newData.hasChildren(['subtaskDescription', 'completed'])",
"subtaskDescription" : {
".validate" : "newData.isString()"
},
"completed" : {
".validate" : "newData.isNumber() && !newData.hasChildren()"
}
}
}


METHOD CALL THAT IS FAILING (Objective-C):

- (KSPromise *)markSubtask:(HDInProgressSubtask *)subtask completed:(BOOL)completed forListID:(NSString *)listID inProgressItemKey:(NSString *)inProgressKey subtaskKey:(NSString *)subtaskKey
{
KSDeferred *deferred = [KSDeferred defer];

FIRDatabaseReference *specificSubtaskReference = [self specificSubtaskCompletionReferenceForListID:listID inProgressItemKey:inProgressKey subtaskKey:subtaskKey];

FIRDatabaseReference *subtaskCompletionReference = [specificSubtaskReference child:kCompleted];

NSNumber *value = [NSNumber numberWithBool:completed];

[subtaskCompletionReference setValue:value withCompletionBlock:^(NSError * _Nullable error, FIRDatabaseReference * _Nonnull ref) {
if (error)
{
[deferred rejectWithError:error]; //Failing case
}
else
{
[deferred resolveWithValue:@(completed)]; //Success case
}
}];

return deferred.promise;
}


The call should simply be sending up the number 1 or a 0 (a BOOL wrapped in an NSNumber), but when I turn on verbose logging, it says it's sending up this behemoth:

{
"d" : {
"a" : "p",
"b" : {
"d" : 1,
"p" : "<the url path for this upload>"
},
"r" : 11
},
"t" : "d"
}


The apparent success from the simulator:
sensitive information redacted

Answer

I finally figured it out myself.

I commented out all of my validation rules so that I could post anything I wanted to the backend. I had figured, up to this point, that since I was wrapping my boolean in an NSNumber, I figured the backend would store that value as a number. Accordingly, my rule for that path was newValue.isNumber(). It turns out, however, that Firebase automagically knows that it’s a boolean, so when I stripped out all my rules, the value that got posted to the backend was “true.” So, I rewrote the rule to be newValue.isBoolean() now it works just fine.

Moral of the story: If you initialize an NSNumber with a boolean, then when you hand that off to Firebase, the JSON payload that gets created will be made with the awareness that the NSNumber was a boolean to begin with.