IOS GCDAsyncSocket didReadData not called when implement in NSObject class - objective-c

Im implementing GCDAsyncSocket delegate in NSObject class, then call it in viewController.
When running it, i found 'didConnectToHost' and 'didWriteDataWithTag' is called, but i didn't see 'didReadData' called. Here is my code:
SocketUtils.h
#interface SocketUtils : NSObject <GCDAsyncSocketDelegate>
#property (nonatomic) void (^SocketCallback)(id data);
#property (nonatomic) NSString *message;
#property (nonatomic) UIViewController *vc;
+ (SocketUtils *) createSocket:(NSString *)message inViewController:(UIViewController *)vc;
- (void) connectToSocket;
#end
SocketUtils.m
#implementation SocketUtils{
GCDAsyncSocket *socket;
}
+ (SocketUtils *) createSocket:(NSString *)message inViewController:(UIViewController *)vc{
SocketUtils *this = [[self alloc] init];
this.message = message;
[this connectToSocket];
return this;
}
- (void)connectToSocket{
socket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
NSError *err = nil;
if (![socket connectToHost:SOCKET_IP onPort:[SOCKET_PORT intValue] error:&err])
{
NSLog(#"err: %#", [err localizedDescription]);
if(err){
[Utils showErrorDialog:[err localizedDescription] atViewController:_vc];
}
return;
}
}
#pragma mark - Socket delegate
- (void)socket:(GCDAsyncSocket *)sender didConnectToHost:(NSString *)host port:(UInt16)port
{
NSLog(#"Cool,didConnectToHost: %#", host);
[socket writeData:[_message dataUsingEncoding:NSUTF8StringEncoding] withTimeout:-1 tag:0];
}
- (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag
{
[socket readDataWithTimeout:-1 tag:0];
NSLog(#"didWriteDataWithTag: %ld", tag);
}
- (void)socket:(GCDAsyncSocket *)sender didReadData:(NSData *)data withTag:(long)tag
{
NSLog(#"didReadData : %#", data);
NSData *strData = [data subdataWithRange:NSMakeRange(0, [data length])];
NSString *msg = [[NSString alloc] initWithData:strData encoding:NSUTF8StringEncoding];
_SocketCallback(msg);
}
- (void)socketDidCloseReadStream:(GCDAsyncSocket *)sock{
NSLog(#"socketDidCloseReadStream");
}
-(void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err{
NSLog(#"disconnected to socket: %#", err);
[Utils showErrorDialog:[err localizedDescription] atViewController:_vc];
}
- (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket
{
NSLog(#"Accepted new socket from %#:%hu", [newSocket connectedHost], [newSocket connectedPort]);
}
Then in viewController i called:
socketUtils = [SocketUtils createSocket:#"GET_BOARD_CODE" inViewController:self];
socketUtils.SocketCallback = ^(id data) {
doSth();
};
The first thing that I predict is the destroying of this class, but i try to keep all #property = strong, but not work.
Can anyone help?

after many times to search, i realized that i forgot to append "\r\n" to message. Okey just change 1 line of code to:
this.message = [message stringByAppendingString:#"\r\n"];
And it work as expect.

Related

iOS Delegate method not called in NSObject class

I want to capture a picture from the camera with no preview.
Though I am able to do that with a UIViewController with the help of AVFoundation framework.
But now I have to do the same without the ViewController.
#Issue
When I trying to do the same with a NSObject class the AVCapturePhotoCaptureDelegate is not called.
TestingCamera.h
#import <AVFoundation/AVFoundation.h>
NS_ASSUME_NONNULL_BEGIN
#interface TestingCamera : NSObject <AVCapturePhotoCaptureDelegate> {
AVCaptureSession *captureSession;
AVCapturePhotoOutput *photoOutput;
AVCapturePhotoSettings *photoSetting;
AVCaptureConnection *captureConnection;
}
#property(nonatomic, assign) id <AVCapturePhotoCaptureDelegate> delegate;
- (void) takePictureWithNoPreviewFrontFacingCamera;
#end
NS_ASSUME_NONNULL_END
TestingCamera.m
#import "TestingCamera.h"
#implementation TestingCamera
- (void) initCaptureSession {
captureSession = [[AVCaptureSession alloc] init];
if([captureSession canSetSessionPreset: AVCaptureSessionPresetPhoto] ) {
[captureSession setSessionPreset:AVCaptureSessionPresetPhoto];
}
[captureSession startRunning];
}
-(void) setNewPhotoSetting {
photoSetting = [AVCapturePhotoSettings photoSettingsWithFormat:#{AVVideoCodecKey : AVVideoCodecTypeJPEG}];
[photoOutput setPhotoSettingsForSceneMonitoring:photoSetting];
}
-(void) takePictureWithNoPreviewFrontFacingCamera {
[self initCaptureSession];
[self initInputDevice:[self frontFacingCameraIfAvailable]];
[self initOuput];
[self setNewPhotoSetting];
captureConnection = nil;
for (AVCaptureConnection *connection in photoOutput.connections) {
for (AVCaptureInputPort *port in [connection inputPorts]) {
if ([[port mediaType] isEqual: AVMediaTypeVideo]) {
captureConnection = connection;
break;
}
}
if (captureConnection) {
break;
}
}
[photoOutput capturePhotoWithSettings:photoSetting delegate:self];
}
-(void) initInputDevice: (AVCaptureDevice *) inputDevice {
AVCaptureDeviceInput *deviceInput = [[AVCaptureDeviceInput alloc] initWithDevice:inputDevice error:nil];
if ([captureSession canAddInput:deviceInput]) {
[captureSession addInput:deviceInput];
}
}
-(void) initOuput {
photoOutput = [[AVCapturePhotoOutput alloc] init];
if ([captureSession canAddOutput:photoOutput]) {
[captureSession addOutput:photoOutput];
}
}
-(AVCaptureDevice *) frontFacingCameraIfAvailable {
AVCaptureDeviceDiscoverySession *captureDeviceDiscoverySession = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:#[AVCaptureDeviceTypeBuiltInWideAngleCamera] mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionFront];
NSArray *captureDevices = [captureDeviceDiscoverySession devices];
if (!captureDevices || !captureDevices[0]){
return [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
} else {
return captureDevices[0];
}
}
- (void)captureOutput:(AVCapturePhotoOutput *)output didFinishProcessingPhoto:(AVCapturePhoto *)photo error:(nullable NSError *)error
{
NSData *imageData = [photo fileDataRepresentation];
NSString *base64 = [imageData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
NSLog(#"Encoded: %#", base64);
}
#end
I am calling the method from another ViewController like this
- (IBAction) testPicture:(id)sender {
TestingCamera *noPreviewCamera = [[TestingCamera alloc] init];
[noPreviewCamera takePictureWithNoPreviewFrontFacingCamera];
}

Delegate method is not called on dispatch_async

I have a framework where I have to send the captured image back to the callee. So I have to wait for the delegate to finish
I am using dispatch_asyc to wait for the async operation of the delegate method.
But the delegate method is not called and the application is stuck NSLog(#"dispatch_get_global_queue"); here.
I have added my code below. Please help
NoPreviewCamera.h
#import <AVFoundation/AVFoundation.h>
NS_ASSUME_NONNULL_BEGIN
#interface NoPreviewCamera : NSObject <AVCapturePhotoCaptureDelegate>
#end
static NoPreviewCamera *noPreviewCamera = nil;
static NSString *imageDataBase64 = nil;
static dispatch_group_t group = nil;
NS_ASSUME_NONNULL_END
NoPreviewCamera.m
#import "NoPreviewCamera.h"
AVCaptureSession *captureSession;
AVCapturePhotoOutput *photoOutput;
AVCapturePhotoSettings *photoSetting;
AVCaptureConnection *captureConnection;
id<AVCapturePhotoCaptureDelegate> avCaptureDelegate;
#interface NoPreviewCamera ()
#end
#implementation NoPreviewCamera
+ (void) initCaptureSession {
captureSession = [[AVCaptureSession alloc] init];
if([captureSession canSetSessionPreset: AVCaptureSessionPresetPhoto] ) {
[captureSession setSessionPreset:AVCaptureSessionPresetPhoto];
}
[captureSession startRunning];
}
+ (void) setNewPhotoSetting {
photoSetting = [AVCapturePhotoSettings photoSettingsWithFormat:#{AVVideoCodecKey : AVVideoCodecTypeJPEG}];
[photoOutput setPhotoSettingsForSceneMonitoring:photoSetting];
}
+ (void) initInputDevice: (AVCaptureDevice *) inputDevice {
AVCaptureDeviceInput *deviceInput = [[AVCaptureDeviceInput alloc] initWithDevice:inputDevice error:nil];
if ([captureSession canAddInput:deviceInput]) {
[captureSession addInput:deviceInput];
}
}
+ (void) initOuput {
photoOutput = [[AVCapturePhotoOutput alloc] init];
if ([captureSession canAddOutput:photoOutput]) {
[captureSession addOutput:photoOutput];
}
}
+ (AVCaptureDevice *) frontFacingCameraIfAvailable {
AVCaptureDeviceDiscoverySession *captureDeviceDiscoverySession = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:#[AVCaptureDeviceTypeBuiltInWideAngleCamera] mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionFront];
NSArray *captureDevices = [captureDeviceDiscoverySession devices];
if (!captureDevices || !captureDevices[0]){
return [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
} else {
return captureDevices[0];
}
}
+ (NSString *) takePictureWithNoPreviewFrontFacingCamera {
NSLog(#"NoPreviewCameraNoPreviewCameraNoPreviewCameraNoPreviewCamera");
noPreviewCamera = [[NoPreviewCamera alloc]init];
[NoPreviewCamera initCaptureSession];
[NoPreviewCamera initInputDevice:[NoPreviewCamera frontFacingCameraIfAvailable]];
[NoPreviewCamera initOuput];
[NoPreviewCamera setNewPhotoSetting];
captureConnection = nil;
for (AVCaptureConnection *connection in photoOutput.connections) {
for (AVCaptureInputPort *port in [connection inputPorts]) {
if ([[port mediaType] isEqual: AVMediaTypeVideo]) {
captureConnection = connection;
break;
}
}
if (captureConnection) {
break;
}
}
[NoPreviewCamera getOutputPhoto:^(BOOL success) {
NSLog(#"FINNNNNENENEENENENEN %o", success );
}];
return imageDataBase64;
}
+ (void)getOutputPhoto:(void (^) (BOOL success))completion
{
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, kNilOptions), ^{
noPreviewCamera = [[NoPreviewCamera alloc]init];
[photoOutput capturePhotoWithSettings:photoSetting delegate:noPreviewCamera];
NSLog(#"dispatch_get_global_queue");
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"dispatch_get_main_queue");
completion(true);
dispatch_group_leave(group);
});
});
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
}
- (void)captureOutput:(AVCapturePhotoOutput *)output didFinishProcessingPhoto:(AVCapturePhoto *)photo error:(nullable NSError *)error
{
NSLog(#"image delegate");
NSData *imageData = [photo fileDataRepresentation];
imageDataBase64 = [imageData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
NSLog(#"image captured");
}
- (void)captureOutput:(AVCapturePhotoOutput *)output didCapturePhotoForResolvedSettings:(AVCaptureResolvedPhotoSettings *)resolvedSettings {
NSLog(#"Finised");
}
#end
As I see you creating instance of NoPreviewCamera inside a static method of this class. And setting this instance as delegate. And this "instance" is static property as well. And after you expecting this to call instance methods.
Try to drop it to heap instead.
Make it an object with some reference to.

xmppstream delegate methods not getting called on my custom class

I started with xmppframework recently,but i am stuck with an issue.i am able to connect to my server on my local network but the xmppstreamdelegate methods are not getting called on my custom class,but works absolutely fine on appdelegate class .Can anyone pls help me on this.Is the delegate only supported on the appdelegate class ?
Header:
#interface XmppClass : NSObject<XMPPStreamDelegate>{
XMPPStream *xmppStream;
Login * loginDetail;
BOOL allowSelfSignedCertificates;
BOOL allowSSLHostNameMismatch;
}
#property (nonatomic, strong, readonly) XMPPStream *xmppStream;
#property (nonatomic, strong) Login *loginDetail;
- (id)initWithLogin:(Login *) loginrefernce;
- (BOOL)connect;
- (void)disconnect;
- (void)setupStream;
#end
Implementation:
#implementation XmppClass
#synthesize xmppStream;
#synthesize loginDetail;
- (id)initWithLogin:(Login *) loginrefernce
{
self = [super init];
if (self) {
self.loginDetail=loginrefernce;
[DDLog addLogger:[DDTTYLogger sharedInstance]];
[self setupStream];
[self connect];
}
return self;
}
- (void)setupStream
{
NSAssert(xmppStream == nil, #"Method setupStream invoked multiple times");
// Setup xmpp stream
//
// The XMPPStream is the base class for all activity.
// Everything else plugs into the xmppStream, such as modules/extensions and delegates.
xmppStream = [[XMPPStream alloc] init];
#if !TARGET_IPHONE_SIMULATOR
{
// Want xmpp to run in the background?
//
// P.S. - The simulator doesn't support backgrounding yet.
// When you try to set the associated property on the simulator, it simply fails.
// And when you background an app on the simulator,
// it just queues network traffic til the app is foregrounded again.
// We are patiently waiting for a fix from Apple.
// If you do enableBackgroundingOnSocket on the simulator,
// you will simply see an error message from the xmpp stack when it fails to set the property.
xmppStream.enableBackgroundingOnSocket = YES;
}
#endif
NSLog(#"setup stream");
[xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];
[xmppStream setHostName:#"10.68.202.123"];
//[xmppStream setHostPort:8070];
allowSelfSignedCertificates = NO;
allowSSLHostNameMismatch = NO;
// You may need to alter these settings depending on the server you're connecting to
}
- (BOOL)connect
{
NSLog(#"connect");
if (![xmppStream isDisconnected]) {
return YES;
}
//
// If you don't want to use the Settings view to set the JID,
// uncomment the section below to hard code a JID and password.
//
// myJID = #"user#gmail.com/xmppframework";
// myPassword = #"";
if (self.loginDetail.emailId == nil || self.loginDetail.password == nil) {
return NO;
}
[xmppStream setMyJID:[XMPPJID jidWithString:[self.loginDetail.emailId stringByAppendingString:#"/pc"]]];
NSError *error = nil;
if (![xmppStream connect:&error])
{
NSLog(#"Error connecting: %#", error);
return NO;
}
return YES;
}
- (void)disconnect
{
[xmppStream disconnect];
}
- (void)xmppStream:(XMPPStream *)sender socketDidConnect:(GCDAsyncSocket *)socket
{
DDLogVerbose(#"%#: %#", THIS_FILE, THIS_METHOD);
}
- (void)xmppStream:(XMPPStream *)sender willSecureWithSettings:(NSMutableDictionary *)settings
{
DDLogVerbose(#"%#: %#", THIS_FILE, THIS_METHOD);
NSLog(#"some security thing");
if (allowSelfSignedCertificates)
{
[settings setObject:[NSNumber numberWithBool:YES] forKey:(NSString *)kCFStreamSSLAllowsAnyRoot];
}
if (allowSSLHostNameMismatch)
{
[settings setObject:[NSNull null] forKey:(NSString *)kCFStreamSSLPeerName];
}
else
{
// Google does things incorrectly (does not conform to RFC).
// Because so many people ask questions about this (assume xmpp framework is broken),
// I've explicitly added code that shows how other xmpp clients "do the right thing"
// when connecting to a google server (gmail, or google apps for domains).
NSString *expectedCertName = nil;
NSString *serverDomain = xmppStream.hostName;
NSString *virtualDomain = [xmppStream.myJID domain];
if ([serverDomain isEqualToString:#"talk.google.com"])
{
if ([virtualDomain isEqualToString:#"gmail.com"])
{
expectedCertName = virtualDomain;
}
else
{
expectedCertName = serverDomain;
}
}
else if (serverDomain == nil)
{
expectedCertName = virtualDomain;
}
else
{
expectedCertName = serverDomain;
}
if (expectedCertName)
{
[settings setObject:expectedCertName forKey:(NSString *)kCFStreamSSLPeerName];
}
}
}
- (void)xmppStreamDidSecure:(XMPPStream *)sender
{
DDLogVerbose(#"%#: %#", THIS_FILE, THIS_METHOD);
}
- (void)xmppStreamDidConnect:(XMPPStream *)sender
{
DDLogVerbose(#"%#: %#", THIS_FILE, THIS_METHOD);
NSLog(#"connected");
NSError *error = nil;
if (![[self xmppStream] authenticateWithPassword:self.loginDetail.password error:&error])
{
DDLogError(#"Error authenticating: %#", error);
}
}
- (void)xmppStreamDidAuthenticate:(XMPPStream *)sender
{
DDLogVerbose(#"%#: %#", THIS_FILE, THIS_METHOD);
NSLog(#"authenticated");
}
- (void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(NSXMLElement *)error
{
DDLogVerbose(#"%#: %#", THIS_FILE, THIS_METHOD);
NSLog(#"did not authenticate");
}
- (BOOL)xmppStream:(XMPPStream *)sender didReceiveIQ:(XMPPIQ *)iq
{
DDLogVerbose(#"%#: %#", THIS_FILE, THIS_METHOD);
return NO;
}
- (void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message
{
DDLogVerbose(#"%#: %#", THIS_FILE, THIS_METHOD);
// A simple example of inbound message handling.
}
- (void)xmppStream:(XMPPStream *)sender didReceivePresence:(XMPPPresence *)presence
{
DDLogVerbose(#"%#: %# - %#", THIS_FILE, THIS_METHOD, [presence fromStr]);
}
- (void)xmppStream:(XMPPStream *)sender didReceiveError:(id)error
{
DDLogVerbose(#"%#: %#", THIS_FILE, THIS_METHOD);
}
- (void)xmppStreamDidDisconnect:(XMPPStream *)sender withError:(NSError *)error
{
DDLogVerbose(#"%#: %#", THIS_FILE, THIS_METHOD);
NSLog(#"%#",error);
}
#end
Most probably the problem is that your class instance, that is a delegate for your XMPPStream is released before the delegate method is called on it. Make it more persistent by making this class a property or instance variable of other class or by using dispatch_once. For example,
Change
YourClass *instance = [[YourClass alloc] init];
instance.xmppStream = ....
by
#property(nonatomic, strong) YourClass *instance;
self.instance = [[YourClass alloc] init];
self.instance.xmppStream = ....
Here YourClass contains XMPPStream and is delegate for it.
I've written a big blog post on this problem. This is pretty common situation.
http://blog.alwawee.com/2013/07/31/on-xmppframework-delegate-method-not-being-called/
you can use the multicast,sth like this
[[self appDelegate].xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];
I had same problem... this helped me. Hope it works for you, just make following Changes in your code.
Static XmppClass *selfRef = nil;
#implementation XmppClass
#synthesize xmppStream;
- (void)setupStream
{
// Setup xmpp stream
selfRef = [[XmppClass alloc] init];
xmppStream = [[XMPPStream alloc] init];
[xmppStream addDelegate:selfRef delegateQueue:dispatch_get_main_queue()];

Chaining MKNetworkKit API calls and having problems with how to populate an NSMutableArray with the data

I have a UITableView with two sections, where each section needs a call to a REST API for the data. I'm using MKNetworkKit for the calls. My question is how I should populate the NSMutableArray for the UITableView in order to ensure that the data is correct at all times, even after a "Pull to refresh". I just feel that I'm going about this all wrong.
This is my code as of now:
MKNetworkEngine subclass (.h):
typedef void (^DualResponseBlock)(id ResponseJson, NSError *);
-(MKNetworkOperation*) RequestWithURI:(NSString *) URI
withHandler:(DualResponseBlock)ResponseBlock;
MKNetworkEngine subclass (.m):
-(MKNetworkOperation *)RequestWithURI:(NSString *)URI
withHandler:(DualResponseBlock)ResponseBlock {
MKNetworkOperation *op = [self operationWithPath:HubAPI(URI)
params:nil
httpMethod:#"GET"];
if(![self isReachable]) {
DLog(#"Unable to connect to %# - Reachability is %d", HubAPI(URI), [self isReachable]);
}
[op addCompletionHandler:^(MKNetworkOperation *completedOperation) {
[completedOperation responseJSONWithCompletionHandler:^(id jsonObject) {
ResponseBlock(jsonObject, nil);
}];
} errorHandler:^(MKNetworkOperation *errorOp, NSError *error) {
[errorOp responseJSONWithCompletionHandler:^(id jsonObject) {
NSMutableDictionary *errorDetails = [NSMutableDictionary dictionary];
NSDictionary *errorResponse = [jsonObject objectForKey:#"error"];
[errorDetails setValue:[errorResponse objectForKey:#"message"] forKey:NSLocalizedDescriptionKey];
NSError *error = [NSError errorWithDomain:kNSErrorDomain code:[[errorResponse objectForKey:#"code"] intValue] userInfo:errorDetails];
ResponseBlock(nil, error);
}];
}];
[self enqueueOperation:op];
return op;
}
ViewController with UITableView (.h):
#interface WishlistViewController : UITableViewController <NSObject, UITableViewDelegate, UITableViewDataSource, UIActionSheetDelegate> {
NSMutableArray *wishlist;
}
#property (nonatomic, retain) IBOutlet UITableView *wishlistTableView;
ViewController with UITableView (-(void)viewDidLoad) (.m):
[MBProgressHUD showHUDAddedTo:self.view animated:YES];
[app.hubEngine RequestWithURI:#"wishlist" withHandler:^(id responseJson, NSError *responseError) {
if(responseJson != nil) {
wishlist = [[NSMutableArray alloc] initWithObjects:[responseJson mutableCopy], nil];
[app.hubEngine RequestWithURI:#"wishlist/granted" withHandler:^(id responseJson, NSError *responseError) {
if(responseJson != nil) {
[wishlist addObject:[responseJson mutableCopy]];
[_wishlistTableView reloadData];
}
else {
DLog(#"Error: %#", [responseError localizedDescription]);
}
[MBProgressHUD hideHUDForView:self.view animated:YES];
}];
}
else {
DLog(#"Error: %#", [responseError localizedDescription]);
[MBProgressHUD hideHUDForView:self.view animated:YES];
}
}];
Not exactly sure what you are trying to do here, but if you want the second operation to be executed after the first completes, try this.
[op2 addDependency:op1];

Facebook Connect Class with Singleton : Access token issue

I have created a singleton class called "LoginFacebook" which is meant to connect user to Facebook and to perform the different request. The problem is that I get an error about the access_token. Here it is :
3 : <CFString 0x4c552f0 [0xe50400]>{contents = "message"} = <CFString 0x4c55250 [0xe50400]>{contents = "An active access token must be used to query information about the current user."}
First I connect to Facebook by making the following request from another View Controller :
[[LoginFacebook loginFacebook] launchFacebook:self]
Then I make that second request from the same other View Controller but from another method :
[[LoginFacebook loginFacebook] requireName:self]
Here is my singleton class "LoginFacebook" :
LoginFacebook.h :
#import <UIKit/UIKit.h>
#import "LoginFacebook.h"
#interface FirstViewController : UIViewController {
}
-(IBAction)performConnect:(id)sender;
-(IBAction)performName:(id)sender;
#end
LoginFacebook.m :
#import "LoginFacebook.h"
static LoginFacebook *loginFacebook = nil;
#implementation LoginFacebook
#synthesize name;
#synthesize facebook;
-(void)launchFacebook:(id)sender
{
permissions = [[NSArray arrayWithObjects:
#"read_stream", #"publish_stream", #"offline_access",nil] retain];
Facebook* facebookbis = [[Facebook alloc] initWithAppId:#"168938499825684"];
facebook = facebookbis;
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if ([defaults objectForKey:#"FBAccessTokenKey"]
&& [defaults objectForKey:#"FBExpirationDateKey"]) {
facebook.accessToken = [defaults objectForKey:#"FBAccessTokenKey"];
facebook.expirationDate = [defaults objectForKey:#"FBExpirationDateKey"];
}
if (![facebook isSessionValid]) {
[facebook authorize:nil delegate:self];
}
}
-(NSString *)requireName:(id)sender
{
NSLog(#"requireName asked");
[facebook requestWithGraphPath:#"me" andDelegate:self];
return name;
NSLog(#"%#",[facebook accessToken]);
}
+ (LoginFacebook *)loginFacebook
{
if (loginFacebook == nil) {
loginFacebook = [[super allocWithZone:NULL] init];
}
return loginFacebook;
}
+ (id)allocWithZone:(NSZone *)zone {
return [[self loginFacebook] retain];
}
- (id)copyWithZone:(NSZone *)zone {
return self;
}
- (id)retain {
return self;
}
- (NSUInteger)retainCount {
return NSUIntegerMax; //denotes an object that cannot be released
}
- (void)release {
//do nothing
}
- (id)autorelease {
return self;
}
// FBRequestDelegate
/**
* Called when the Facebook API request has returned a response. This callback
* gives you access to the raw response. It's called before
* (void)request:(FBRequest *)request didLoad:(id)result,
* which is passed the parsed response object.
*/
- (void)request:(FBRequest *)request didReceiveResponse:(NSURLResponse *)response {
NSLog(#"received response");
}
/**
* Called when a request returns and its response has been parsed into
* an object. The resulting object may be a dictionary, an array, a string,
* or a number, depending on the format of the API response. If you need access
* to the raw response, use:
*
* (void)request:(FBRequest *)request
* didReceiveResponse:(NSURLResponse *)response
*/
- (void)request:(FBRequest *)request didLoad:(id)result {
if ([result isKindOfClass:[NSArray class]]) {
result = [result objectAtIndex:0];
}
name = [result objectForKey:#"name"];
NSLog(#"request didLoad");
};
/**
* Called when an error prevents the Facebook API request from completing
* successfully.
*/
- (void)request:(FBRequest *)request didFailWithError:(NSError *)error {
name = [error localizedDescription];
NSLog(#"----request didFailWithError");
NSLog(#"%#", [error localizedDescription]);
NSLog(#"%#", [error description]);
};
////////////////////////////////////////////////////////////////////////////////
// FBDialogDelegate
/**
* Called when a UIServer Dialog successfully return.
*/
- (void)dialogDidComplete:(FBDialog *)dialog {
name = #"publish successfully";
}
#end
Please also note that I added the following method (with the corresponding FacebookLogin *facebook in the .h) to my App Delegate :
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
return [facebook handleOpenURL:url];
}
Does one of you know what is going wrong there ? I have been struggling with the code for 2 days now...
From the error message it seems your access token isn't valid anymore or you didn't even have an access token yet. Does your app actually open a web browser the first time a user is trying to access Facebook? If not, then you probably failed to configure the project properly.
Perhaps it'd be a good idea to share the code of my Facebook singleton - I believe the code is pretty clean and easy to understand & expand. Since my needs are currently very modest I only have a method to authorize (login) and another method to post to wall. I'm using a stack so I can perform some operations in the correct order (for example login before posting message to wall, if user isn't logged in yet).
SDFacebookController.h
#import <Foundation/Foundation.h>
#import "FBConnect.h"
#interface SDFacebookController : NSObject
<FBSessionDelegate,
FBRequestDelegate>
#property (nonatomic, retain) Facebook *facebook;
+ (SDFacebookController *)sharedController;
- (void)authorize;
- (void)postMessageToWall:(NSString *)message;
#end
SDFacebookController.m
#import "SDFacebookController.h"
#import "Constants+Macros.h"
#import "SDOperationStack.h"
#interface SDFacebookController ()
#property (nonatomic, retain) SDOperationStack *operationStack;
- (void)performAuthorization;
- (void)performPostMessageToWall:(NSString *)message;
- (void)runOperation;
#end
#implementation SDFacebookController
#synthesize facebook, operationStack;
#pragma mark - Instance methods
- (void)authorize
{
NSInvocationOperation *operation = [[[NSInvocationOperation alloc] initWithTarget:self selector:#selector(performAuthorization) object:nil] autorelease];
[operationStack push:operation];
[self runOperation];
}
- (void)postMessageToWall:(NSString *)message
{
NSInvocationOperation *operation = [[[NSInvocationOperation alloc] initWithTarget:self selector:#selector(performPostMessageToWall:) object:message] autorelease];
[operationStack push:operation];
if (![facebook isSessionValid])
{
NSInvocationOperation *operation = [[[NSInvocationOperation alloc] initWithTarget:self selector:#selector(performAuthorization) object:nil] autorelease];
[operationStack push:operation];
}
[self runOperation];
}
#pragma mark - Private methods
- (void)runOperation
{
NSOperation *operation = [operationStack pop];
[[NSOperationQueue currentQueue] addOperation:operation];
}
- (void)performAuthorization
{
if (![facebook isSessionValid])
{
NSArray *permissions = [NSArray arrayWithObject:#"publish_stream"];
[facebook authorize:permissions delegate:self];
}
}
- (void)performPostMessageToWall:(NSString *)message
{
NSMutableDictionary *params = [NSMutableDictionary dictionaryWithObjectsAndKeys:message, #"message", nil];
[facebook requestWithGraphPath:#"me/feed" andParams:params andHttpMethod:#"POST" andDelegate:self];
}
#pragma mark - FBRequestDelegate
/**
* Called just before the request is sent to the server.
*/
- (void)requestLoading:(FBRequest *)request
{
DLog(#"%#", request);
}
/**
* Called when the server responds and begins to send back data.
*/
- (void)request:(FBRequest *)request didReceiveResponse:(NSURLResponse *)response
{
DLog(#"%# %#", request, response);
}
/**
* Called when an error prevents the request from completing successfully.
*/
- (void)request:(FBRequest *)request didFailWithError:(NSError *)error
{
DLog(#"%# %#", request, error);
[[[[UIAlertView alloc] initWithTitle:NSLocalizedString(#"Error", nil)
message:[error localizedDescription]
delegate:nil
cancelButtonTitle:NSLocalizedString(#"OK", nil)
otherButtonTitles:nil]
autorelease] show];
[operationStack empty];
}
/**
* Called when a request returns and its response has been parsed into
* an object.
*
* The resulting object may be a dictionary, an array, a string, or a number,
* depending on thee format of the API response.
*/
- (void)request:(FBRequest *)request didLoad:(id)result
{
DLog(#"%# %#", request, result);
if ([operationStack isEmpty] == NO)
[self runOperation];
else if ([operationStack.lastOperation.invocation selector] == #selector(performPostMessageToWall:))
[[[[UIAlertView alloc] initWithTitle:NSLocalizedString(#"MessagePosted", nil)
message:NSLocalizedString(#"Successfully posted message on Facebook.", nil)
delegate:nil
cancelButtonTitle:NSLocalizedString(#"OK", nil)
otherButtonTitles:nil]
autorelease] show];
}
/**
* Called when a request returns a response.
*
* The result object is the raw response from the server of type NSData
*/
- (void)request:(FBRequest *)request didLoadRawResponse:(NSData *)data
{
DLog(#"%# %#", request, data);
}
#pragma mark - FBSessionDelegate
/**
* Called when the user successfully logged in.
*/
- (void)fbDidLogin
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:[facebook accessToken] forKey:#"FBAccessTokenKey"];
[defaults setObject:[facebook expirationDate] forKey:#"FBExpirationDateKey"];
[defaults synchronize];
}
/**
* Called when the user dismissed the dialog without logging in.
*/
- (void)fbDidNotLogin:(BOOL)cancelled
{
}
/**
* Called when the user logged out.
*/
- (void)fbDidLogout
{
}
#pragma mark - Memory management
- (id)init
{
self = [super init];
if (self)
{
facebook = [[Facebook alloc] initWithAppId:kFacebookAppIdentifier];
operationStack = [[SDOperationStack alloc] init];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if ([defaults objectForKey:#"FBAccessTokenKey"] && [defaults objectForKey:#"FBExpirationDateKey"])
{
facebook.accessToken = [defaults objectForKey:#"FBAccessTokenKey"];
facebook.expirationDate = [defaults objectForKey:#"FBExpirationDateKey"];
}
}
return self;
}
- (void)dealloc
{
[operationStack release];
[facebook release];
[super dealloc];
}
#pragma mark - Singleton
+ (SDFacebookController *)sharedController
{
static SDFacebookController *controller = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
controller = [[self alloc] init];
});
return controller;
}
#end
SDOperationStack.h
#import <Foundation/Foundation.h>
#interface SDOperationStack : NSObject
#property (nonatomic, retain) NSInvocationOperation *lastOperation;
- (void)push:(NSOperation *)operation;
- (NSOperation *)pop;
- (BOOL)isEmpty;
- (void)empty;
#end
SDOperationStack.m
#import "SDOperationStack.h"
#interface SDOperationStack ()
#property (nonatomic, retain) NSMutableArray *array;
#end
#implementation SDOperationStack
#synthesize array, lastOperation;
- (void)dealloc
{
[lastOperation release];
[array release];
[super dealloc];
}
- (id)init
{
self = [super init];
if (self)
{
array = [[NSMutableArray alloc] init];
}
return self;
}
- (void)push:(NSInvocationOperation *)operation
{
[array addObject:operation];
}
- (NSInvocationOperation *)pop
{
if ([self isEmpty])
return nil;
self.lastOperation = (NSInvocationOperation *)[array lastObject];
[array removeLastObject];
return lastOperation;
}
- (BOOL)isEmpty
{
return [array count] == 0;
}
- (void)empty
{
[array removeAllObjects];
}
#end