NSOperationQueue and sending delegate to main thread - iphone

Hi i have my class Sensors where i have gps,gyroscope,accelerometer and i want o send data in delegate from nsoperationqueue:
#protocol SensorsDelegate <NSObject>
#optional
- (void)motionManagerDidAccelerateData:(CMAccelerometerData *)accelerometerData;
#end
- (void)startAccelerometr
{
if (motionManager.accelerometerAvailable) {
self.motionManager.accelerometerUpdateInterval = 1.0/10.0;
[self.motionManager startAccelerometerUpdatesToQueue:self.operationQueue
withHandler:^(CMAccelerometerData *accelerometerData, NSError *error) {
if (error) {
[motionManager stopAccelerometerUpdates];
}
else {
[self.delegate motionManagerDidAccelerateData:accelerometerData];
}
}];
}
}
How to send data to delegate which is in main thread ?? Or send acceleremoter data ?

Have you tried using
[self.delegate performSelectorOnMainThread:#selector(motionManagerDidAccelerateData:) withObject:accelerometerData waitUntilDone:NO];

Put onMainThread: method in the same class and change your code like this
- (void)startAccelerometr
{
if (motionManager.accelerometerAvailable) {
self.motionManager.accelerometerUpdateInterval = 1.0/10.0;
[self.motionManager startAccelerometerUpdatesToQueue:self.operationQueue
withHandler:^(CMAccelerometerData *accelerometerData, NSError *error) {
if (error) {
[motionManager stopAccelerometerUpdates];
}
else {
[self performSelectorOnMainThread:#selector(onMainThread:)
withObject:accelerometerData waitUntilDone:NO];
}
}];
}
}
- (void)onMainThread:(id)accelerometerData{
[self.delegate motionManagerDidAccelerateData:accelerometerData];
}

You can try:
Assuming that delegate is a property of type NSObject<SensorDelegate>* assigned at Sensor init method:
[self.delegate performSelectorOnMainThread:#selector(motionManagerDidAccelerateData:) withObject:accelerometerData waitUntilDone:NO];
Hope it helps.

Change the queue you are sending the accelerometer updates to the main queue.
[self.motionManager startAccelerometerUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMAccelerometerData *accelerometerData, NSError *error) {

Related

GCDAsyncSocket "didReadDataWithTag" Never called with NSOperation subclass

In a "getMyFile" method of some XIB file.
I am creating a object of class "A"(subclass of NSOperation) and adding it to a "myFileQueue"(object of NSOperationQueue).
myFileQueue.MaxConcurrentOperationCount = 1;
Problem : didReadDataWithTag" delegate NEVER called in any case.
#import "GCDAsyncSocket.h"
//and all other required classes are imported correctly
//Class : A
#interface A : NSOperation
{
{
GCDAsyncSocket* socket;
dispatch_queue_t dQueue;
BOOL isWorkDone;
}
}
#implementation A {
-main(){
#autoreleasepool {
isWorkDone = NO;
dQueue = dispatch_queue_create(#"MyDQueue", NULL);
socket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dQueue];
//NOTE: Even after setting delegateQueue: dispatch_get_main_queue()
//didReadDataWithTag delegate never called
NSError *err = nil;
if (![socket connectToHost:#"192.168.1.142" onPort:12345 error:&err]) // Asynchronous!
{
// If there was an error, it's likely something like "already connected" or "no delegate set"
NSLog(#"I goofed: %#", err);
}
do{
[NSThread sleep:0.2];
}while(!isWorkDone)
}
}
- (void)socket:(GCDAsyncSocket *)sender didConnectToHost:(NSString *)host port:(UInt16)port
{
NSString* myString= #"testing";
NSData* data=[myString dataUsingEncoding: [NSString defaultCStringEncoding] ];
[socket writeData:data withTimeout:-1 tag:1];
}
- (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag
{
[socket readDataToData:[GCDAsyncSocket ZeroData] withTimeout:-1 tag:TAG_RESPONSE_HEADER];
}
- (void)socket:(GCDAsyncSocket *)sender didReadData:(NSData *)data withTag:(long)tag
{
NSLog(#"didReadDataWithTag called...."); //this method never called in any case
isWorkDone = YES;
}
}

Failing to authenticate local player iOS6

I'm building a Game Center game in iOS6 and am continuously running into problems with it. The current one has me stumped - Every single time my game tries to authenticate the local player it fails. Every time it is triggering my "disable game center" function and it's driving me crazy.
- (void) disableGameCenter
{
// Write something to disable gamecenter.
// gameCenterAvailable = FALSE;
}
-(void)showAuthenticationDialogWhenReasonable:(UIViewController *)viewController
{
// Pause Tasks Here
// [[[(AppDelegate *)[[UIApplication sharedApplication] delegate] window] rootViewController] presentViewController:viewController animated:YES completion:nil];
}
- (void) authenticateLocalPlayer
{
localPlayer = [GKLocalPlayer localPlayer];
__weak GKLocalPlayer* weakLocalPlayer = localPlayer;
weakLocalPlayer.authenticateHandler = ^(UIViewController *viewController, NSError *error)
{
if (viewController != nil)
{
[self showAuthenticationDialogWhenReasonable: viewController];
}
else if (weakLocalPlayer.isAuthenticated)
{
self.localPlayer = weakLocalPlayer;
}
else
{
[self disableGameCenter];
}
};
}
Forgive me if I'm wrong, but I don't think you are running the authenticate method.
Try this method instead:
- (void) authenticateLocalPlayer
{
GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];
[localPlayer authenticateWithCompletionHandler:^(NSError *error) {
if (localPlayer.isAuthenticated)
{
NSLog(#"Authenticated");
}
else
{
NSLog(#"Not Authenticated");
}
}];
}

Handle ACAccountStoreDidChangeNotification for Facebook & Twitter in iOS 6+

I am using Social framework for Facebook & Twitter integration. For this I followed this link
Everything is working fine except when I app receives ACAccountStoreDidChangeNotification , it crashes.
App crashes when
1) I installed app for first time & user don't have Facebook / Twitter account configured in Settings.
2) When I call any method of Facebook / Twitter , it gives me error code 6 as I don't have account configured which is right.
3) I click on HOME button so my app enters background & add account in Settigs.
4) Once I configured account & come back to app , my app launches from background & receives ACAccountStoreDidChangeNotification.
5) In selector of ACAccountStoreDidChangeNotification my account object is nil so my app crashes. So I checked for the nil
Here is my code
#pragma mark - Request Facebook Account Access
-(void)requestFacebookAccountAccess
{
requestType = kNoOpRequest;
// Register for Account Change notification
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(accountChanged:) name:ACAccountStoreDidChangeNotification object:nil];
self.accountStore = [[ACAccountStore alloc]init];
ACAccountType *FBaccountType= [self.accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook];
// Set facebook permissions
NSArray *permissions = nil;
// We need to ask for Basic permission first
if (![kUserDefaults boolForKey:FACEBOOK_BASIC_PERMISSION]) {
permissions = #[#"email",#"user_about_me",#"user_location",#"friends_about_me"];
self.isBasicFacebookPermissionsRequested = YES;
}
else {
permissions = #[#"email",#"user_about_me",#"user_location",#"friends_about_me",#"publish_stream"];
}
//use ACAccountStore to help create your dictionary
NSDictionary *dictFacebook = #{
ACFacebookAppIdKey: kFacebookAppIDKey,
ACFacebookPermissionsKey: permissions,
ACFacebookAudienceKey: ACFacebookAudienceFriends
};
[self.accountStore requestAccessToAccountsWithType:FBaccountType options:dictFacebook completion:^(BOOL granted, NSError *error) {
if (granted) {
if (self.isBasicFacebookPermissionsRequested) {
self.isBasicFacebookPermissionsRequested = NO;
[kUserDefaults saveBasicFacebookPermissionsGrantedStatus:YES];
//[self requestFacebookAccountAccess];
}
// User granted permission to Accout
NSArray *accountsArray = [self.accountStore accountsWithAccountType:FBaccountType];
if ([accountsArray count]>0) {
//It will always be the last object with SSO
self.facebookAccount = [accountsArray lastObject];
NSLog(#"In %s::IS MAIN THREAD:- %#",__PRETTY_FUNCTION__,[NSThread isMainThread]?#"YES":#"NO");
if ([self.delegate respondsToSelector:#selector(didFacebookAccountAccessGranted)]) {
dispatch_async(dispatch_get_main_queue(),^{
[self.delegate didFacebookAccountAccessGranted];
});
}
}
else {
[self showAlertWithTitle:#"Error" andMessage:#"You don't have any Facebook accounts set up yet.Please set a Facebbok account and try again."];
}
}
else {
DLog(#"User denied permission to Accout::Localized Error:- %#",error.localizedDescription);
if([error code]==6) {
[self showAlertWithTitle:#"Error" andMessage:#"Please setup facebook account from Settings"];
if ([self.delegate respondsToSelector:#selector(didFacebookAccountAccessDenied:)]) {
dispatch_async(dispatch_get_main_queue(),^{
[self.delegate didFacebookAccountAccessDenied:error];
});
}
}
else if ([self.delegate respondsToSelector:#selector(didFacebookAccountAccessDenied:)]) {
dispatch_async(dispatch_get_main_queue(),^{
[self.delegate didFacebookAccountAccessDenied:error];
});
}
else {
// User denied permission to Account
[self showAlertWithTitle:#"Error" andMessage:[NSString stringWithFormat:#"User denied permission to Accout::Error:- %#",error.localizedDescription]];
}
}
}];
}
#pragma mark - Get Logged In User Info
-(void)getLoggedInUserInfo
{
NSURL *requestURL = [NSURL URLWithString:#"https://graph.facebook.com/fql"];
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:#"SELECT current_location,uid,name,sex,birthday,email,pic FROM user WHERE uid=me()",#"q", nil];
requestType = kGetLoggedInUserInfoRequest;
[self requestDataUsingURL:requestURL parameters:dict requestMethod:SLRequestMethodGET];
}
#pragma mark - Request Data From Facebook
-(void)requestDataUsingURL:(NSURL*)requestURL parameters:(NSDictionary*)params requestMethod:(SLRequestMethod)reuqestMethod
{
SLRequest *request = [SLRequest requestForServiceType:SLServiceTypeFacebook
requestMethod:reuqestMethod
URL:requestURL
parameters:params];
request.account = self.facebookAccount;
[request performRequestWithHandler:^(NSData *data,
NSHTTPURLResponse *response,
NSError *error)
{
NSDictionary *dictData =[NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
if(!error)
{
if([dictData objectForKey:#"error"]!=nil)
{
DLog(#"Request Data From Facebook Errro:- %#",dictData);
[self attemptRenewCredentials];
if ([self.delegate respondsToSelector:#selector(didGetFacebookError:)]) {
dispatch_async(dispatch_get_main_queue(),^{
[self.delegate didGetFacebookError:dictData];
});
}
}
else {
// Process the response
//NSLog(#"Response Dictionary contains: %#", dictData );
if ([self.delegate respondsToSelector:#selector(didPostToFacebook:)]) {
dispatch_async(dispatch_get_main_queue(),^{
[self.delegate didPostToFacebook:dictData];
});
}
}
}
else{
if ([self.delegate respondsToSelector:#selector(didGetFacebookError:)]) {
dispatch_async(dispatch_get_main_queue(),^{
[self.delegate didGetFacebookError:dictData];
});
}
else {
[self showAlertWithTitle:#"Error" andMessage:#"Some error occured while processing Facebook request"];
}
}
}];
}
-(void)accountChanged:(NSNotification *)notif
{
if (self.facebookAccount!=nil) {
[self attemptRenewCredentials];
}
else {
[self requestFacebookAccountAccess];
}
}
#pragma mark - Attempt to Renew Credentials
-(void)attemptRenewCredentials
{
[self.accountStore renewCredentialsForAccount:(ACAccount *)self.facebookAccount completion:^(ACAccountCredentialRenewResult renewResult, NSError *error){
if(!error)
{
switch (renewResult) {
case ACAccountCredentialRenewResultRenewed:
NSLog(#"Good to go");
[self againRequestFacebook];
break;
case ACAccountCredentialRenewResultRejected:
NSLog(#"User declined permission");
break;
case ACAccountCredentialRenewResultFailed:
NSLog(#"non-user-initiated cancel, you may attempt to retry");
break;
default:
break;
}
}
else{
//handle error gracefully
NSLog(#"error from renew credentials%#",error);
}
}];
}
-(void)againRequestFacebook
{
switch (requestType) {
case kGetLoggedInUserInfoRequest:
[self getLoggedInUserInfo];
break;
case kGetFriendsInfoRequest:
[self getFacebookFriendsInfo];
break;
case kPostToWallRequest:
[self postToFacebookWallInBackgroundForUserId:fbUserIdForPost withParams:paramsToPost];
break;
default:
break;
}
}
#pragma mark - Remove Observer for Notification
-(void)dealloc
{
requestType = kNoOpRequest;
[[NSNotificationCenter defaultCenter] removeObserver:ACAccountStoreDidChangeNotification];
}
But accountChanged method gets called multiple times.
What is wrong in this ? How can I handle this situation ?
Any kind of help is highly appreciated.
Thanks in advance.
I am facing the same problem...so i used a work around by having a BOOL value in NSUserDefaults to execute accountChanged method only once
-(void)accountChanged:(NSNotification *)notif
{
BOOL calledAccountChanged =[[[NSUserDefaults standardUserDefaults] objectForKey:DidAccountChanged] boolValue];
if(!calledAccountChanged)
{
if (self.facebookAccount!=nil) {
[self attemptRenewCredentials];
}
else {
[self requestFacebookAccountAccess];
}
}
}
Then we can change DidAccountChanged value later in the code...
If you have better solution do post it...

AFNetworking - How to setup requests to be retried in the event of a timeout?

I have recently migrated from ASIHTTPRequest to AFNetworking, which has been great. However, the server that I am connecting with has some issues and sometimes causes my requests to timeout. When using ASIHTTPRequest it was possible to setup a retry count on a request in the event of a timeout using the following selector
-setNumberOfTimesToRetryOnTimeout:
This can be further referenced in this post, Can an ASIHTTPRequest be retried?
This is AFNetworking if you are unfamiliar
https://github.com/AFNetworking/AFNetworking#readme
I was unable to find an equivalent api in AFNetworking, has anyone found a solution for retrying network requests in the event of timeout using AFNetworking?
Matt Thompson developer of AFNetworking was kind enough to answer this for me. Below is the github link explaining the solution.
https://github.com/AFNetworking/AFNetworking/issues/393
Basically, AFNetworking doesn't support this functionality. It is left to the developer to implement on a case by case basis as shown below (taken from Matt Thompson's answer on github)
- (void)downloadFileRetryingNumberOfTimes:(NSUInteger)ntimes
success:(void (^)(id responseObject))success
failure:(void (^)(NSError *error))failure
{
if (ntimes <= 0) {
if (failure) {
NSError *error = ...;
failure(error);
}
} else {
[self getPath:#"/path/to/file" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
if (success) {
success(...);
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
[self downloadFileRetryingNumberOfTimes:ntimes - 1 success:success failure:failure];
}];
}
}
I implemented private method in my ApiClient class:
- (void)sendRequest:(NSURLRequest *)request successBlock:(void (^)(AFHTTPRequestOperation *operation, id responseObject))successBlock failureBlock:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failureBlock
{
__block NSUInteger numberOfRetries = 3;
__block __weak void (^weakSendRequestBlock)(void);
void (^sendRequestBlock)(void);
weakSendRequestBlock = sendRequestBlock = ^{
__strong typeof (weakSendRequestBlock)strongSendRequestBlock = weakSendRequestBlock;
numberOfRetries--;
AFHTTPRequestOperation *operation = [self.httpManager HTTPRequestOperationWithRequest:request success:successBlock failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSInteger statusCode = [[[error userInfo] objectForKey:AFNetworkingOperationFailingURLResponseErrorKey] statusCode];
if (numberOfRetries > 0 && (statusCode == 500 || statusCode == 502 || statusCode == 503 || statusCode == 0)) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
strongSendRequestBlock();
});
} else {
if (failureBlock) {
failureBlock(operation, error);
}
}
}];
[self.httpManager.operationQueue addOperation:operation];
};
sendRequestBlock();
}
Example of usage:
- (void)getSomeDetails:(DictionaryResultBlock)block
{
if (!block) {
return;
}
NSString *urlString = #"your url string";
NSMutableURLRequest *request = [self.httpManager.requestSerializer requestWithMethod:#"POST" URLString:[[NSURL URLWithString:urlString relativeToURL:self.defaultUrl] absoluteString] parameters:nil error:nil];
// Configure you request here
[request setValue:version forHTTPHeaderField:#"client-version"];
NSMutableDictionary *bodyParams = #{};
[request setHTTPBody:[NSJSONSerialization dataWithJSONObject:bodyParams options:0 error:nil]];
[self sendRequest:request successBlock:^(AFHTTPRequestOperation *operation, id responseObject) {
id response = [NSJSONSerialization JSONObjectWithData:responseObject options:0 error:nil];
block(response, nil);
} failureBlock:^(AFHTTPRequestOperation *operation, NSError *error) {
block(nil, error);
}];
}
In my case, I frequently required retry functionality so I came up wit this retry policy category that will help you with that AFNetworking+RetryPolicy
With respect to AFNetworking 3.0 it could serve well.
Based on your answers, you could do something even more generic (and tricky) by using a block taking as parameter a block :
typedef void (^CallbackBlock)(NSError* error, NSObject* response);
- (void) performBlock:(void (^)(CallbackBlock callback)) blockToExecute retryingNumberOfTimes:(NSUInteger)ntimes onCompletion:(void (^)(NSError* error, NSObject* response)) onCompletion {
blockToExecute(^(NSError* error, NSObject* response){
if (error == nil) {
onCompletion(nil, response);
} else {
if (ntimes <= 0) {
if (onCompletion) {
onCompletion(error, nil);
}
} else {
[self performBlock:blockToExecute retryingNumberOfTimes:(ntimes - 1) onCompletion:onCompletion];
}
};
});
}
Then surround your asynchronous HTTP requests like the following :
[self performBlock:^(CallbackBlock callback) {
[...]
AFHTTPRequestOperationManager *manager = [WSManager getHTTPRequestOperationManager];
[manager POST:base parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
dispatch_async(dispatch_get_main_queue(), ^(void){
if (callback) {
callback(nil, responseObject);
}
});
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
if (callback) {
NSError* errorCode = [[NSError alloc] initWithDomain:AppErrorDomain code:[operation.response statusCode] userInfo:#{ NSLocalizedDescriptionKey :error.localizedDescription}];
callback(errorCode, nil);
}
}];
} retryingNumberOfTimes:5 onCompletion:^(NSError *error, NSObject* response) {
//everything done
}];
This way the retries wait for the HTTP request to finish and you don't have to implement the retry loop in each request methods.

How Can i Know if GKAchievement is Completed?

I have built objectives for my game and everything works just fine accept the part of making the objectives not be called any more after any of them is completed.
I know there is a Property of the GKAchievement Class "completed" which is a boolean that returns yes when the Achievement is 100 percent done.
here is the method that called when a Achievement is 100 percent done it passes id which is the Achievement identifier and report the acheeee :
- (void)AchivmentDidAchive:(id)Achivment{
NSString *identifier = Achivment;
NSLog(#"%#",identifier);
self.achivment = [[GKAchievement alloc]initWithIdentifier:identifier];
self.achivment.showsCompletionBanner = YES;
if (!self.achivment.completed) {
self.achivment.percentComplete = 100;
NSLog(#"Reproting!");
[self.achivment reportAchievementWithCompletionHandler: ^(NSError *error)
{
}];
}
else {
NSLog(#"Achivment Completed!");
} }
what I am trying to do here is to set the percent completed to 100 and report it so in the next time ie want get called again.
but it always works... any better idea for how to handle this?
in interface add variable & property:
NSMutableDictionary *earnedAchievementCache;
#property (nonatomic, retain)NSMutableDictionary *earnedAchievementCache;
in .m:
#synthesize earnedAchievementCache;
- (void) submitAchievement: (NSString*) identifier percentComplete: (double) percentComplete
{
if(self.earnedAchievementCache == NULL)
{
[GKAchievement loadAchievementsWithCompletionHandler: ^(NSArray *scores, NSError *error)
{
if(error == NULL)
{
NSMutableDictionary* tempCache= [NSMutableDictionary dictionaryWithCapacity: [scores count]];
for (GKAchievement* score in scores)
{
[tempCache setObject: score forKey: score.identifier];
}
self.earnedAchievementCache= tempCache;
[self submitAchievement: identifier percentComplete: percentComplete];
}
}];
}
else
{
GKAchievement* achievement= [self.earnedAchievementCache objectForKey: identifier];
if(achievement != NULL)
{
if((achievement.percentComplete >= 100.0) || (achievement.percentComplete >= percentComplete))
{
achievement= NULL;
}
achievement.percentComplete= percentComplete;
}
else
{
achievement= [[[GKAchievement alloc] initWithIdentifier: identifier] autorelease];
achievement.percentComplete= percentComplete;
[self.earnedAchievementCache setObject: achievement forKey: achievement.identifier];
}
if(achievement!= NULL)
{
//Submit the Achievement...
if (achievement.percentComplete>=100) {
//show banner
achievement.showsCompletionBanner = YES; //only in IOS 5+
}
[achievement reportAchievementWithCompletionHandler: ^(NSError *error)
{
if (error!=NULL){
NSLog(#"Error!!");
} else NSLog(#"all is well");
}];
}
}
}
in dealloc :
[self.earnedAchievementCache release];
i'm using the cache to not submit scores already submitted / completed
PS: the code is perfect just copy and paste it into your class and it will work
this is what I use in my helper Game Center class:
-(void) reportAchievementWithID:(NSString*) AchievementID {
[GKAchievement loadAchievementsWithCompletionHandler:^(NSArray *achievements, NSError *error) {
if(error) NSLog(#"error");
for (GKAchievement *ach in achievements) {
if([ach.identifier isEqualToString:AchievementID]) { //already submitted
NSLog(#"Already submitted");
return ;
}
}
GKAchievement *achievementToSend = [[GKAchievement alloc] initWithIdentifier:AchievementID];
achievementToSend.percentComplete = 100;
achievementToSend.showsCompletionBanner = YES;
[achievementToSend reportAchievementWithCompletionHandler:NULL];
}];
}
note: I don't use percentages in my achievements, so you'd need to modify things a little bit if you do.