What is wrong with this locking scheme - objective-c

for (NSString * district in allLinedStrings) {
PO1(district);
[self.mainLock lock];
CLGeocoder * geocode= [[CLGeocoder alloc]init];
[geocode geocodeAddressString:district completionHandler:^(NSArray *placemarks, NSError *error )
{
for (CLPlacemark * thePlace in placemarks)
{
[self handlePlacemark:thePlace];
}
[self.mainLock unlock];
}];
}
I want to run geocodeAddressString synchronously and I do this. Somehow I got error of deadlock. But what's wrong?

If you are using NSLock: Calling the lock method twice on the same thread will lock up your thread permanently.
for (NSString * district in allLinedStrings) {
PO1(district);
[self.mainLock lock];
CLGeocoder * geocode= [[CLGeocoder alloc]init];
[geocode geocodeAddressString:district completionHandler:^(NSArray *placemarks, NSError *error )
{
for (CLPlacemark * thePlace in placemarks)
{
[self handlePlacemark:thePlace];
}
}];
[self.mainLock unlock];
}

Related

Why does CLGeocoder crash upon call

I want to convert an Address into a Location on the Mac for routing purposes.
I am using
[[CLGeocoder alloc] geocodeAddressString:#"1 Infinite Loop, Cupertino, CA 95014"
completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {
if(error){
NSLog(#"%#", error);
}
if(placemarks){
NSLog(#"%#", placemarks);
}
}];
Upon runtime the execution crashes with a Bad Excess on the snippets first line.
Does anyone has an idea on how to overcome this?
You need to init CLGeocoder.
CLGeocoder *geoCoder = [[CLGeocoder alloc] init];
[geoCoder geocodeAddressString:#"1 Infinite Loop, Cupertino, CA 95014"
completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {
if(error){
NSLog(#"%#", error);
}
if(placemarks){
NSLog(#"%#", placemarks);
}
}];

FBSDKLoginManager keep Facebook login since get user info

I get user name and email using this (see below).
the problem, are that the login webview close, the call to initWithGraphPath:#"me" was done async, so user can see empty field by the time I get response to this call.
Is there a way to keep the facebook login webview open since the call to initWithGraphPath was completed?
Thanks!
-(void)loginButtonClicked_Facebook{
FBSDKLoginManager *login = [[FBSDKLoginManager alloc] init];
[login
logInWithReadPermissions: #[#"public_profile", #"email"]
fromViewController:self
handler:^(FBSDKLoginManagerLoginResult *result, NSError *error) {
if (error) {
NSLog(#"Process error");
} else if (result.isCancelled) {
NSLog(#"Cancelled");
} else {
if ([result.grantedPermissions containsObject:#"email"]){
NSLog(#"result is:%#",result);
[self fetchUserInfo];
}
}
}];
}
-(void)fetchUserInfo{
if ([FBSDKAccessToken currentAccessToken]){
NSLog(#"Token is available : %#",[[FBSDKAccessToken currentAccessToken]tokenString]);
[[[FBSDKGraphRequest alloc] initWithGraphPath:#"me" parameters:#{#"fields": #"id, name, email"}]
startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) {
if (!error){
NSString *userName = [result valueForKey:#"name"];
NSString *email = [result valueForKey:#"email"];
self.tfName.text=userName;
self.tfEmail.text=email;
self.tfEmailConfirmation.text=email;
NSLog(#"resultis:%#",result);
}else{
NSLog(#"Error %#",error);
}
}];
}
}
//YOURCONTROLLER.M
#import <FBSDKCoreKit/FBSDKCoreKit.h>
#import <FBSDKLoginKit/FBSDKLoginKit.h>
NSString *getFbid;
NSString *getFbFirstName,*getFBlastName, *getFbemail,*getfbBirthday,*getfbGender,*getFBpHone,*getFBlocation,*getFBcountry;
- (void) loginButton: (FBSDKLoginButton *)loginButton
didCompleteWithResult: (FBSDKLoginManagerLoginResult *)result
error: (NSError *)error{
NSLog(#"facebook login button test");
}
- (void) loginButtonDidLogOut:(FBSDKLoginButton *)loginButton{
NSLog(#"facebook logout button test");
}
-(void)loginButtonClicked
{
NSUserDefaults *defFacebookData = [NSUserDefaults standardUserDefaults];
FBSDKLoginManager *login = [[FBSDKLoginManager alloc] init];
[login
logInWithReadPermissions: #[#"public_profile", #"user_friends", #"email"]
fromViewController:self
handler:^(FBSDKLoginManagerLoginResult *result, NSError *error) {
if (error) {
DLog(#"Process error======%#",error.description);
indicators.hidden=YES;
[indicators stopAnimating];
} else if (result.isCancelled) {
DLog(#"Cancelled");
indicators.hidden=YES;
[indicators stopAnimating];
} else {
if ([FBSDKAccessToken currentAccessToken]) {
[[[FBSDKGraphRequest alloc] initWithGraphPath:#"me" parameters:#{#"fields": #"id, name, link, first_name, last_name, picture.type(large), email, birthday, bio ,location ,friends ,hometown , gender ,friendlists"}]
startWithCompletionHandler:^(
FBSDKGraphRequestConnection *connection, id result, NSError *error) {
if (!error)
{
// NSLog(#"fetched user:%#", result);
// [self fetchingFacebookFriends];
[defFacebookData setObject:[result objectForKey:#"email"] forKey:#"fbEmail"];
[defFacebookData setObject:[result objectForKey:#"id"] forKey:#"fbID"];
//PASS ID
getFbid = [result objectForKey:#"id"];
NSLog(#"getFbid========>%#",getFbid);
//PASS FIRST NAME
getFbFirstName=[result objectForKey:#"first_name"];
NSLog(#"first======>%#",getFbFirstName);
//PASS LAST NAME
getFBlastName=[result objectForKey:#"last_name"];
NSLog(#"first======>%#",getFBlastName);
//PASS EMAIL
getFbemail=[result objectForKey:#"email"];
NSLog(#"first======>%#",getFbemail);
//PASS PHONE
getfbGender=[result objectForKey:#"gender"];
NSLog(#"first======>%#",getfbGender);
[defFacebookData setObject:[result objectForKey:#"name"] forKey:#"fbName"];
// Image
FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc]
initWithGraphPath:[NSString stringWithFormat:#"me/picture?type=large&redirect=false"]
parameters:nil
HTTPMethod:#"GET"];
[request startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection,
id fbImageResult,
NSError *error) {
NSString *strURL = [NSString stringWithFormat:#"%#",[[fbImageResult objectForKey:#"data"] objectForKey:#"url"]];
NSLog(#"strURL=====>%#",strURL);
[defFacebookData setObject:strURL forKey:#"fbImage"];
[defFacebookData synchronize];
NSDictionary *fbdict=[NSDictionary dictionaryWithObjectsAndKeys:getFbid,#"id",getFbFirstName,#"first_name",getFBlastName,#"last_name",getFbemail,#"email",getfbGender,#"gender",strURL,#"fbImage", nil];
NSLog(#"done=========>%#",fbdict);
UIStoryboard*storyboard=[AppDelegate storyBoardType];
NSUserDefaults *defaults =[NSUserDefaults standardUserDefaults];
BOOL fblogin =[defaults boolForKey:#"KeyEditProfile"];
if (fblogin)
{
UIStoryboard*Storyboard=[AppDelegate storyBoardType];
DashboardVC* tabbarController = (DashboardVC*)[Storyboard instantiateViewControllerWithIdentifier:#"DashboardVCId"];
// tabbarController.dictFacebookdict =fbdict;
// tabbarController.strFBlogin =#"fbAllDataValue";
indicators.hidden=YES;
[indicators stopAnimating];
[self.navigationController pushViewController:tabbarController animated:YES];
}
else
{
EditFBVC *cpvc=(EditFBVC*)[storyboard instantiateViewControllerWithIdentifier:#"EditFBVCId"];
NSLog(#"get fb id ===%#",getFbid);
// cpvc.checkBtnclick =#"1";
cpvc.dictFacebookdict =fbdict;
cpvc.strFBlogin =#"fbAllDataValue";
indicators.hidden=YES;
[indicators stopAnimating];
[self.navigationController pushViewController:cpvc animated:YES];
} // [self facebookdataOnServer];
}];
}
else{
//[SVProgressHUD dismiss];
DLog(#"error is %#", error.description);
}
}];
}
}
}];
}

Retrieving resultant data from blocks

-(void) ReverseGeocode
{
CLGeocoder *geocoder=[[CLGeocoder alloc]init];
[geocoder reverseGeocodeLocation:self.toLocation
completionHandler:^(NSArray *placemarks, NSError *error){
if(error){
NSLog(#"Geocode failed with error: %#",error);
return;
}
if(placemarks &&placemarks.count>0){
CLPlacemark *placemark=placemarks[0];
NSDictionary *addressDictionary=placemark.addressDictionary;
NSString *Address=[addressDictionary objectForKey:(NSString*)
kABPersonAddressStreetKey];
NSString *City=[addressDictionary objectForKey:(NSString*)
kABPersonAddressCityKey];
NSString *State=[addressDictionary objectForKey:(NSString*)
kABPersonAddressStateKey];
NSString *Zip=[addressDictionary objectForKey:(NSString*)
kABPersonAddressZIPKey];
NSString *adress=[NSString localizedStringWithFormat:#"%# %# %# %# %#",
Address,City,State,#"United States",Zip];
// return adress;
}
}];
}
Above is a method for ReverseGeocoding.
I am trying to retrieve the final result, NSString adress. How can I do that. I have many such methods where I need to retrieve the data.
- (void)reverseGeocodeWithCompletionHandler:(void (^)(NSString *address))completionHandler {
[geocoder reverseGeocodeLocation:self.toLocation
completionHandler:^(NSArray *placemarks, NSError *error){
if(error) {
NSLog(#"Geocode failed with error: %#",error);
completionHandler(nil);
} else if {
if(placemarks &&placemarks.count>0) {
//...
NSString *adress=[NSString localizedStringWithFormat:#"%# %# %# %# %#",
Address,City,State,#"United States",Zip];
completionHandler(address);
}
}];
}
Call this method like this:
[obj reverseGeocodeWithCompletionHandler:^(NSString *address) {
NSLog(#"Address is = %#", address);
}];

Return data after AFNetworking is done

I got a async problem with my code. I got all of my webrequests in 1 class. One of my requests needs to return an NSMutableArray that another class needs to use. My webRequest code is here:
- (NSMutableArray*) getTournamentsInClub:(NSString *)clubGUID withDelegateViewController:(UIViewController *)viewController {
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
NSMutableArray *responseArray = [[[NSMutableArray alloc] init] autorelease];
NSString *URL = [[NSString alloc]initWithFormat:#"SomeURL=%#",clubGUID];
[manager POST:URL parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
for (id obj in responseObject){
//NSLog(#"obj: %#",[obj valueForKey:#"CustomerName"]);
[responseArray addObject:obj];
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
return responseArray;
}
I call the method from a viewController like this:
[self handleClubTournaments:[[TournamentsWebService sharedToursWS] getTournamentsInClub:
//Show load screen. (hide in handler function)
GP_MobilAppDelegate *xdelegate = [[UIApplication sharedApplication] delegate];
[xdelegate showLoadingScreen:self.clubToursTableView andStatus:NSLocalizedString(#"loadTours", #"")];
And my handleClubTournaments function looks like this:
-(void) handleClubTournaments:(id)result {
GP_MobilAppDelegate *xdelegate = [[UIApplication sharedApplication] delegate];
if([result isKindOfClass: [NSError class]]) {
// If an error has occurred, handle it
[xdelegate hideLoadingScreen];
[[TournamentsWebService sharedToursWS] showErrorMessageAccordingToFault:result];
return;
}
if([result isKindOfClass: [SoapFault class]]) {
[xdelegate hideLoadingScreen];
// If a server error has occurred, handle it
[[TournamentsWebService sharedToursWS] showErrorMessageAccordingToFault:result];
return;
}
//Do something with result...
if ([result count] > 0) {
NSLog(#"Antal klubturneringer: %d", [result count]);
//Start by removing excisting tours
[self.tournamentsSourceArray removeAllObjects];
NSMutableArray *tempArray=[NSMutableArray array];
for (GGTournamentData *t in result) { //cast object in result list and add them to array
[tempArray addObject:t];
}
self.tournamentsSourceArray = [self sortByStringDate:tempArray]; //sort by date
[tempArray release];
NSLog(NSLocalizedString(#"tourLoadet", #""));
}
[self.clubToursTableView reloadData];
[xdelegate hideLoadingScreen];
//Scroll view
if (self.tournamentsSourceArray.count > 0) { //hvis det er turneringer..
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:[self findIndexOfMonthClosestToDate]];
[self.clubToursTableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
}
So my problem is that the NSMutableArray gets returned before my async task is done. I know a async task behaves like that, but how do i make sure that my handleClubTournaments function don't run before my webrequest(getTournamentsInClub) got some data for it?
Thanks in advance.
I don't think you know how Asynchronous operations work. The NSMutableArray will never be set, because it is returned synchronously.
In your case, I suggest you to work with delegates.
- (void)getTournamentsInClub:(NSString *)clubGUID withDelegateViewController:(UIViewController *)viewController completionBlock:(void (^)(NSMutableArray *result))completionBlock {
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
NSMutableArray *responseArray = [[[NSMutableArray alloc] init] autorelease];
NSString *URL = [[NSString alloc]initWithFormat:#"SomeURL=%#",clubGUID];
[manager POST:URL parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
for (id obj in responseObject) {
[responseArray addObject:obj];
}
// Request finished. Call the block.
completionBlock(responseArray);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
}
- (void)handleClubTournaments {
GP_MobilAppDelegate *xdelegate = [[UIApplication sharedApplication] delegate];
[[TournamentsWebService sharedToursWS] getTournamentsInClub:^(NSMutableArray *result)
{
// Hide the Loading Indicator. Do something with the Result.
}];
// You can't access the result synchronously, therefore it's impossible to depend on it synchronously.
}
another way to return the data asynchronously would be blocks, similar to the AFNetworking solution.
You can read more about getting started with blocks here and how to use delegates here.

unexpected results from geocodeAddressString

My question is, why are poiCoordinate's latitude and longitude properties coming out 0, 0 when addressTF.text has a very sensible address string, for example "1 Infinite Loop, Cupertino, CA 95014"?
//HomeViewController.m
#implementation HomeViewController
CLLocationCoordinate2D poiCoordinate;
-(void)performStringGeocode
{
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder geocodeAddressString:_addressTF.text completionHandler:^(NSArray *placemarks, NSError *error)
{
if (error)
{
NSLog(#"Geocode failed with error: %#", error);
return;
}
CLPlacemark *placemark = [placemarks firstObject]; //Line A
poiCoordinate = placemark.location.coordinate; //Line B
}];
}