NSLog(#"pressed");
[_order setObject:_placeChosen forKey:#"place"];
_order[#"place"] = _placeChosen;
//[_order saveEventually];
[_order saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
NSLog(#"saving...");
if(succeeded){
NSLog(#"succeeded dude");
[self performSegueWithIdentifier:#"pickDate" sender:self]; }
else{
NSLog(#"error");
NSLog([error debugDescription]);
}
}];
That's my code. As you can see, I've logged all the possible places and here is the crazy thing: There is no error! The "error" won't show up nor will the debugDescription. "saving..." is not showing up either. however, "pressed" DOES show up. I thought it was faulty network connection but i waited for a while, tried a bunch of times, went to different places and it STILL doesn't work. Is this a bug? Or is there another way to do this?
It turns out that in my prepareForSegue in the previous controller, I had spelled the name of the segue identifier wrong so that the _order wasn't being transferred over thus it was nil. Fixing the spelling fixed the issue. Although, Parse should really give an error saying that the PFObject is nil so it can't be saved.
Related
I'm desperate. I'm trying to use iAd's requestAttributionDetailsWithBlock, and it's not calling anything inside my completion handler (I'm using breakpoints in XCode and logs. Any suggestions?
[[ADClient sharedClient] requestAttributionDetailsWithBlock:^void (NSDictionary* attributionDetails, NSError* error)
{
if (error == nil)
{
NSLog(#"Attributes found");
}
else
{
NSLog(#"Request search ads attributes failed with error: %#", error.description);
}
}];
So, I figured out the problem. My code was calling ADClient elsewhere (determineAppInstallationAttributionWithCompletionHandler) and for some reason it was blocking my requestAttributionDetailsWithBlock completion handler from executing. After I commented out the code, everything worked fine. Still not exactly sure why it works, but it does!
I have a method that accepts an NSString by reference, and the idea is that if an error occurs, the string will contain a specific error message; otherwise, it'll be nil.
-(BOOL)doStuffThatCouldProduceAnError:(NSString *)error {
...
// An error occurred, so set the string
error = #"Foo Bar is invalid"
return NO;
}
But the problem is, the caller of doStuffThatCouldProduceAnError doesn't see the error message:
-(void)someMethod {
NSString *error;
[self doStuffThatCouldProduceAnError:error];
[NSLog #"Message: %#", error]; // Logs "[nil]"
}
I'm not sure how to search for a solution, and what I did try to search on doesn't cover the passing by reference and setting from another method. I've also tried NSMutableString, but that doesn't seem to make any difference.
Thank you in advance!
Edit: I forgot to mention that I've tried using error = [error stringByAppendingString:...], but that didn't work either.
You are not passing by reference.
You got to do
-(BOOL)doStuffThatCouldProduceAnError:(NSString **)error {
*error = #"Foo Bar is invalid"
and
[self doStuffThatCouldProduceAnError:&error];
I'm trying to retrieve the current user's current geopoint, but nothing happens when I call the geoPointForCurrentLocationInBackground:. I can't log the NSLog's from the block, however the other NSLog appears in the console.
- (IBAction)whereCurrentUser:(id)sender {
[PFGeoPoint geoPointForCurrentLocationInBackground:^(PFGeoPoint *geoPoint, NSError *error) {
if (geoPoint) {
NSLog(#"GEOPOINT %#", geoPoint);
}
else
{
NSLog(#"ERROR");
}
}];
NSLog(#"BUTTON TAPPED");
}
Do I need to implement something else? The documentation is quite clear therefore I can't figure out what could be the problem.
There's a solution posted on Parse.com PFGeoPoint.geoPointForCurrentLocationInBackground not doing anything. Basically update Parse to the latest version and add the NSLocationWhenInUseUsageDescription key to your Info.plist, this is a change made in iOS 8 and is needed whenever you want to use an users location within your app.
In a iOS app I'm developing I'm using parse.com as my backend. SDK Version is 1.2.20.
The problems come when performing fetches like this:
[PFObject fetchAllIfNeededInBackground:objectsToFetch block:^(NSArray *objects, NSError *error) {
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self.collectionView reloadData];
}];
}];
This block never gets called, with or without error. There is no timeout and there is no way to debug or to know what is going on.
I have also tried this form:
NSOperationQueue * parseQueue = [NSOperationQueue new];
[parseQueue addOperationWithBlock:^{
[PFObject fetchAll:objectsToFetch];
[self.collectionView reloadData];
}];
I set a breakpoint on reload data and it is never hit.
"po objectsToFetch" from the debugger console:
<__NSArrayM 0x11620c710>(
<Object:g06aHOTaLI:(null)> {
},
<Object:XDTcLQCegF:(null)> {
},
<Object:KCIFxCSBUw:(null)> {
},
<Object:g06aHOTaLI:(null)> {
},
<Object:0PjRyl9cC4:(null)> {
},
<Object:WjYY01c931:(null)> {
},
<Object:m9F2Dm8HhD:(null)> {
}
)
Can anyone point me in the right direction to solve this issue?
After a lot of toil, I have discovered that this problem only happens when there are duplicate objects in the array to fetch.
As you can see in the array I posted above there were in fact duplicate objects.
I would like a response from someone parse.com. is this is the expected behavior or is it a bug?
If its the expected behavior, the block should be called with an error and a message saying that duplicate objects are not allowed when performing a batch fetch.
There is nothing about this in the documentation.
If it is a bug, I would be happy to post a bug report.
I’m testing code based on the firechat-ios example. I’ve added the FirebaseSimpleLogin call loginToFacebookAppWithId and have it set up so that one view controller performs the login and then transitions to a different view controller that holds the chat logic:
self.firebase = [[Firebase alloc] initWithUrl:#"https://xxxxx.firebaseio.com/"];
[self observeEventType:FEventTypeChildAdded withBlock:^(FDataSnapshot *snapshot) {
NSLog(#"%#", snapshot.value);
// Add the chat message to the array.
[self.chat addObject:snapshot.value];
// Reload the table view so the new message will show up.
[self.tableView reloadData];
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:([self.tableView numberOfRowsInSection:0] - 1) inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}];
FirebaseSimpleLogin *authClient = [[FirebaseSimpleLogin alloc] initWithRef:self.firebase];
[authClient loginToFacebookAppWithId:kFacebookAppID permissions:#[#"email"]
audience:ACFacebookAudienceOnlyMe
withCompletionBlock:^(NSError *error, FAUser *user) {
if (error != nil) {
// There was an error logging in
NSLog(#"facebook error");
} else {
// We have a logged in facebook user
NSLog(#"facebook logged in");
[authClient checkAuthStatusWithBlock:^(NSError* error, FAUser* user) {
if (error != nil) {
// Oh no! There was an error performing the check
NSLog(#"auth error");
} else if (user == nil) {
// No user is logged in
NSLog(#"auth not logged in");
} else {
// There is a logged in user
NSLog(#"auth logged in");
// segue to the chat view controller
[self performSegueWithIdentifier:#"segueToViewController" sender:self];
}
}];
}
}];
Here are the firebase rules:
{
"rules": {
".read": "auth != null",
".write": "auth != null"
}
}
The problem is, about 10% of the time, the UITableView of the chat messages is blank, and I don’t see any chat message entries in the log. I’ve tried playing around with the order of observeEventType, putting it before and after the loginToFacebookAppWithId call.
I’m wondering if there is a race condition where maybe the messages are arriving before I call observeEventType. I’ve checked the return value of observeEventType and I get a FirebaseHandle of 1 even when no messages arrive. I’ve also upgraded the firebase framework that comes with firechat ios to https://cdn.firebase.com/ObjC/Firebase.framework-LATEST.zip and it still fails.
I thought that maybe the connection dropped, but I’m able to post messages with childByAutoId after I’ve authenticated and see them appear on the firebase server. I just never receive any messages.
I wonder if it’s trying to send me the messages in the brief moment before I’m authenticated, and failing because I don’t have read permission. Is there a way to delay event observations until after I’m in?
I’ve tried everything I can think of but I can’t make it work reliably.
---------- UPDATE ----------
I seem to be able to log in every time if I type my credentials manually. I'm currently checking for a previous successful login with:
[FBSession openActiveSessionWithAllowLoginUI:false]
To determine if I successfully logged in on the last launch of the app. If it fails, I go to a view controller for FirebaseSimpleLogin. But if it works, I call FirebaseSimpleLogin in the current view controller and wait till it succeeds in the background.
I'm running in the simulator, so I tried deleting the preferences plist at:
~/Library/Application Support/iPhone Simulator/7.0.3/Applications/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/Library/Preferences/com.xxxxxxxxxx.plist
and relaunching, which forces me to re-authenticate. Then I tried typing in my credentials and logging in 25 times without a problem.
So I think the problem is either somehow related to trying to login with Facebook before I use FirebaseSimpleLogin, or logging in with credentials from the previous launch (without bringing up the login dialog). I'm still trying to narrow down the culprit.
---------- UPDATE 2 ----------
I just wanted to add a note that after further testing, I found that the call to:
[FBSession openActiveSessionWithAllowLoginUI:false]
has no effect on FirebaseSimpleLogin. If I skip that call altogether and simply substitute true or false there, I can reproduce the issue. The problem turned out to be a race condition, see my answer below.
I finally figured out what was happening, it was due a wrong assumption on my part about UIViewController message callbacks and CFRunLoop.
The code sample in my question was distilled down from my real code to remove extraneous calls, but it turns out the part I removed was actually the culprit. I had written a function to log in and wait until success or failure on the spot (rather than receiving the response in a block later) by using a run loop:
-(bool)loginUsingFacebookReturningError:(NSError**)error andUser:(FAUser**)user
{
__block NSError *errorTemp;
__block FAUser *userTemp;
[self loginUsingFacebookWithCompletionBlock:^(NSError *error, FAUser *user) {
errorTemp = error;
userTemp = user;
CFRunLoopStop(CFRunLoopGetCurrent());
}];
CFRunLoopRun(); // needs a timeout or way for the user to cancel but I haven't implemented it yet
if(error) *error = errorTemp;
if(user) *user = userTemp;
return !errorTemp && userTemp;
}
-(void)loginUsingFacebookWithCompletionBlock:(void (^)(NSError* error, FAUser* user))block
{
FirebaseSimpleLogin *authClient = [[FirebaseSimpleLogin alloc] initWithRef:self.firebase];
[authClient loginToFacebookAppWithId:kFacebookAppID permissions:#[#"email"]
audience:ACFacebookAudienceOnlyMe
withCompletionBlock:^(NSError *error, FAUser *user) {
if (error != nil) {
// There was an error logging in
NSLog(#"facebook error");
block(error, nil);
} else {
// We have a logged in facebook user
NSLog(#"facebook logged in");
[authClient checkAuthStatusWithBlock:block];
}
}];
}
This was called with:
NSError *error;
FAUser *user;
bool success = [self loginUsingFacebookReturningError:&error andUser:&user];
The way loginUsingFacebookReturningError works is, it calls loginUsingFacebookWithCompletionBlock which fires off the loginToFacebookAppWithId and checkAuthStatusWithBlock messages like usual, but then I start a run loop. The run loop allows processing to happen in the background, even though the main thread pauses on CFRunLoopRun() until the completion block calls CFRunLoopStop().
What I hadn't realized is that run loops continue to process the application's messages in the background. So while I thought program flow had stopped in viewDidLoad, it had actually continued and called viewWillAppear, which is where I had placed my call to observeEventType (because I assumed that authentication would be complete by the time the program got there).
This created a race condition where the program attached the observeEventType callback during the time that Facebook and Firebase were authenticating. 90% of the time, authentication had completed before observeEventType was called, but 10% of the time there was lag or other network delays and observeEventType was called prematurely.
I fixed the problem by moving the FirebaseSimpleLogin code to its own view controller in the storyboard, and using the completion block to initiate the segue to the next view controller, which installed the observeEventType callback.
So to summarize: the solution is to call FirebaseSimpleLogin's authentication, and then AFTER it has finished and the completion block is done, call observeEventType. Otherwise Firebase's rules will deny your request to see data that's only visible to authenticated users (which is correct).
Here is the final code, untested but the method works:
// only global for illustration purposes, should really go in a singleton or AppDelegate, or be passed through the segue to the next view controller
Firebase *gFirebase;
// LoginViewController (root view controller in storyboard)
- (void)viewDidLoad
{
[super viewDidLoad];
gFirebase = [[Firebase alloc] initWithUrl:#"https://xxxxx.firebaseio.com/"];
FirebaseSimpleLogin *authClient = [[FirebaseSimpleLogin alloc] initWithRef:gFirebase];
[authClient loginToFacebookAppWithId:kFacebookAppID permissions:#[#"email"]
audience:ACFacebookAudienceOnlyMe
withCompletionBlock:^(NSError *error, FAUser *user) {
if (error != nil) {
// There was an error logging in
NSLog(#"facebook error");
} else {
// We have a logged in facebook user
NSLog(#"facebook logged in");
[authClient checkAuthStatusWithBlock:^(NSError* error, FAUser* user) {
if (error != nil) {
// Oh no! There was an error performing the check
NSLog(#"auth error");
} else if (user == nil) {
// No user is logged in
NSLog(#"auth not logged in");
} else {
// There is a logged in user
NSLog(#"auth logged in");
// segue to the chat view controller
[self performSegueWithIdentifier:#"segueToViewController" sender:self];
}
}];
}
}];
}
// ViewController (destination of segueToViewController)
- (void)viewDidLoad
{
[super viewDidLoad];
[gFirebase observeEventType:FEventTypeChildAdded withBlock:^(FDataSnapshot *snapshot) {
NSLog(#"%#", snapshot.value);
// Add the chat message to the array.
[self.chat addObject:snapshot.value];
// Reload the table view so the new message will show up.
[self.tableView reloadData];
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:([self.tableView numberOfRowsInSection:0] - 1) inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}];
}