Block not getting called from requestAttributionDetailsWithBlock - ios

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!

Related

Can't retrieve geopoint with geoPointForCurrentLocationInBackground

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.

Fetch callback block never getting called

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.

saveInBackground is not being called

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.

How to wait for asyn operation in iOS unit test using NSConditionLock

I have a unit test in which I need to wait for an async task to finish. I am trying to use NSConditionLock as it seems to be a pretty clean solution but I cannot get it to work.
Some test code:
- (void)testSuccess
{
loginLock = [[NSConditionLock alloc] init];
Login login = [[Login alloc] init];
login.delegate = self;
// The login method will make an async call.
// I have setup myself as the delegate.
// I would like to wait to the delegate method to get called
// before my test finishes
[login login];
// try to lock to wait for delegate to get called
[loginLock lockWhenCondition:1];
// At this point I can do some verification
NSLog(#"Done running login test");
}
// delegate method that gets called after login success
- (void) loginSuccess {
NSLog(#"login success");
// Cool the delegate was called this should let the test continue
[loginLock unlockWithCondition:1];
}
I was trying to follow the solution here:
How to unit test asynchronous APIs?
My delegate never gets called if I lock. If I take out the lock code and put in a simple timer it works fine.
Am I locking the entire thread and not letting the login code run and actually make the async call?
I also tried this to put the login call on a different thread so it does not get locked.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
[login login];
});
What am I doing wrong?
EDIT adding login code. Trimmed do the code for readability sake. Basically just use AFNetworking to execute a POST. When done will call delegate methods.
Login make a http request:
NSString *url = [NSString stringWithFormat:#"%#/%#", [_baseURL absoluteString], #"api/login"];
[manager POST:url parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
if (_delegate) {
[_delegate loginSuccess];
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
if (_delegate) {
[_delegate loginFailure];
}
}];
The answer can be found in https://github.com/AFNetworking/AFNetworking/blob/master/AFNetworking/AFHTTPRequestOperation.m.
Since you are not setting the completionQueue property of the implicitly created AFHTTPRequestOperation, it is scheduling the callbacks on the main queue, which you are blocking.
Unfortunately, many answers (not all) in the given SO thread ("How to unit test asynchronous APIs?") are bogus and contain subtle issues. Most authors don't care about thread-safity, the need for memory-barriers when accessing shared variables, and how run loops do work actually. In effect, this leads to unreliable and ineffective code.
In your example, the culprit is likely, that your delegate methods are dispatched on the main thread. Since you are waiting on the condition lock on the main thread as well, this leads to a dead lock. One thing, the most accepted answer that suggests this solution does not mention at all.
A possible solution:
First, change your login method so that it has a proper completion handler parameter, which a call-site can set in order to figure that the login process is complete:
typedef void (^void)(completion_t)(id result, NSError* error);
- (void) loginWithCompletion:(completion_t)completion;
After your Edit:
You could implement your login method as follows:
- (void) loginWithCompletion:(completion_t)completion
{
NSString *url = [NSString stringWithFormat:#"%#/%#", [_baseURL absoluteString], #"api/login"];
[manager POST:url parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
if (completion) {
completion(responseObject, nil);
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
if (completion) {
completion(nil, error);
}
}];
Possible usage:
[self loginWithCompletion:^(id result, NSError* error){
if (error) {
[_delegate loginFailure:error];
}
else {
// Login succeeded with "result"
[_delegate loginSuccess];
}
}];
Now, you have an actual method which you can test. Not actually sure WHAT you are trying to test, but for example:
-(void) testLoginController {
// setup Network MOCK and/or loginController so that it fails:
...
[loginController loginWithCompletion:^(id result, NSError*error){
XCTAssertNotNil(error, #"");
XCTAssert(...);
<signal completion>
}];
<wait on the run loop until completion>
// Test possible side effects:
XCTAssert(loginController.isLoggedIn == NO, #""):
}
For any other further steps, this may help:
If you don't mind to utilize a third party framework, you can then implement the <signal completion> and <wait on the run loop until completion> tasks and other things as described here in this answer: Unit testing Parse framework iOS

Prevent UIDocument openWithCompletionHandler being called when already opening

I have a singleton class (DTTSingleton) with the following methods:
+ (UIManagedDocument *)managedDocument
{
static UIManagedDocument *managedDocument = nil;
static dispatch_once_t mngddoc;
dispatch_once(&mngddoc, ^
{
if(!managedDocument)
{
NSURL *url = [[DTTHelper applicationDocumentsDirectory] URLByAppendingPathComponent:kDTTDatabaseName];
managedDocument = [[DTTManagedDocument alloc] initWithFileURL:url];
}
});
return managedDocument;
}
+ (void)useDefaultDocumentWithBlock:(completion_block_t)completionBlock
{
if (![[NSFileManager defaultManager] fileExistsAtPath:[DTTSingleton.managedDocument.fileURL path]])
{
[DTTSingleton.managedDocument saveToURL:DTTSingleton.managedDocument.fileURL forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success)
{
if (success)
{
completionBlock(DTTSingleton.managedDocument.managedObjectContext);
}
else
{
NSLog(#"Failed to save!");
}
}];
}
else if (DTTSingleton.managedDocument.documentState == UIDocumentStateClosed)
{
[DTTSingleton.managedDocument openWithCompletionHandler:^(BOOL success)
{
if (success)
{
completionBlock(DTTSingleton.managedDocument.managedObjectContext);
}
else
{
NSLog(#"Failed to open!");
}
}];
}
else if (DTTSingleton.managedDocument.documentState == UIDocumentStateNormal)
{
completionBlock(DTTSingleton.managedDocument.managedObjectContext);
}
}
And in my UITableViewController I have the following code in the viewDidLoad method:
[DTTSingleton useDefaultDocumentWithBlock:^(NSManagedObjectContext *moc)
{
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"SomeEntity"];
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:moc cacheName:nil];
}];
[DTTSingleton useDefaultDocumentWithBlock:^(NSManagedObjectContext *moc)
{
NSLog(#"When this is called it errors because DTTSingleton is already trying to open it!");
}];
When executed I get the error:
Terminating app due to uncaught exception
'NSInternalInconsistencyException', reason: 'attempt to open or a
revert document that already has an open or revert operation in flight
I understand why I'm getting this error, it's because I'm trying to open the document when another opening process is already running. So my question are...
1) How do I ensure openWithCompletionHandler is only called once?
2) How do I ensure the second block is executed once the document has opened?
Thanks for any help!
I'm not sure if you've seen this yet, but a good resource would probably be here: http://adevelopingstory.com/blog/2012/03/core-data-with-a-single-shared-uimanageddocument.html
In the event that you're just trying to create your own (rather than using the above link - or similar) and are looking for some input, I can point out a few things I see (though I do not claim by any means to be an expert)...
Anyways, I believe your issue stems here:
// ....
} else if(DTTSingleton.managedDocument.documentState == UIDocumentStateClosed) {
[DTTSingleton.managedDocument openWithCompletionHandler:^(BOOL success) {
if(success) {
completionBlock(DTTSingleton.managedDocument.managedObjectContext);
} else {
NSLog(#"Failed to open!");
}
}];
}
The method openWithCompletionHandler attempts to open a connection to the document asynchronously. The issue with this is that, on your first call to open the document in your UITableView, the code you're using notices the document is closed - so it attempts to open it. This is all fine and dandy, but the code you're using here then re-issues yet another attempt to create an instance of the singleton. More than likely, this is happening so fast (and close together) that it, yet again, attempts to open the document asynchronously.
To test this, try putting a breakpoint after the UIDocumentStateClosed check for the line:
[DTTSingleton.managedDocument openWithCompletionHandler:^(BOOL success)
I believe you'll see this being executed numerous times...
I'm not skilled enough to explain how to solve this, but I would seriously recommend using the approach shown in the link above where he applies a block to assign/track the existence of an open document
Hopefully that helps?
Edit:
* Added the suggestion for the breakpoint.
Edit: Here is another stackoverflow ticket with a similar issue (and suggestion/conclusion) - so I may not be too far off here after all =)
Just thought I'd post back as say the issues I was having have been solved by disabling buttons that access Core Data until the document is ready, this way you'll never try to open the document at the same time as another process. And as for Core Data access in a life cycle handler like viewDidLoad, I implemented a system where by if the document is opening (state kept manually with a variable) it delays the call by looping until the document is open, in essence queuing the calls. Don't forget to use performSelector:withObject:afterDelay: in the call within to loop otherwise you'll get an application crash.
Thanks for your suggestions John.

Resources