I am using XMPPFramework to implement group chat functionality in my app. The one-to-one chat works fine, but when I join a room by calling [xmppRoom joinRoomUsingNickname], the stream disconnects without giving any error.
I implemented xmppStreamDidDisconnect:withError too, but it's still giving nil error. The user also leaves the room immediately after joining it, since the stream disconnects. I am also using Reconnect module, but when it reconnects, the room is not auto-joined.
I am using pidgin to test it too, but it works fine there. What could be the reason for immediate disconnection?
PS: I am using testing it on iPhone 5 running on iOS 9.1
Update: It's now giving the following error -
Error Domain=GCDAsyncSocketErrorDomain Code=7 "Socket closed by remote
peer" UserInfo={NSLocalizedDescription=Socket closed by remote peer}
Try this modules
MessageManager.h
// Created by Deepak MK on 27/11/15.
//
#import <UIKit/UIKit.h>
#import "XMPP.h"
#import <CoreData/CoreData.h>
#import "XMPPFramework.h"
#import "XMPPMessageDeliveryReceipts.h"
#import "XMPPLastActivity.h"
#import "XMPPRosterMemoryStorage.h"
#import "XMPPRoomMemoryStorage.h"
#import "XMPPvCardCoreDataStorage.h"
#import "XMPPvCardTemp.h"
/**
message manager class manage all message sequence.
*/
#interface MessageManager : NSObject
{
XMPPStream *xmppStream;
NSString *password;
BOOL isOpen;
BOOL isRegistering;
id _delgate;
XMPPRosterCoreDataStorage *xmppRosterStorage;
XMPPRoster *xmppRoster;
XMPPvCardCoreDataStorage* xmppvCardStorage;
XMPPvCardTempModule*xmppvCardTempModule;
}
+ (MessageManager *) sharedMessageHandler;
/**
The stream varible for connecting stream
*/
#property (nonatomic, readonly) XMPPStream *xmppStream;
/**
XMPPRoster varible
*/
#property (nonatomic, strong,readonly) XMPPRoster *xmppRoster;
/**
XMPPRosterCoreDataStorage varible
*/
#property (nonatomic, strong,readonly) XMPPRosterCoreDataStorage *xmppRosterStorage;
/**
XMPPRoom varible
*/
#property (nonatomic, strong, readonly) XMPPRoom *xmppRoom;
/**
Setting of delegate
#param delegate class delegate
*/
- (void) setdelegate:(id)delegate;
/**
Return of delegate
*/
- (id) delegate;
/**
connecting stream of Xmpp server
*/
- (void) setupStream;
/**
Connect user to Xmpp server
#param jabberID login user name
#param myPassword login password
*/
- (BOOL)connectWithUserId:(NSString*)jabberID withPassword:(NSString*)myPassword;
/**
Connect user to Xmpp server
#param userName login user name
#param myPassword login password
*/
- (void) authenticateUserWIthUSerName:(NSString*)userName withPassword:(NSString*)myPassword;
/**
disconnect user from Xmpp server
*/
- (void) disconnect;
/**
changes the presence to online
*/
- (void) goOnline;
/**
changes the presence to offline
*/
- (void) goOffline;
/**
Register new user to xmpp server
#param userName new user name
#param _password new password
#param EmailId new email id
*/
- (void)registerStudentWithUserName:(NSString *)userName withPassword:(NSString *)_password withEmailId:(NSString *)EmailId;
/**
send message to other user with content
#param toAdress destination address
#param content content of message
*/
- (BOOL)sendMessageTo:(NSString*)toAdress withContents:(NSString*)content;
/**
This method is used for sending subscribe invitation to user
#param userID destination address
*/
- (void) sendSubscribeMessageToUser:(NSString*)userID;
/**
This method is used for setting substate of presence
#param subState substate of user
*/
- (void) presenceWithStubState:(NSString*)subState;
/**
This method is used to create new room
#param ChatRoomJID New room name
*/
- (void) setUpRoom:(NSString *)ChatRoomJID;
/**
This method is used to destroyRoom
*/
- (void) destroyCreatedRoom;
/**
This method is used to send message to group
*/
- (BOOL)sendGroupMessageWithBody:(NSString*)_body;
- (void) requestAllMesssage;
#end
/**
Set of methods to be implemented to act as a restaurant patron
*/
#protocol MessageManagerDelegate <NSObject>
/**
Methods to be get state of stream
*/
- (void) didGetStreamState:(BOOL)state;
/**
Methods to be get state of Authentication
*/
- (void) didGetAuthenticationState:(BOOL)state;
/**
Methods to be get state of registration
*/
- (void) didGetRegistrationState:(BOOL)state WithErrorMessage:(NSString*)errorMessage;
/**
Methods to get recieved message
*/
- (void) didReceiveMessageWithBody:(NSString *) body;
/**
Methods to get presence of other user
*/
- (void) didRecievePresence:(NSString*)state withUserName:(NSString*)userName WithSubState:(NSString*)subState;
/**
Methods to get event of user joined room
*/
- (void) didCreatedOrJoinedRoomWithCreatedRoomName:(NSString*)_roomName;
- (void) didGetUserJoinedToRoomORLeaveRoomWithName:(NSString*)_userName WithPresence:(NSString*)presence;
#end
MessageManager.m
#import "MessageManager.h"
#import "DDLog.h"
#import "DDTTYLogger.h"
#import <CFNetwork/CFNetwork.h>
#if DEBUG
static const int ddLogLevel = LOG_LEVEL_VERBOSE;
#else
static const int ddLogLevel = LOG_LEVEL_INFO;
#endif
#define kBaseXMPPURL #"XXXXXXXXXX"
#interface MessageManager()
#end
static MessageManager *sharedMessageHandler = nil;
#implementation MessageManager
#synthesize xmppStream;
#synthesize xmppRoster;
#synthesize xmppRosterStorage;
#synthesize xmppRoom;
#pragma mark - self class Delegate
- (void) setdelegate:(id)delegate
{
_delgate= delegate;
}
- (id) delegate
{
return _delgate;
}
#pragma mark - custom Functions
+ (MessageManager *) sharedMessageHandler
{
if (sharedMessageHandler == nil)
{
sharedMessageHandler = [[super allocWithZone:NULL] init];
}
return sharedMessageHandler;
}
+ (id)allocWithZone:(NSZone *)zone {
return [self sharedMessageHandler];
}
- (id)copyWithZone:(NSZone *)zone {
return self;
}
#pragma mark - connection setup Functions
/**
This fuction is used to setup XMPP Stream
*/
- (void)setupStream
{
// 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];
[xmppStream setHostName:kBaseXMPPURL];
[xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];
}
/**
This fuction is used to Connect XMPP With userId and Password
*/
- (BOOL)connectWithUserId:(NSString*)jabberID withPassword:(NSString*)myPassword
{
[self setupStream];
isRegistering=NO;
if (![xmppStream isDisconnected]) {
return YES;
}
if (jabberID == nil || myPassword == nil) {
return NO;
}
[xmppStream setMyJID:[XMPPJID jidWithString:jabberID]];
password = myPassword;
NSError *error = nil;
if (![xmppStream connectWithTimeout:XMPPStreamTimeoutNone error:&error])
{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Error"
message:[NSString stringWithFormat:#"Can't connect to server %#", [error localizedDescription]]
delegate:nil
cancelButtonTitle:#"Ok"
otherButtonTitles:nil];
[alertView show];
return NO;
}
return YES;
}
- (void) authenticateUserWIthUSerName:(NSString*)userName withPassword:(NSString*)myPassword
{
if ([xmppStream isConnected])
{
NSError*error =nil;
[xmppStream setMyJID:[XMPPJID jidWithString:userName]];
[xmppStream authenticateWithPassword:myPassword error:&error];
}
else
{
[self connectWithUserId:userName withPassword:myPassword];
}
}
#pragma mark ---Delegate of Connect
/**
This fuction is called when stream is connected
*/
- (void)xmppStreamDidConnect:(XMPPStream *)sender {
isOpen = YES;
NSError *error = nil;
NSLog(#"Stream Connected");
if (!isRegistering)
{
if([[self delegate] respondsToSelector:#selector(didGetStreamState:)])
{
[[self delegate]didGetStreamState:YES];
}
[xmppStream authenticateWithPassword:password error:&error];
}
else
{
[xmppStream registerWithPassword:password error:&error];
}
}
/**
This fuction is called when User is Authenticated
*/
- (void)xmppStreamDidAuthenticate:(XMPPStream *)sender {
[self goOnline];
NSLog(#"Stream Authenticated");
if([[self delegate] respondsToSelector:#selector(didGetAuthenticationState:)])
{
[[self delegate]didGetAuthenticationState:YES];
}
// if ([xmppStream isAuthenticated]) {
// NSLog(#"authenticated");
// xmppvCardStorage = [[XMPPvCardCoreDataStorage alloc] initWithInMemoryStore];
// xmppvCardTempModule = [[XMPPvCardTempModule alloc] initWithvCardStorage:xmppvCardStorage];
// [xmppvCardTempModule activate:[self xmppStream]];
// [xmppvCardTempModule addDelegate:self delegateQueue:dispatch_get_main_queue()];
// [xmppvCardTempModule fetchvCardTempForJID:[sender myJID] ignoreStorage:YES];
// }
}
- (void)xmppvCardTempModule:(XMPPvCardTempModule *)vCardTempModule didReceivevCardTemp:(XMPPvCardTemp *)vCardTemp forJID:(XMPPJID *)jid{
NSLog(#"Delegate is called");
XMPPvCardTemp *vCard = [xmppvCardStorage vCardTempForJID:jid xmppStream:xmppStream];
NSLog(#"Stored card: %#",vCard);
NSLog(#"%#", vCard.description);
NSLog(#"%#", vCard.name);
NSLog(#"%#", vCard.emailAddresses);
NSLog(#"%#", vCard.formattedName);
NSLog(#"%#", vCard.givenName);
NSLog(#"%#", vCard.middleName);
}
/**
This fuction is called when User is not Authenticated
*/
- (void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(NSXMLElement *)error
{
NSLog(#"Not Authenticated");
if([[self delegate] respondsToSelector:#selector(didGetAuthenticationState:)])
{
[[self delegate]didGetAuthenticationState:NO];
}
}
#pragma mark - Stream disconnection
/**
This fuction is used to disconnet user
*/
- (void)disconnect
{
[self goOffline];
[xmppStream disconnect];
}
#pragma mark ---Delegate of disconnect
/**
This fuction is called when stream is disConnected
*/
- (void)xmppStreamDidDisconnect:(XMPPStream *)sender withError:(NSError *)error
{
NSLog(#"Stream Disconnected");
if([[self delegate] respondsToSelector:#selector(didGetStreamState:)])
{
[[self delegate]didGetStreamState:NO];
}
}
#pragma mark - setting presence
/**
This fuction is used change the presence to online
*/
- (void)goOnline
{
XMPPPresence *presence = [XMPPPresence presence];
[xmppStream sendElement:presence];
}
/**
This fuction is used change the presence to Ofline
*/
- (void)goOffline
{
XMPPPresence *presence = [XMPPPresence presenceWithType:#"unavailable"];
[xmppStream sendElement:presence];
}
/**
This fuction is used change the presence substate
*/
- (void) presenceWithStubState:(NSString*)subState
{
XMPPPresence *presence = [XMPPPresence presence];// type="available" is implicit
NSXMLElement *status = [NSXMLElement elementWithName:#"status"];
[status setStringValue:subState];
[presence addChild:status];
[xmppStream sendElement:presence];
}
/**
This fuction is called when other user state is changed
*/
- (void)xmppStream:(XMPPStream *)sender didReceivePresence:(XMPPPresence *)presence
{
DDLogVerbose(#"%#: %# - %#", THIS_FILE, THIS_METHOD, [presence fromStr]);
NSString *presenceType = [presence type]; // online/offline
NSString *myUsername = [[sender myJID] user];
NSString *presenceFromUser = [[presence from] user];
NSString* presenceState= [presence status];
NSLog(#"%# is %# state %#",presenceFromUser,presenceType,presenceState);
if (![presenceFromUser isEqualToString:myUsername])
{
if ([presenceType isEqualToString:#"available"])
{
if([[self delegate] respondsToSelector:#selector(didRecievePresence:withUserName:WithSubState:)])
{
[[self delegate] didRecievePresence:presenceType withUserName:presenceFromUser WithSubState:presenceState];
}
}
else if ([presenceType isEqualToString:#"unavailable"]) {
if([[self delegate] respondsToSelector:#selector(didRecievePresence:withUserName:WithSubState:)])
{
[[self delegate] didRecievePresence:presenceType withUserName:presenceFromUser WithSubState:presenceState];
}
}
else if ([presenceType isEqualToString:#"subscribe"])
{
[xmppRoster subscribePresenceToUser:[presence from]];
[self goOnline];
}
else if ([presenceType isEqualToString:#"subscribed"])
{
[xmppRoster subscribePresenceToUser:[presence from]];
}
}
if (xmppRoom)
{
[xmppRoom fetchMembersList];
}
}
#pragma mark - subscription
- (void) sendSubscribeMessageToUser:(NSString*)userID
{
XMPPJID* jbid= [XMPPJID jidWithString:userID];
XMPPPresence *presence = [XMPPPresence presenceWithType:#"subscribe" to:jbid];
[xmppStream sendElement:presence];
}
#pragma mark - XMPP delegates
/**
This fuction is called when new IQ is received
*/
- (BOOL)xmppStream:(XMPPStream *)sender didReceiveIQ:(XMPPIQ *)iq {
return NO;
}
#pragma mark - RegistrationFunction
/**
This fuction is user to retister new user
if stream is connected the it will directly call registeration function
otherwise it will connect stream and then call registeration process
*/
- (void)registerStudentWithUserName:(NSString *)userName withPassword:(NSString *)_password withEmailId:(NSString *)EmailId
{
if (xmppStream==nil)
{
[self setupStream];
}
[xmppStream setMyJID:[XMPPJID jidWithString:userName]];
NSLog(#"Attempting registration for username %#",xmppStream.myJID.bare);
password=_password;
NSError *error = nil;
BOOL success;
if(![ xmppStream isConnected])
{
success = [[self xmppStream] connectWithTimeout:XMPPStreamTimeoutNone error:&error];
isRegistering=YES;
}
else
{
success = [[self xmppStream] registerWithPassword:_password error:&error];
}
if (success)
{
NSLog(#"succeed ");
isRegistering=YES;
}
else
{
if([[self delegate] respondsToSelector:#selector(didGetRegistrationState:WithErrorMessage:)])
{
[[self delegate]didGetRegistrationState:YES WithErrorMessage:#"Stream not connected"];
}
}
}
#pragma mark ---delegates of registrtaion
/**
This fuction is called when new user is registered
*/
- (void)xmppStreamDidRegister:(XMPPStream *)sender{
if([[self delegate] respondsToSelector:#selector(didGetRegistrationState:WithErrorMessage:)])
{
[[self delegate]didGetRegistrationState:YES WithErrorMessage:#"Registration with XMPP Successful!"];
}
}
/**
This fuction is called when registeration process failed
*/
- (void)xmppStream:(XMPPStream *)sender didNotRegister:(NSXMLElement *)error{
// DDXMLElement *errorXML = [error elementForName:#"error"];
// NSString *errorCode = [[errorXML attributeForName:#"code"] stringValue];
// NSString *regError = [NSString stringWithFormat:#"ERROR :- %#",error.description];
//
//
// if([errorCode isEqualToString:#"409"])
// {
// regError=#"Username Already Exists!";
// }
// else
// {
// regError= #"Server not connected";
// }
if([[self delegate] respondsToSelector:#selector(didGetRegistrationState:WithErrorMessage:)])
{
[[self delegate]didGetRegistrationState:NO WithErrorMessage:#"Username Already Exists!"];
}
}
#pragma mark - send and recieve message
/**
This fuction is used to send message to other user with contents of body
*/
//-(void)sendMessageTo:(NSString*)toAdress withContents:(NSString*)messageStr
//{
//
//
// if([messageStr length]> 0)
// {
//
// NSXMLElement *body = [NSXMLElement elementWithName:#"body"];
// [body setStringValue:messageStr];
//
// NSXMLElement *message = [NSXMLElement elementWithName:#"message"];
// [message addAttributeWithName:#"type" stringValue:#"chat"];
// [message addAttributeWithName:#"to" stringValue:toAdress];
// [message addChild:body];
//
// [self.xmppStream sendElement:message];
// }
//}
- (BOOL)sendMessageTo:(NSString*)toAdress withContents:(NSString*)content
{
if([content length]> 0)
{
NSXMLElement *body = [NSXMLElement elementWithName:#"body"];
[body setStringValue:content];
NSXMLElement *message = [NSXMLElement elementWithName:#"message"];
[message addAttributeWithName:#"type" stringValue:#"chat"];
[message addAttributeWithName:#"to" stringValue:toAdress];
[message addChild:body];
[self.xmppStream sendElement:message];
}
return YES;
}
#pragma mark recieve message
/**
This fuction is called when new message arrived
*/
- (void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message
{
DDLogVerbose(#"%#: %#", THIS_FILE, THIS_METHOD);
// A simple example of inbound message handling.
if ([message body])
{
NSString *body = [[message elementForName:#"body"] stringValue];
if([[self delegate] respondsToSelector:#selector(didReceiveMessageWithBody:)])
{
[[self delegate] didReceiveMessageWithBody:body];
}
}
}
#pragma mark - create new room
/**
This fuction is used to setup room with roomId
*/
-(void)setUpRoom:(NSString *)ChatRoomJID
{
if (!ChatRoomJID)
{
return;
}
// Configure xmppRoom
XMPPRoomMemoryStorage *roomMemoryStorage = [[XMPPRoomMemoryStorage alloc] init];
XMPPJID *roomJID = [XMPPJID jidWithString:ChatRoomJID];
xmppRoom = [[XMPPRoom alloc] initWithRoomStorage:roomMemoryStorage jid:roomJID dispatchQueue:dispatch_get_main_queue()];
[xmppRoom activate:xmppStream];
[xmppRoom addDelegate:self delegateQueue:dispatch_get_main_queue()];
NSXMLElement *history = [NSXMLElement elementWithName:#"history"];
[history addAttributeWithName:#" maxchars" stringValue:#"0"];
[xmppRoom joinRoomUsingNickname:xmppStream.myJID.user
history:history
password:nil];
[self performSelector:#selector(ConfigureNewRoom:) withObject:nil afterDelay:4];
}
/**
This fuction is used configure new
*/
- (void)ConfigureNewRoom:(id)sender
{
[xmppRoom configureRoomUsingOptions:nil];
[xmppRoom fetchConfigurationForm];
[xmppRoom fetchBanList];
[xmppRoom fetchMembersList];
[xmppRoom fetchModeratorsList];
}
/**
This fuction is called when new room is created
*/
- (void)xmppRoomDidCreate:(XMPPRoom *)sender
{
DDLogInfo(#"%#: %#", THIS_FILE, THIS_METHOD);
// I am inviting friends after room is created
}
/**
This fuction is called when user joined room
*/
- (void)xmppRoomDidJoin:(XMPPRoom *)sender
{
[sender fetchMembersList];
[sender fetchConfigurationForm];
[self requestAllMesssage];
DDLogInfo(#"%#: %#", THIS_FILE, THIS_METHOD);
if([[self delegate] respondsToSelector:#selector(didCreatedOrJoinedRoomWithCreatedRoomName:)])
{
[[self delegate] didCreatedOrJoinedRoomWithCreatedRoomName:sender.myRoomJID.bare];
}
}
- (void)xmppRoom:(XMPPRoom *)sender didFetchMembersList:(NSArray *)items
{
}
- (void)xmppRoom:(XMPPRoom *)sender occupantDidJoin:(XMPPJID *)occupantJID withPresence:(XMPPPresence *)presence
{
if([[self delegate] respondsToSelector:#selector(didGetUserJoinedToRoomORLeaveRoomWithName:WithPresence:)])
{
// id details =occupantJID;
// NSString* string = (NSString*)details;
[[self delegate] didGetUserJoinedToRoomORLeaveRoomWithName:[occupantJID resource] WithPresence:[presence type]];
}
}
- (void)xmppRoom:(XMPPRoom *)sender occupantDidLeave:(XMPPJID *)occupantJID withPresence:(XMPPPresence *)presence
{
if([[self delegate] respondsToSelector:#selector(didGetUserJoinedToRoomORLeaveRoomWithName:WithPresence:)])
{
[[self delegate] didGetUserJoinedToRoomORLeaveRoomWithName:[occupantJID resource] WithPresence:[presence type]];
}
}
- (void)xmppRoom:(XMPPRoom *)sender occupantDidUpdate:(XMPPJID *)occupantJID withPresence:(XMPPPresence *)presence
{
if([[self delegate] respondsToSelector:#selector(didGetUserJoinedToRoomORLeaveRoomWithName:WithPresence:)])
{
[[self delegate] didGetUserJoinedToRoomORLeaveRoomWithName:[occupantJID resource] WithPresence:[presence type]];
}
}
- (void) requestAllMesssage
{
// <presence
// from='hag66#shakespeare.lit/pda'
// id='n13mt3l'
// to='coven#chat.shakespeare.lit/thirdwitch'>
// <x xmlns='http://jabber.org/protocol/muc'>
// <history since='1970-01-01T00:00:00Z'/>
// </x>
// </presence>
//
// NSXMLElement *iQ = [NSXMLElement elementWithName:#"presence"];
// [iQ addAttributeWithName:#"type" stringValue:#"get"];
// [iQ addAttributeWithName:#"id" stringValue:#"n13mt3l"];
//
// NSXMLElement *retrieve = [NSXMLElement elementWithName:#"retrieve"];
// [retrieve addAttributeWithName:#"xmlns" stringValue:#"urn:xmpp:archive"];
// [retrieve addAttributeWithName:#"history since" stringValue:#"1970-01-01T00:00:00Z"];
//
// NSXMLElement *set = [NSXMLElement elementWithName:#"set"];
// [set addAttributeWithName:#"xmlns" stringValue:#"http://jabber.org/protocol/muc"];
//
// [retrieve addChild:set];
// [iQ addChild:retrieve];
//
// [xmppStream sendElement:iQ];
}
- (void)xmppRoom:(XMPPRoom *)sender didFetchConfigurationForm:(NSXMLElement *)configForm
{
DDLogInfo(#"%#: %#", THIS_FILE, THIS_METHOD);
NSXMLElement *newConfig = [configForm copy];
NSArray *fields = [newConfig elementsForName:#"field"];
for (NSXMLElement *field in fields)
{
NSString *var = [field attributeStringValueForName:#"var"];
// Make Room Persistent
if ([var isEqualToString:#"muc#roomconfig_persistentroom"])
{
[field removeChildAtIndex:0];
[field addChild:[NSXMLElement elementWithName:#"value" stringValue:#"1"]];
}
if ([var isEqualToString:#"roomconfig_enablelogging"])
{
[field removeChildAtIndex:0];
[field addChild:[NSXMLElement elementWithName:#"value" stringValue:#"1"]];
}
if ([var isEqualToString:#"muc#roomconfig_maxusers"])
{
[field removeChildAtIndex:0];
[field addChild:[NSXMLElement elementWithName:#"value" stringValue:#"100"]];
}
}
// [sender configureRoomUsingOptions:newConfig];
}
/**
This fuction is used to destroy created room
*/
- (void) destroyCreatedRoom
{
[xmppRoom destroyRoom];
}
- (BOOL)sendGroupMessageWithBody:(NSString*)_body
{
[xmppRoom sendMessageWithBody:_body];
return YES;
}
#end
Generally when the server is closing the connection, you get this error.
There are two reasons when the server closes the connection:
You are not sending regular pings if the client idle.
You are logging in from some different client with the same credentials,
and in the server settings have the setting:
Always kick - If there is a resource conflict, immediately kick the other
resource. in Server>server settings>resource policy.
Please check the server settings.
use this for server setting http://docs.ejabberd.im/admin/guide/installation/
And also check for App Transport Security Setting in your project. if it is not added then add the below code to info.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
</plist>
The actual problem was that I was changing subject of the Room, just after creating the room. On removing the code for changing the subject of the room, it started working fine. Here's the code I was using -
-(XMPPRoom *)createRoom:(XMPPJID *)roomJid withSubject:(NSString*)subject{
XMPPRoomMemoryStorage *roomStorage = [[XMPPRoomMemoryStorage alloc]init];
XMPPRoom *xmppRoom = [[XMPPRoom alloc] initWithRoomStorage:roomStorage
jid:roomJid
dispatchQueue:dispatch_get_main_queue()];
[xmppRoom activate:self.xmppStream];
[xmppRoom addDelegate:self delegateQueue:dispatch_get_main_queue()];
// if(subject){
// [xmppRoom changeRoomSubject:subject];
// }
return xmppRoom;
}
-(GroupCore *)createRoomAndGroup:(XMPPJID *)roomJid withSubject:(NSString*)subject{
XMPPRoom* xmppRoom = [self createRoom:roomJid withSubject:subject];
GroupCore* group = [appDelegate.coreDataController addGroup:xmppRoom withOptions:CreateOnly];
if(!xmppRoom.roomSubject){
group.name = subject;
}
//[appDelegate saveContext];
// dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
// [group joinRoom];
// });
return group;
}
Related
I am working on Twilio programmable voice SDK. I integrated it with the help of documentation and GitHub SDK. I can make a call from iOS to any number. It works fine.
The issue is on receiving a call on Twilio number. I did everything that was mentioned in Twilio installation documentation and GitHub SDK, but its not working.
here is the code:
#import PushKit;
#import CallKit;
#import TwilioVoice;
#import UserNotifications;
static NSString * kAccessToken = #"";
NSString * phoneNumber = #"";
NSString * newToken = #"";
static NSString *const kTwimlParamTo = #"to";
static NSInteger const kRegistrationTTLInDays = 365;
NSString * const kCachedDeviceToken = #"CachedDeviceToken";
NSString * const kCachedBindingTime = #"CachedBindingTime";
#interface RCTCallPackageModule () <TVONotificationDelegate, TVOCallDelegate, CXProviderDelegate, UITextFieldDelegate, AVAudioPlayerDelegate , PushKitEventDelegate, PKPushRegistryDelegate>
#property (nonatomic, weak) id<PushKitEventDelegate> pushKitEventDelegate;
#property (nonatomic, strong) void(^incomingPushCompletionCallback)(void);
#property (nonatomic, strong) void(^callKitCompletionCallback)(BOOL);
#property (nonatomic, strong) TVODefaultAudioDevice *audioDevice;
#property (nonatomic, strong) NSMutableDictionary *activeCallInvites;
#property (nonatomic, strong) NSMutableDictionary *activeCalls;
// activeCall represents the last connected call
#property (nonatomic, strong) TVOCall *activeCall;
#property (nonatomic, strong) CXProvider *callKitProvider;
#property (nonatomic, strong) CXCallController *callKitCallController;
#property (nonatomic, assign) BOOL userInitiatedDisconnect;
#property (nonatomic, assign) BOOL playCustomRingback;
#property (nonatomic, strong) AVAudioPlayer *ringtonePlayer;
#end
//#import <React/RCTLog.h>
#implementation RCTCallPackageModule
-(NSString *)fetchAccessToken {
NSString *accessToken = [NSString stringWithContentsOfURL:[NSURL URLWithString:#"http://54.172.240.200:4000/accessToken"]
encoding:NSUTF8StringEncoding
error:nil];
return accessToken;
}
-(void) mainIntializerFunction {
self.pushKitEventDelegate = self;
self.callKitCallController = [[CXCallController alloc] init];
// fetchData();
/* Please note that the designated initializer `[CXProviderConfiguration initWithLocalizedName:]` has been deprecated on iOS 14. */
CXProviderConfiguration *configuration = [[CXProviderConfiguration alloc] initWithLocalizedName:#"Voice Quickstart"];
configuration.maximumCallGroups = 1;
configuration.maximumCallsPerCallGroup = 1;
self.callKitProvider = [[CXProvider alloc] initWithConfiguration:configuration];
[self.callKitProvider setDelegate:self queue:nil];
self.audioDevice = [TVODefaultAudioDevice audioDevice];
TwilioVoiceSDK.audioDevice = self.audioDevice;
self.activeCallInvites = [NSMutableDictionary dictionary];
self.activeCalls = [NSMutableDictionary dictionary];
self.playCustomRingback = NO;
[self MainFunctionToCall]; }
- (void)dealloc {
if (self.callKitProvider) {
[self.callKitProvider invalidate];
} }
-(void) MainFunctionToCall {
if (self.activeCall != nil) {
self.userInitiatedDisconnect = YES;
[self performEndCallActionWithUUID:self.activeCall.uuid];
} else {
NSUUID *uuid = [NSUUID UUID];
NSString *handle = #"Voice Bot";
[self checkRecordPermission:^(BOOL permissionGranted) {
[center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert + UNAuthorizationOptionSound)
completionHandler:^(BOOL granted, NSError * _Nullable error) {
// Enable or disable features based on authorization.
}];
// [[UIApplication sharedApplication] registerForRemoteNotifications];
[self performStartCallActionWithUUID:uuid handle:handle];
}];
}
}
// performEndCallActiveUUID
- (void)performEndCallActionWithUUID:(NSUUID *)uuid {
CXEndCallAction *endCallAction = [[CXEndCallAction alloc] initWithCallUUID:uuid];
CXTransaction *transaction = [[CXTransaction alloc] initWithAction:endCallAction];
[self.callKitCallController requestTransaction:transaction completion:^(NSError *error) {
if (error) {
NSLog(#"EndCallAction transaction request failed: %#", [error localizedDescription]);
}
else {
}
}];
}
// checkRecordPermission
- (void)checkRecordPermission:(void(^)(BOOL permissionGranted))completion {
AVAudioSessionRecordPermission permissionStatus = [[AVAudioSession sharedInstance] recordPermission];
switch (permissionStatus) {
case AVAudioSessionRecordPermissionGranted:
// Record permission already granted.
completion(YES);
break;
case AVAudioSessionRecordPermissionDenied:
// Record permission denied.
completion(NO);
break;
case AVAudioSessionRecordPermissionUndetermined:
{
[[AVAudioSession sharedInstance] requestRecordPermission:^(BOOL granted) {
completion(granted);
}];
break;
}
default:
completion(NO);
break;
}
}
#pragma mark - CallKit Actions
- (void)performStartCallActionWithUUID:(NSUUID *)uuid handle:(NSString *)handle {
if (uuid == nil || handle == nil) {
return;
}
CXHandle *callHandle = [[CXHandle alloc] initWithType:CXHandleTypeGeneric value:handle];
CXStartCallAction *startCallAction = [[CXStartCallAction alloc] initWithCallUUID:uuid handle:callHandle];
CXTransaction *transaction = [[CXTransaction alloc] initWithAction:startCallAction];
[self.callKitCallController requestTransaction:transaction completion:^(NSError *error) {
if (error) {
NSLog(#"StartCallAction transaction request failed: %#", [error localizedDescription]);
} else {
CXCallUpdate *callUpdate = [[CXCallUpdate alloc] init];
callUpdate.remoteHandle = callHandle;
callUpdate.supportsDTMF = YES;
callUpdate.supportsHolding = YES;
callUpdate.supportsGrouping = NO;
callUpdate.supportsUngrouping = NO;
callUpdate.hasVideo = NO;
[self.callKitProvider reportCallWithUUID:uuid updated:callUpdate];
}
}];
}
#pragma mark - PushKitEventDelegate
- (void)credentialsUpdated:(PKPushCredentials *)credentials {
NSData *cachedDeviceToken = [[NSUserDefaults standardUserDefaults] objectForKey:kCachedDeviceToken];
if ([self registrationRequired] || ![cachedDeviceToken isEqualToData:credentials.token]) {
cachedDeviceToken = credentials.token;
[TwilioVoiceSDK registerWithAccessToken:kAccessToken
deviceToken:cachedDeviceToken
completion:^(NSError *error) {
if (error) {
} else {
// Save the device token after successfully registered.
[[NSUserDefaults standardUserDefaults] setObject:cachedDeviceToken forKey:kCachedDeviceToken];
[[NSUserDefaults standardUserDefaults] setObject:[NSDate date] forKey:kCachedBindingTime];
}
}];
}
}
- (BOOL)registrationRequired {
BOOL registrationRequired = YES;
NSDate *lastBindingCreated = [[NSUserDefaults standardUserDefaults] objectForKey:kCachedBindingTime];
if (lastBindingCreated) {
NSDateComponents *dayComponent = [[NSDateComponents alloc] init];
// Register upon half of the TTL
dayComponent.day = kRegistrationTTLInDays / 2;
NSDate *bindingExpirationDate = [[NSCalendar currentCalendar] dateByAddingComponents:dayComponent toDate:lastBindingCreated options:0];
NSDate *currentDate = [NSDate date];
if ([bindingExpirationDate compare:currentDate] == NSOrderedDescending) {
registrationRequired = NO;
}
}
return registrationRequired;
}
- (void)credentialsInvalidated {
NSData *cachedDeviceToken = [[NSUserDefaults standardUserDefaults] objectForKey:kCachedDeviceToken];
if ([cachedDeviceToken length] > 0) {
[TwilioVoiceSDK unregisterWithAccessToken:kAccessToken
deviceToken:cachedDeviceToken
completion:^(NSError *error) {
if (error) {
} else {
}
}];
}
[[NSUserDefaults standardUserDefaults] removeObjectForKey:kCachedDeviceToken];
// Remove the cached binding as credentials are invalidated
[[NSUserDefaults standardUserDefaults] removeObjectForKey:kCachedBindingTime];
}
-(void)incomingPushReceived:(PKPushPayload *)payload withCompletionHandler:(void (^)(void))completion {
// The Voice SDK will use main queue to invoke `cancelledCallInviteReceived:error` when delegate queue is not passed
if (![TwilioVoiceSDK handleNotification:payload.dictionaryPayload delegate:self delegateQueue:nil]) {
}
if (completion) {
if ([[NSProcessInfo processInfo] operatingSystemVersion].majorVersion < 13) {
// Save for later when the notification is properly handled.
self.incomingPushCompletionCallback = completion;
} else {
completion();
}
}
}
- (void)incomingPushHandled {
if (self.incomingPushCompletionCallback) {
self.incomingPushCompletionCallback();
self.incomingPushCompletionCallback = nil;
}
}
#pragma mark - TVONotificationDelegate
- (void)callInviteReceived:(TVOCallInvite *)callInvite {
[[NSUserDefaults standardUserDefaults] setObject:[NSDate date] forKey:kCachedBindingTime];
if (callInvite.callerInfo.verified != nil && [callInvite.callerInfo.verified boolValue]) {
}
NSString *from = #"Voice Bot";
if (callInvite.from) {
from = [callInvite.from stringByReplacingOccurrencesOfString:#"client:" withString:#""];
}
// Always report to CallKit
[self reportIncomingCallFrom:from withUUID:callInvite.uuid];
self.activeCallInvites[[callInvite.uuid UUIDString]] = callInvite;
if ([[NSProcessInfo processInfo] operatingSystemVersion].majorVersion < 13) {
[self incomingPushHandled];
}
}
- (void)cancelledCallInviteReceived:(TVOCancelledCallInvite *)cancelledCallInvite error:(NSError *)error {
TVOCallInvite *callInvite;
for (NSString *uuid in self.activeCallInvites) {
TVOCallInvite *activeCallInvite = [self.activeCallInvites objectForKey:uuid];
if ([cancelledCallInvite.callSid isEqualToString:activeCallInvite.callSid]) {
callInvite = activeCallInvite;
break;
}
}
if (callInvite) {
[self performEndCallActionWithUUID:callInvite.uuid];
[self.activeCallInvites removeObjectForKey:callInvite.uuid.UUIDString];
}
}
- (void)callDidStartRinging:(TVOCall *)call {
NSLog(#"callDidStartRinging:");
if (self.playCustomRingback) {
[self playRingback];
}
// [self.placeCallButton setTitle:#"Ringing" forState:UIControlStateNormal];
}
- (void)callDidConnect:(TVOCall *)call {
NSLog(#"callDidConnect:");
if (self.playCustomRingback) {
[self stopRingback];
}
[self sendEventWithName:#"onSessionConnect" body:#"Connected"];
self.callKitCompletionCallback(YES);
}
- (void)call:(TVOCall *)call isReconnectingWithError:(NSError *)error {
NSLog(#"Call is reconnecting");
}
- (void)callDidReconnect:(TVOCall *)call {
NSLog(#"Call reconnected");
}
- (void)call:(TVOCall *)call didFailToConnectWithError:(NSError *)error {
NSLog(#"Call failed to connect: %#", error);
self.callKitCompletionCallback(NO);
[self.callKitProvider reportCallWithUUID:call.uuid endedAtDate:[NSDate date] reason:CXCallEndedReasonFailed];
[self sendEventWithName:#"onSessionConnect" body:#"Failure"];
[self callDisconnected:call];
}
- (void)call:(TVOCall *)call didDisconnectWithError:(NSError *)error {
if (error) {
NSLog(#"Call failed: %#", error);
} else {
NSLog(#"Call disconnected");
}
if (!self.userInitiatedDisconnect) {
CXCallEndedReason reason = CXCallEndedReasonRemoteEnded;
if (error) {
reason = CXCallEndedReasonFailed;
}
[self.callKitProvider reportCallWithUUID:call.uuid endedAtDate:[NSDate date] reason:reason];
}
[self sendEventWithName:#"onSessionConnect" body:#"Disconnected"];
[self callDisconnected:call];
}
- (void)callDisconnected:(TVOCall *)call {
if ([call isEqual:self.activeCall]) {
self.activeCall = nil;
}
[self.activeCalls removeObjectForKey:call.uuid.UUIDString];
self.userInitiatedDisconnect = NO;
if (self.playCustomRingback) {
[self stopRingback];
}
}
- (void)call:(TVOCall *)call
didReceiveQualityWarnings:(NSSet<NSNumber *> *)currentWarnings
previousWarnings:(NSSet<NSNumber *> *)previousWarnings {
/**
* currentWarnings: existing quality warnings that have not been cleared yet
* previousWarnings: last set of warnings prior to receiving this callback
*
* Example:
* - currentWarnings: { A, B }
* - previousWarnings: { B, C }
* - intersection: { B }
*
* Newly raised warnings = currentWarnings - intersection = { A }
* Newly cleared warnings = previousWarnings - intersection = { C }
*/
NSMutableSet *warningIntersetction = [currentWarnings mutableCopy];
[warningIntersetction intersectSet:previousWarnings];
NSMutableSet *newWarnings = [currentWarnings mutableCopy];
[newWarnings minusSet:warningIntersetction];
if ([newWarnings count] > 0) {
[self qualityWarningUpdatePopup:newWarnings isCleared:NO];
}
NSMutableSet *clearedWarnings = [previousWarnings mutableCopy];
[clearedWarnings minusSet:warningIntersetction];
if ([clearedWarnings count] > 0) {
[self qualityWarningUpdatePopup:clearedWarnings isCleared:YES];
}
}
- (void)qualityWarningUpdatePopup:(NSSet *)warnings isCleared:(BOOL)cleared {
NSString *popupMessage = (cleared)? #"Warnings cleared:" : #"Warnings detected:";
for (NSNumber *warning in warnings) {
NSString *warningName = [self warningString:[warning unsignedIntValue]];
popupMessage = [popupMessage stringByAppendingString:[NSString stringWithFormat:#" %#", warningName]];
}
[UIView animateWithDuration:1.0f
animations:^{
} completion:^(BOOL finished) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[UIView animateWithDuration:1.0 animations:^{
} completion:^(BOOL finished) {
}];
});
}];
}
- (NSString *)warningString:(TVOCallQualityWarning)qualityWarning {
switch (qualityWarning) {
case TVOCallQualityWarningHighRtt:
return #"high-rtt";
break;
case TVOCallQualityWarningHighJitter:
return #"high-jitter";
break;
case TVOCallQualityWarningHighPacketsLostFraction:
return #"high-packets-lost-fraction";
break;
case TVOCallQualityWarningLowMos:
return #"low-mos";
break;
case TVOCallQualityWarningConstantAudioInputLevel:
return #"constant-audio-input-level";
break;
default:
return #"Unknown warning";
break;
}
}
-(void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(NSString *)type {
NSLog(#"pushRegistry:didReceiveIncomingPushWithPayload:forType:");
if ([type isEqualToString:PKPushTypeVoIP]) {
// The Voice SDK will use main queue to invoke `cancelledCallInviteReceived:error` when delegate queue is not passed
if (![TwilioVoiceSDK handleNotification:payload.dictionaryPayload delegate:self delegateQueue:nil]) {
NSLog(#"This is not a valid Twilio Voice notification.");
}
else
{
}
}
}
/**
* This delegate method is available on iOS 11 and above. Call the completion handler once the
* notification payload is passed to the `TwilioVoice.handleNotification()` method.
*/
- (void)pushRegistry:(PKPushRegistry *)registry
didReceiveIncomingPushWithPayload:(PKPushPayload *)payload
forType:(PKPushType)type
withCompletionHandler:(void (^)(void))completion {
NSLog(#"pushRegistry:didReceiveIncomingPushWithPayload:forType:withCompletionHandler:");
// Save for later when the notification is properly handled.
self.incomingPushCompletionCallback = completion;
if ([type isEqualToString:PKPushTypeVoIP]) {
// The Voice SDK will use main queue to invoke `cancelledCallInviteReceived:error` when delegate queue is not passed
if (![TwilioVoiceSDK handleNotification:payload.dictionaryPayload delegate:self delegateQueue:nil]) {
NSLog(#"This is not a valid Twilio Voice notification.");
}
}
if ([[NSProcessInfo processInfo] operatingSystemVersion].majorVersion < 13) {
// Save for later when the notification is properly handled.
self.incomingPushCompletionCallback = completion;
} else {
/**
* The Voice SDK processes the call notification and returns the call invite synchronously. Report the incoming call to
* CallKit and fulfill the completion before exiting this callback method.
*/
completion();
}
}
#pragma mark - AVAudioSession
- (void)toggleAudioRoute:(BOOL)toSpeaker {
// The mode set by the Voice SDK is "VoiceChat" so the default audio route is the built-in receiver. Use port override to switch the route.
self.audioDevice.block = ^ {
// We will execute `kDefaultAVAudioSessionConfigurationBlock` first.
kTVODefaultAVAudioSessionConfigurationBlock();
// Overwrite the audio route
AVAudioSession *session = [AVAudioSession sharedInstance];
NSError *error = nil;
if (toSpeaker) {
if (![session overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:&error]) {
NSLog(#"Unable to reroute audio: %#", [error localizedDescription]);
}
} else {
if (![session overrideOutputAudioPort:AVAudioSessionPortOverrideNone error:&error]) {
NSLog(#"Unable to reroute audio: %#", [error localizedDescription]);
}
}
};
self.audioDevice.block();
}
#pragma mark - CXProviderDelegate
- (void)providerDidReset:(CXProvider *)provider {
NSLog(#"providerDidReset:");
self.audioDevice.enabled = NO;
}
- (void)providerDidBegin:(CXProvider *)provider {
NSLog(#"providerDidBegin:");
}
- (void)provider:(CXProvider *)provider didActivateAudioSession:(AVAudioSession *)audioSession {
NSLog(#"provider:didActivateAudioSession:");
self.audioDevice.enabled = YES;
}
- (void)provider:(CXProvider *)provider didDeactivateAudioSession:(AVAudioSession *)audioSession {
NSLog(#"provider:didDeactivateAudioSession:");
self.audioDevice.enabled = NO;
}
- (void)provider:(CXProvider *)provider timedOutPerformingAction:(CXAction *)action {
NSLog(#"provider:timedOutPerformingAction:");
}
- (void)provider:(CXProvider *)provider performStartCallAction:(CXStartCallAction *)action {
NSLog(#"provider:performStartCallAction:");
[self.callKitProvider reportOutgoingCallWithUUID:action.callUUID startedConnectingAtDate:[NSDate date]];
__weak typeof(self) weakSelf = self;
[self performVoiceCallWithUUID:action.callUUID client:nil completion:^(BOOL success) {
__strong typeof(self) strongSelf = weakSelf;
if (success) {
NSLog(#"performVoiceCallWithUUID successful");
[strongSelf.callKitProvider reportOutgoingCallWithUUID:action.callUUID connectedAtDate:[NSDate date]];
} else {
NSLog(#"performVoiceCallWithUUID failed");
}
[action fulfill];
}];
}
- (void)provider:(CXProvider *)provider performAnswerCallAction:(CXAnswerCallAction *)action {
NSLog(#"provider:performAnswerCallAction:");
[self performAnswerVoiceCallWithUUID:action.callUUID completion:^(BOOL success) {
if (success) {
NSLog(#"performAnswerVoiceCallWithUUID successful");
} else {
NSLog(#"performAnswerVoiceCallWithUUID failed");
}
}];
[action fulfill];
}
- (void)provider:(CXProvider *)provider performEndCallAction:(CXEndCallAction *)action {
NSLog(#"provider:performEndCallAction:");
TVOCallInvite *callInvite = self.activeCallInvites[action.callUUID.UUIDString];
TVOCall *call = self.activeCalls[action.callUUID.UUIDString];
if (callInvite) {
[callInvite reject];
[self.activeCallInvites removeObjectForKey:callInvite.uuid.UUIDString];
} else if (call) {
[call disconnect];
} else {
NSLog(#"Unknown UUID to perform end-call action with");
}
[action fulfill];
}
- (void)provider:(CXProvider *)provider performSetHeldCallAction:(CXSetHeldCallAction *)action {
TVOCall *call = self.activeCalls[action.callUUID.UUIDString];
if (call) {
[call setOnHold:action.isOnHold];
[action fulfill];
} else {
[action fail];
}
}
- (void)provider:(CXProvider *)provider performSetMutedCallAction:(CXSetMutedCallAction *)action {
TVOCall *call = self.activeCalls[action.callUUID.UUIDString];
if (call) {
[call setMuted:action.isMuted];
[action fulfill];
} else {
[action fail];
}
}
#pragma mark - CallKit Actions
- (void)reportIncomingCallFrom:(NSString *) from withUUID:(NSUUID *)uuid {
CXHandle *callHandle = [[CXHandle alloc] initWithType:CXHandleTypeGeneric value:from];
CXCallUpdate *callUpdate = [[CXCallUpdate alloc] init];
callUpdate.remoteHandle = callHandle;
callUpdate.supportsDTMF = YES;
callUpdate.supportsHolding = YES;
callUpdate.supportsGrouping = NO;
callUpdate.supportsUngrouping = NO;
callUpdate.hasVideo = NO;
[self.callKitProvider reportNewIncomingCallWithUUID:uuid update:callUpdate completion:^(NSError *error) {
if (!error) {
}
else {
}
}];
}
- (void)performVoiceCallWithUUID:(NSUUID *)uuid
client:(NSString *)client
completion:(void(^)(BOOL success))completionHandler {
__weak typeof(self) weakSelf = self;
TVOConnectOptions *connectOptions = [TVOConnectOptions optionsWithAccessToken:kAccessToken block:^(TVOConnectOptionsBuilder *builder) {
__strong typeof(self) strongSelf = weakSelf;
builder.params = #{kTwimlParamTo:phoneNumber};
builder.uuid = uuid;
}];
TVOCall *call = [TwilioVoiceSDK connectWithOptions:connectOptions delegate:self];
if (call) {
self.activeCall = call;
self.activeCalls[call.uuid.UUIDString] = call;
}
self.callKitCompletionCallback = completionHandler;
}
- (void)performAnswerVoiceCallWithUUID:(NSUUID *)uuid
completion:(void(^)(BOOL success))completionHandler {
TVOCallInvite *callInvite = self.activeCallInvites[uuid.UUIDString];
NSAssert(callInvite, #"No CallInvite matches the UUID");
TVOAcceptOptions *acceptOptions = [TVOAcceptOptions optionsWithCallInvite:callInvite block:^(TVOAcceptOptionsBuilder *builder) {
builder.uuid = callInvite.uuid;
}];
TVOCall *call = [callInvite acceptWithOptions:acceptOptions delegate:self];
if (!call) {
completionHandler(NO);
} else {
self.callKitCompletionCallback = completionHandler;
self.activeCall = call;
self.activeCalls[call.uuid.UUIDString] = call;
}
[self.activeCallInvites removeObjectForKey:callInvite.uuid.UUIDString];
if ([[NSProcessInfo processInfo] operatingSystemVersion].majorVersion < 13) {
[self incomingPushHandled];
}
}
#pragma mark - Ringtone
- (void)playRingback {
NSString *ringtonePath = [[NSBundle mainBundle] pathForResource:#"ringtone" ofType:#"wav"];
if ([ringtonePath length] <= 0) {
return;
}
NSError *error;
self.ringtonePlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL URLWithString:ringtonePath] error:&error];
if (error != nil) {
} else {
self.ringtonePlayer.delegate = self;
self.ringtonePlayer.numberOfLoops = -1;
self.ringtonePlayer.volume = 1.0f;
[self.ringtonePlayer play];
}
}
- (void)stopRingback {
if (!self.ringtonePlayer.isPlaying) {
return;
}
[self.ringtonePlayer stop];
}
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {
if (flag) {
NSLog(#"Audio player finished playing successfully");
} else {
NSLog(#"Audio player finished playing with some error");
}
}
- (void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player error:(NSError *)error {
NSLog(#"Decode error occurred: %#", error);
}
#end
So, I have all multipeer connectivity related code in the main thread. I have a MCSession, MCNearbyServiceAdvertiser, and a MCNearbyServiceBrowser. These are all created with peerID, and I make sure only one sends an invitation.
The session becomes connected. My problems is that, two clients take around 20-30 seconds to connect. This is unacceptable. The clients are on good Wifi, and bluetooth. I want the browsing, invitation handler, and connection to take place within 1 second. Does anyone have any idea what is slowing things down?
Code is exactly as provided here and I also implemented certificateHandler(YES)
#interface SessionController () // Class extension
#property (nonatomic, strong) MCPeerID *peerID;
#property (nonatomic, strong) MCSession *session;
#property (nonatomic, strong) MCNearbyServiceAdvertiser *serviceAdvertiser;
#property (nonatomic, strong) MCNearbyServiceBrowser *serviceBrowser;
// Connected peers are stored in the MCSession
// Manually track connecting and disconnected peers
#property (nonatomic, strong) NSMutableOrderedSet *connectingPeersOrderedSet;
#property (nonatomic, strong) NSMutableOrderedSet *disconnectedPeersOrderedSet;
#end
#implementation SessionController
static NSString * const kMCSessionServiceType = #"mcsessionp2p";
#pragma mark - Initializer
- (instancetype)init
{
self = [super init];
if (self)
{
_peerID = [[MCPeerID alloc] initWithDisplayName:[[UIDevice currentDevice] name]];
_connectingPeersOrderedSet = [[NSMutableOrderedSet alloc] init];
_disconnectedPeersOrderedSet = [[NSMutableOrderedSet alloc] init];
NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter];
// Register for notifications
[defaultCenter addObserver:self
selector:#selector(startServices)
name:UIApplicationWillEnterForegroundNotification
object:nil];
[defaultCenter addObserver:self
selector:#selector(stopServices)
name:UIApplicationDidEnterBackgroundNotification
object:nil];
[self startServices];
_displayName = self.session.myPeerID.displayName;
}
return self;
}
#pragma mark - Memory management
- (void)dealloc
{
// Unregister for notifications on deallocation.
[[NSNotificationCenter defaultCenter] removeObserver:self];
// Nil out delegates
_session.delegate = nil;
_serviceAdvertiser.delegate = nil;
_serviceBrowser.delegate = nil;
}
#pragma mark - Override property accessors
- (NSArray *)connectedPeers
{
return self.session.connectedPeers;
}
- (NSArray *)connectingPeers
{
return [self.connectingPeersOrderedSet array];
}
- (NSArray *)disconnectedPeers
{
return [self.disconnectedPeersOrderedSet array];
}
#pragma mark - Private methods
- (void)setupSession
{
// Create the session that peers will be invited/join into.
_session = [[MCSession alloc] initWithPeer:self.peerID];
self.session.delegate = self;
// Create the service advertiser
_serviceAdvertiser = [[MCNearbyServiceAdvertiser alloc] initWithPeer:self.peerID
discoveryInfo:nil
serviceType:kMCSessionServiceType];
self.serviceAdvertiser.delegate = self;
// Create the service browser
_serviceBrowser = [[MCNearbyServiceBrowser alloc] initWithPeer:self.peerID
serviceType:kMCSessionServiceType];
self.serviceBrowser.delegate = self;
}
- (void)teardownSession
{
[self.session disconnect];
[self.connectingPeersOrderedSet removeAllObjects];
[self.disconnectedPeersOrderedSet removeAllObjects];
}
- (void)startServices
{
[self setupSession];
[self.serviceAdvertiser startAdvertisingPeer];
[self.serviceBrowser startBrowsingForPeers];
}
- (void)stopServices
{
[self.serviceBrowser stopBrowsingForPeers];
[self.serviceAdvertiser stopAdvertisingPeer];
[self teardownSession];
}
- (void)updateDelegate
{
[self.delegate sessionDidChangeState];
}
- (NSString *)stringForPeerConnectionState:(MCSessionState)state
{
switch (state) {
case MCSessionStateConnected:
return #"Connected";
case MCSessionStateConnecting:
return #"Connecting";
case MCSessionStateNotConnected:
return #"Not Connected";
}
}
#pragma mark - MCSessionDelegate protocol conformance
- (void)session:(MCSession *)session peer:(MCPeerID *)peerID didChangeState:(MCSessionState)state
{
NSLog(#"Peer [%#] changed state to %#", peerID.displayName, [self stringForPeerConnectionState:state]);
switch (state)
{
case MCSessionStateConnecting:
{
[self.connectingPeersOrderedSet addObject:peerID];
[self.disconnectedPeersOrderedSet removeObject:peerID];
break;
}
case MCSessionStateConnected:
{
[self.connectingPeersOrderedSet removeObject:peerID];
[self.disconnectedPeersOrderedSet removeObject:peerID];
break;
}
case MCSessionStateNotConnected:
{
[self.connectingPeersOrderedSet removeObject:peerID];
[self.disconnectedPeersOrderedSet addObject:peerID];
break;
}
}
[self updateDelegate];
}
- (void)session:(MCSession *)session didReceiveData:(NSData *)data fromPeer:(MCPeerID *)peerID
{
// Decode the incoming data to a UTF8 encoded string
NSString *receivedMessage = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"didReceiveData %# from %#", receivedMessage, peerID.displayName);
}
- (void)session:(MCSession *)session didStartReceivingResourceWithName:(NSString *)resourceName fromPeer:(MCPeerID *)peerID withProgress:(NSProgress *)progress
{
NSLog(#"didStartReceivingResourceWithName [%#] from %# with progress [%#]", resourceName, peerID.displayName, progress);
}
- (void)session:(MCSession *)session didFinishReceivingResourceWithName:(NSString *)resourceName fromPeer:(MCPeerID *)peerID atURL:(NSURL *)localURL withError:(NSError *)error
{
NSLog(#"didFinishReceivingResourceWithName [%#] from %#", resourceName, peerID.displayName);
// If error is not nil something went wrong
if (error)
{
NSLog(#"Error [%#] receiving resource from %# ", [error localizedDescription], peerID.displayName);
}
else
{
// No error so this is a completed transfer. The resources is located in a temporary location and should be copied to a permenant location immediately.
// Write to documents directory
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *copyPath = [NSString stringWithFormat:#"%#/%#", [paths firstObject], resourceName];
if (![[NSFileManager defaultManager] copyItemAtPath:[localURL path] toPath:copyPath error:nil])
{
NSLog(#"Error copying resource to documents directory");
}
else
{
// Get a URL for the path we just copied the resource to
NSURL *url = [NSURL fileURLWithPath:copyPath];
NSLog(#"url = %#", url);
}
}
}
// Streaming API not utilized in this sample code
- (void)session:(MCSession *)session didReceiveStream:(NSInputStream *)stream withName:(NSString *)streamName fromPeer:(MCPeerID *)peerID
{
NSLog(#"didReceiveStream %# from %#", streamName, peerID.displayName);
}
#pragma mark - MCNearbyServiceBrowserDelegate protocol conformance
// Found a nearby advertising peer
- (void)browser:(MCNearbyServiceBrowser *)browser foundPeer:(MCPeerID *)peerID withDiscoveryInfo:(NSDictionary *)info
{
NSString *remotePeerName = peerID.displayName;
NSLog(#"Browser found %#", remotePeerName);
MCPeerID *myPeerID = self.session.myPeerID;
BOOL shouldInvite = ([myPeerID.displayName compare:remotePeerName] == NSOrderedDescending);
if (shouldInvite)
{
NSLog(#"Inviting %#", remotePeerName);
[browser invitePeer:peerID toSession:self.session withContext:nil timeout:30.0];
}
else
{
NSLog(#"Not inviting %#", remotePeerName);
}
[self updateDelegate];
}
- (void)browser:(MCNearbyServiceBrowser *)browser lostPeer:(MCPeerID *)peerID
{
NSLog(#"lostPeer %#", peerID.displayName);
[self.connectingPeersOrderedSet removeObject:peerID];
[self.disconnectedPeersOrderedSet addObject:peerID];
[self updateDelegate];
}
- (void)browser:(MCNearbyServiceBrowser *)browser didNotStartBrowsingForPeers:(NSError *)error
{
NSLog(#"didNotStartBrowsingForPeers: %#", error);
}
#pragma mark - MCNearbyServiceAdvertiserDelegate protocol conformance
- (void)advertiser:(MCNearbyServiceAdvertiser *)advertiser didReceiveInvitationFromPeer:(MCPeerID *)peerID withContext:(NSData *)context invitationHandler:(void(^)(BOOL accept, MCSession *session))invitationHandler
{
NSLog(#"didReceiveInvitationFromPeer %#", peerID.displayName);
invitationHandler(YES, self.session);
[self.connectingPeersOrderedSet addObject:peerID];
[self.disconnectedPeersOrderedSet removeObject:peerID];
[self updateDelegate];
}
- (void)advertiser:(MCNearbyServiceAdvertiser *)advertiser didNotStartAdvertisingPeer:(NSError *)error
{
NSLog(#"didNotStartAdvertisingForPeers: %#", error);
}
#end
I've noticed this issue to and i can't seem to figure out whats happening here, it appears when debugging that there is a huge delay between the notification for state changing being fired, perhaps something to do with the view itself.
Update: Well i think i figured the issues out after a bit of reading, the response is pretty much instant now, in the notification in the view i have pushed the process back to the main thread like so:
-(void)peerDidChangeStateWithNotification:(NSNotification *)notification{
MCPeerID *peerID = [[notification userInfo] objectForKey:#"peerID"];
NSString *peerDisplayName = peerID.displayName;
MCSessionState state = [[[notification userInfo] objectForKey:#"state"] intValue];
if (state != MCSessionStateConnecting) {
if (state == MCSessionStateConnected) {
// add the user
[arrConnectedDevices addObject:peerDisplayName];
}
else if (state == MCSessionStateNotConnected){
// do we have connections
if ([arrConnectedDevices count] > 0) {
int indexOfPeer = [arrConnectedDevices indexOfObject:peerDisplayName];
[arrConnectedDevices removeObjectAtIndex:indexOfPeer];
}
}
}
// push to main queue for speedy response
dispatch_async(dispatch_get_main_queue(), ^(void) {
[collView reloadData];
BOOL peersExist = ([[appDelegate.mcManager.session connectedPeers] count] == 0);
NSLog(#"PEER COUNT IS %lu",(unsigned long)[[appDelegate.mcManager.session connectedPeers] count]);
[disconnectButton setEnabled:!peersExist];
if ([disconnectButton isEnabled]) {
[disconnectButton setBackgroundColor:[UIColor colorWithRed:(51/255.0) green:(202/255.0) blue:(168/255.0) alpha:1.0]];
}
else{
[disconnectButton setBackgroundColor:[UIColor colorWithRed:(107/255.0) green:(107/255.0) blue:(107/255.0) alpha:1.0]];
}
});
}
Hope this helps anyone who ran into the issues.
I am trying to make a jabber chat application using the xmppframework.
I have implemented the xmppStream methods in the applicationAppDelegate, but none of these method has been invoked.
Here is the code of the applicationAppDelegate:
- (void)setupStream {
xmppStream = [[XMPPStream alloc] init];
[xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];
//[self connect];
}
- (void)goOnline {
XMPPPresence *presence = [XMPPPresence presence];
[[self xmppStream] sendElement:presence];
}
- (void)goOffline {
XMPPPresence *presence = [XMPPPresence presenceWithType:#"unavailable"];
[[self xmppStream] sendElement:presence];
}
- (BOOL)connect {
[self setupStream];
NSString *emailUserDefault = [[NSUserDefaults standardUserDefaults] stringForKey:#"email"];
NSString *jabberID = [emailUserDefault stringByAppendingString:#"#server.local"];
NSLog(#"%#",jabberID);
NSString *myPassword = [[NSUserDefaults standardUserDefaults] stringForKey:#"password"];
NSLog(#"%#",myPassword);
if (![xmppStream isDisconnected]) {
NSLog(#"You are connected");
return YES;
}
if (jabberID == nil || myPassword == nil) {
return NO;
}
[xmppStream setMyJID:[XMPPJID jidWithString:jabberID]];
//xmppStream.myJID = [XMPPJID jidWithString:jabberID];
password = myPassword;
NSError *error = nil;
if (![xmppStream connectWithTimeout:20 error:&error])
{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Error"
message:[NSString stringWithFormat:#"Can't connect to server %#", [error localizedDescription]]
delegate:nil
cancelButtonTitle:#"Ok"
otherButtonTitles:nil];
[alertView show];
return NO;
}
return YES;
}
- (void)disconnect {
[self goOffline];
[xmppStream disconnect];
}
- (void)xmppStreamDidConnect:(XMPPStream *)sender {
isOpen = YES;
NSError *error = nil;
[[self xmppStream] authenticateWithPassword:password error:&error];
}
- (void)xmppStreamDidAuthenticate:(XMPPStream *)sender {
[self goOnline];
}
- (void)xmppStream:(XMPPStream *)sender didReceivePresence:(XMPPPresence *)presence {
NSString *presenceType = [presence type]; // online/offline
NSString *myUsername = [[sender myJID] user];
NSString *presenceFromUser = [[presence from] user];
if (![presenceFromUser isEqualToString:myUsername]) {
if ([presenceType isEqualToString:#"available"]) {
[__chatDelegate newBuddyOnline:[NSString stringWithFormat:#"%##%#", presenceFromUser, #"server.local"]];
} else if ([presenceType isEqualToString:#"unavailable"]) {
[__chatDelegate buddyWentOffline:[NSString stringWithFormat:#"%##%#", presenceFromUser, #"server.local"]];
}
}
}
- (void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message {
NSString *msg = [[message elementForName:#"body"] stringValue];
NSString *from = [[message attributeForName:#"from"] stringValue];
NSMutableDictionary *m = [[NSMutableDictionary alloc] init];
[m setObject:msg forKey:#"msg"];
[m setObject:from forKey:#"sender"];
[__messageDelegate newMessageReceived:m];
}
Here my code for the chatViewController classe:
- (myApplicationAppDelegate *)appDelegate {
return (myApplicationAppDelegate *)[[UIApplication sharedApplication] delegate];
}
- (XMPPStream *)xmppStream {
return [[self appDelegate] xmppStream];
}
- (void)viewDidLoad
{
[super viewDidLoad];
onlineBuddies = [[NSMutableArray alloc ] init];
myApplicationAppDelegate *del = [self appDelegate];
[self xmppStream];
NSString *login = [[NSUserDefaults standardUserDefaults] objectForKey:#"email"];
del._chatDelegate = self;
if (login) {
if ([[self appDelegate] connect]) {
NSLog(#"show buddy list");
}
} else {
NSLog(#"Login Error");
}
}
I cannot figure out why the xmpp delegate methods are not being invoked. If someone can give me a hand, please don't hesitate.
Thanks in advance.
I think you misunderstood the purpose of AppDelegate. First of all for every iOS app that you are creating in Xcode there is a class created that contains the name AppDelegate but this class should only be used to get information of your application state, such as if the app goes to background, if it's launched with success or if it's coming up from background. Also the app delegate is used to specify the root (or entry point) view controller of your application.
So I think you should first check the basic rules (or a basic tutorial) on how to create a very simple application (a "Hello World Application"), after that you can go forward and create a basic structure of your application and decide what view controller or what model classes will handle your connection handling and response/request parsing.
I strongly suggest that you have a look over view controllers and I'm pretty sure that after you do the above suggested "tasks" you will answer yourself the posted question.
P.S Last point, have a look on "iOS naming & other conventions"enter link description herelife cycle methods
I'm following the tutorial at:
http://mobile.tutsplus.com/tutorials/iphone/building-a-jabber-client-for-ios-server-setup/ to set up an iOS app with an ejabberd server. So far I have pretty much copied the code over to a new project.
My problem is that the XMPP delegate functions AppDelegate.m are not being called when run on the phone. Everything works fine in Simulator and the two functions below are called.
- (void)xmppStreamDidConnect:(XMPPStream *)sender {
NSLog(#"in WSAppDelegate - xmppStreamDidConnect");
isOpen = YES;
NSError *error = nil;
[[self xmppStream] authenticateWithPassword:password error:&error];
}
- (void)xmppStreamDidAuthenticate:(XMPPStream *)sender {
NSLog(#"in WSAppDelegate - xmppStreamDidAuthenticate");
[self goOnline];
}
I am able to connect on both the phone and simulator as this call runs without error:
[xmppStream connect:&error]
Here is my AppDelegate.h code:
#import <UIKit/UIKit.h>
#import "XMPPRoster.h"
#import "XMPP.h"
#import "SMChatDelegate.h"
#import "SMMessageDelegate.h"
#class SMBuddyListViewController;
#interface WSAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
SMBuddyListViewController *viewController;
XMPPStream *xmppStream;
XMPPRoster *xmppRoster;
NSString *password;
BOOL isOpen;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet SMBuddyListViewController *viewController;
#property (nonatomic, readonly) XMPPStream *xmppStream;
#property (nonatomic, readonly) XMPPRoster *xmppRoster;
#property (nonatomic, assign) id _chatDelegate;
#property (nonatomic, assign) id _messageDelegate;
- (BOOL)connect;
- (void)disconnect;
#end
And AppDelegate.m:
#import "WSBuddyListViewController.h"
#interface WSAppDelegate()
- (void)setupStream;
- (void)goOnline;
- (void)goOffline;
#end
#implementation WSAppDelegate
#synthesize xmppStream;
#synthesize xmppRoster;
#synthesize window;
#synthesize viewController;
#synthesize _chatDelegate;
#synthesize _messageDelegate;
- (void)applicationWillResignActive:(UIApplication *)application {
[self disconnect];
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
[self setupStream];
BOOL connected = NO;
connected = [self connect];
NSLog(#"*** connected = %i", connected);
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
return YES;
}
- (void)setupStream {
NSLog(#"in WSAppDelegate - setupStream");
xmppStream = [[XMPPStream alloc] init];
[xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];
[xmppStream setHostName:#"localhost"];
}
- (void)goOnline {
NSLog(#"in WSAppDelegate - goOnline");
XMPPPresence *presence = [XMPPPresence presence];
[[self xmppStream] sendElement:presence];
}
- (void)goOffline {
XMPPPresence *presence = [XMPPPresence presenceWithType:#"unavailable"];
[[self xmppStream] sendElement:presence];
}
- (BOOL)connect {
NSLog(#"in WSAppDelegate - connect");
[self setupStream];
NSString *jabberID = [[NSUserDefaults standardUserDefaults] stringForKey:#"userID"];
NSString *myPassword = [[NSUserDefaults standardUserDefaults] stringForKey:#"userPassword"];
if (![xmppStream isDisconnected]) {
NSLog(#"in WSAppDelegate - connect - if (![xmppStream isDisconnected]) ");
return YES;
}
if (jabberID == nil || myPassword == nil) {
NSLog(#"in WSAppDelegate - connect - if (jabberID == nil || myPassword == nil)");
return NO;
}
[xmppStream setMyJID:[XMPPJID jidWithString:jabberID]];
password = myPassword;
NSError *error = nil;
if (![xmppStream connect:&error])
{
NSLog(#"in WSAppDelegate - connect - if (![xmppStream connect:&error]))");
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Error"
message:[NSString stringWithFormat:#"Can't connect to server %#", [error localizedDescription]]
delegate:nil
cancelButtonTitle:#"Ok"
otherButtonTitles:nil];
[alertView show];
return NO;
}
return YES;
}
- (void)disconnect {
[self goOffline];
[xmppStream disconnect];
[_chatDelegate didDisconnect];
}
#pragma mark -
#pragma mark XMPP delegates
- (void)xmppStreamDidConnect:(XMPPStream *)sender {
NSLog(#"in WSAppDelegate - xmppStreamDidConnect");
isOpen = YES;
NSError *error = nil;
[[self xmppStream] authenticateWithPassword:password error:&error];
}
- (void)xmppStreamDidAuthenticate:(XMPPStream *)sender {
NSLog(#"in WSAppDelegate - xmppStreamDidAuthenticate");
[self goOnline];
}
- (BOOL)xmppStream:(XMPPStream *)sender didReceiveIQ:(XMPPIQ *)iq {
return NO;
}
- (void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message {
NSLog(#"in WSAppDelegate - xmppStream:(XMPPStream *)sender didReceiveMessage");
NSString *msg = [[message elementForName:#"body"] stringValue];
NSString *from = [[message attributeForName:#"from"] stringValue];
NSMutableDictionary *m = [[NSMutableDictionary alloc] init];
[m setObject:msg forKey:#"msg"];
[m setObject:from forKey:#"sender"];
[_messageDelegate newMessageReceived:m];
}
- (void)xmppStream:(XMPPStream *)sender didReceivePresence:(XMPPPresence *)presence {
NSLog(#"in WSAppDelegate - xmppStream:(XMPPStream *)sender didReceivePresence:");
NSString *presenceType = [presence type]; // online/offline
NSString *myUsername = [[sender myJID] user];
NSString *presenceFromUser = [[presence from] user];
if (![presenceFromUser isEqualToString:myUsername]) {
if ([presenceType isEqualToString:#"available"]) {
[_chatDelegate newBuddyOnline:[NSString stringWithFormat:#"%##%#", presenceFromUser, #"localhost"]];
} else if ([presenceType isEqualToString:#"unavailable"]) {
[_chatDelegate buddyWentOffline:[NSString stringWithFormat:#"%##%#", presenceFromUser, #"localhost"]];
}
}
}
- (void)dealloc {
[xmppStream removeDelegate:self];
[xmppRoster removeDelegate:self];
[xmppStream disconnect];
}
#end
If you look at your setupStream method, you are using the name "localhost." This is leading me to believe the server is on your development machine, and the device is trying to connect to itself (localhost). You will have to replace that with your server name.
This likely works in the simulator because the the client and server are one and the same.
UPDATE:
I just read through the tutorial, and it does not do a good job at all at describing how it would work on real device.
as Mike D said, you're connecting to the server which is on your machine([xmppStream setHostName:#"localhost"];)
In order to connect to the "localhost" you have to change hosts file on your device(/etc/hosts), but that is forbidden by Apple, since your app cant change stuff outside the sandbox.(unless the device is jailbroken).
I've done this stuff on an android phone, when faced similar problem in the past.
Check this discussion(Does hosts file exist on the iPhone? How to change it?)
I'm attempting to implement the latest Facebook Connect SDK and I'm having some troubles. For some reason the delegate callbacks for FBSessionDelegate protocol are not being fired. I've followed the instructions on the git Facebook page and tried to mimic the Facebook sample app but no luck. I'm going crazy here so I'm gonna post my code and maybe somebody will see something silly that I've missed.
#import <Foundation/Foundation.h>
#import "FBConnect.h"
#interface FacebookWrapper : UIViewController <FBSessionDelegate, FBRequestDelegate, FBDialogDelegate>{
Facebook* _facebook;
NSArray* _permissions;
}
#property(readonly) Facebook *facebook;
- (void)login;
#end
#import "FacebookWrapper.h"
static NSString* kAppId = #"1234455667778";
#implementation FacebookWrapper
#synthesize facebook = _facebook;
- (id)init {
if (self = [super init]) {
_permissions = [[NSArray arrayWithObjects: #"read_stream", #"offline_access",nil] retain];
_facebook = [[Facebook alloc] initWithAppId:kAppId];
}
return self;
}
- (void)dealloc {
[_facebook release];
[_permissions release];
[super dealloc];
}
- (void)login {
[_facebook authorize:_permissions delegate:self];
}
- (void)fbDidLogin {
NSLog(#"Did Log In");
}
- (void)fbDidNotLogin:(BOOL)cancelled {
NSLog(#"Failed to log in");
}
- (void)fbDidLogout {
NSLog(#"Logged Out");
}
And to call this from another class,
FacebookWrapper *fw = [[FacebookWrapper alloc] init];
[fw login];
The behavior that I'm seeing on the phone is as expected. The Facebook app launches on init and permissions are requested. The phone then brings my app back to the foreground but the delegates for FBSessionDelegate are never fired. I've tried this in the Facebook sample app using my app ID and it worked fine. I have no idea what the difference is.
I figured out the problem. In the App Delegate you need to override handleOpenURL.
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
return [[facebookWrapper facebook] handleOpenURL:url];
}
If you're creating a wrapper class as I am you'll need an instance of it in the app delegate so you can call the hanleOpenURL method in the Facebook class. Also notice that there is a public readonly property for my Facebook instance in my FacebookWrapper class so I can call handlOpenURL.
By doing this your app will know where to continue when it returns from getting permissions inside of the Facebook app.
I have developed wrapper class which will be very helpful to you.
First download latest SDK of Facebook.
Create one class named as "FacebookHelper"
Add following code in .h file:
#import <Foundation/Foundation.h>
#import "FBConnect.h"
#protocol FBApiCallDelegate;
typedef enum FBApiCallType
{
FBApiCallNone = 0,
FBApiCallGetUserInfo = 1,
FBApiCallGetUserFriend = 2,
FBApiCallPostMessage = 3,
FBApiCallPostPicture = 4,
FBApiCallShareLink = 5,
FBApiCallPostAll = 6,
FBApiCallPostMessageFriendWall = 7
} FBApiCallType;
#interface FacebookHelper : NSObject<FBRequestDelegate, FBDialogDelegate, FBSessionDelegate> {
Facebook *objFacebook;
NSArray *arrPermission;
id<FBApiCallDelegate> apiCallDelegate;
FBApiCallType currentApiCallType;
NSString *strMessage;
NSString *strUrlTitle;
NSString *strUrl;
NSString *strCaption;
NSString *strPictureUrl;
UIImage *image;
}
#property(readonly) Facebook *objFacebook;
#property (nonatomic, assign) id<FBApiCallDelegate> apiCallDelegate;
#property (nonatomic, assign) FBApiCallType currentApiCallType;
#property (nonatomic, retain) NSString *strMessage;
#property (nonatomic, retain) NSString *strUrlTitle;
#property (nonatomic, retain) NSString *strUrl;
#property (nonatomic, retain) NSString *strCaption;
#property (nonatomic, retain) NSString *strPictureUrl;
#property (nonatomic, retain) UIImage *image;
+ (FacebookHelper *) sharedInstance;
- (void)releaseObjects;
//Store Authentication
- (void)storeAuthData:(NSString *)accessToken expiresAt:(NSDate *)expiresAt;
- (void)removeAuthData;
//Public Methods
-(BOOL)isLoggedIn;
-(void)loginToFacebook;
-(void)logoutFromFacebook;
//Facebook Methods
-(void)getUserInfo; //Get User Info
-(void)getUserFriends; //Get User's Friend List
-(void)postMessageToWall; //Post Message to FB Wall
-(void)postPictureToWall; //Post Picture to FB Wall
-(void)shareLinkOnWall; //Share Link on FB Wall
-(void)postAllToWall; //Post All - Message, Link, Caption, PhotoUrl
-(void)postMessageToFriendWall; //Post Message to Friend Wall
//String Methods
- (BOOL)isEmptyString:(NSString *)strValue;
- (NSString *) trimWhiteSpace:(NSString *)strValue;
// Default AlertView
-(void)showAlertView:(NSString *)pstrTitle withMessage:(NSString *)pstrMessage delegate:(id)pDelegate;
#end
#protocol FBApiCallDelegate <NSObject>
#optional
//Get User Info Delegate
-(void)finishUserInfoResponse:(id)result;
-(void)failedUserInfoResponse:(NSError *)error;
//Get User's Friend List
-(void)finishUserFriendResponse:(id)result;
-(void)failedUserFriendResponse:(NSError *)error;
//Post Message to FB Wall Delegate
-(void)finishPostMessageResponse:(id)result;
-(void)failedPostMessageResponse:(NSError *)error;
//Post Picture to FB Wall
-(void)finishPostPictureResponse:(id)result;
-(void)failedPostPictureResponse:(NSError *)error;
//Share Link on FB Wall
-(void)finishShareLinkResponse:(id)result;
-(void)failedShareLinkResponse:(NSError *)error;
//Post All - Message, Link, Caption, PhotoUrl
-(void)finishPostAllResponse:(id)result;
-(void)failedPostAllResponse:(NSError *)error;
//Post Message to Friend Wall Delegate
-(void)finishPostMsgFriendWallResponse:(id)result;
-(void)failedPostMsgFriendWallResponse:(NSError *)error;
#end
Add following code in .m file:
#import "FacebookHelper.h"
#implementation FacebookHelper
#synthesize objFacebook;
#synthesize apiCallDelegate, currentApiCallType;
#synthesize strMessage, strUrlTitle, strUrl, strCaption, strPictureUrl, image;
#pragma mark -
#pragma mark Singleton Variables
static FacebookHelper *singletonHelper = nil;
#pragma mark -
#pragma mark Singleton Methods
- (id)init {
if (!g_kFacebookAppId) {
NSLog(#"%#", msgFBAppIDMissing);
exit(1);
return nil;
}
if ((self = [super init])) {
arrPermission = [[NSArray arrayWithObjects: #"read_stream", #"publish_stream", #"offline_access", #"email", #"read_friendlists", #"friends_birthday",nil] retain];
}
return self;
}
+ (FacebookHelper *)sharedInstance {
#synchronized(self) {
if (singletonHelper == nil) {
[[self alloc] init]; // assignment not done here
}
}
return singletonHelper;
}
+ (id)allocWithZone:(NSZone *)zone {
#synchronized(self) {
if (singletonHelper == nil) {
singletonHelper = [super allocWithZone:zone];
// assignment and return on first allocation
return singletonHelper;
}
}
// on subsequent allocation attempts return nil
return nil;
}
- (id)copyWithZone:(NSZone *)zone {
return self;
}
- (id)retain {
return self;
}
- (unsigned)retainCount {
return UINT_MAX; // denotes an object that cannot be released
}
//- (void)release {
- (void)dealloc {
[self releaseObjects];
[super dealloc];
}
- (id)autorelease {
return self;
}
- (void)releaseObjects {
[self.strMessage release];
[self.strUrlTitle release];
[self.strUrl release];
[self.strCaption release];
[self.strPictureUrl release];
[self.image release];
[objFacebook release];
objFacebook = nil;
}
#pragma mark -
#pragma mark FBDelegate(FBSessionDelegate) Methods
/**
* Called when the user has logged in successfully.
*/
- (void)fbDidLogin {
NSLog(#"FB login OK");
[self storeAuthData:objFacebook.accessToken expiresAt:objFacebook.expirationDate];
switch(currentApiCallType)
{
case FBApiCallGetUserInfo:
[self getUserInfo];
break;
case FBApiCallGetUserFriend:
[self getUserFriends];
break;
case FBApiCallPostMessage:
[self postMessageToWall];
break;
case FBApiCallPostPicture:
[self postPictureToWall];
break;
case FBApiCallShareLink:
[self shareLinkOnWall];
break;
case FBApiCallPostAll:
[self postAllToWall];
break;
case FBApiCallPostMessageFriendWall:
[self postMessageToFriendWall];
break;
}
}
/**
* Called when the user canceled the authorization dialog.
*/
-(void)fbDidNotLogin:(BOOL)cancelled {
NSLog(#"FB did not login");
[self removeAuthData];
}
/**
* Called when the request logout has succeeded.
*/
- (void)fbDidLogout {
NSLog(#"FB logout OK");
[self removeAuthData];
}
-(void)fbDidExtendToken:(NSString *)accessToken expiresAt:(NSDate *)expiresAt {
NSLog(#"token extended");
[self storeAuthData:accessToken expiresAt:expiresAt];
}
/**
* Called when the session has expired.
*/
- (void)fbSessionInvalidated {
[self showAlertView:msgFBSessionInvalidateTitle withMessage:msgFBSessionInvalidateMessage delegate:nil];
[self fbDidLogout];
}
#pragma mark -
#pragma mark FBRequestDelegate Methods
/**
* 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 {
NSLog(#"FB request OK");
switch(currentApiCallType)
{
case FBApiCallGetUserInfo:
if([self.apiCallDelegate respondsToSelector:#selector(finishUserInfoResponse:)])
[self.apiCallDelegate finishUserInfoResponse:result];
break;
case FBApiCallGetUserFriend:
if ([self.apiCallDelegate respondsToSelector:#selector(finishUserFriendResponse:)])
[self.apiCallDelegate finishUserFriendResponse:result];
break;
case FBApiCallPostMessage:
if ([self.apiCallDelegate respondsToSelector:#selector(finishPostMessageResponse:)])
[self.apiCallDelegate finishPostMessageResponse:result];
break;
case FBApiCallPostPicture:
if ([self.apiCallDelegate respondsToSelector:#selector(finishPostPictureResponse:)])
[self.apiCallDelegate finishPostPictureResponse:result];
break;
case FBApiCallShareLink:
if ([self.apiCallDelegate respondsToSelector:#selector(finishShareLinkResponse:)])
[self.apiCallDelegate finishShareLinkResponse:result];
break;
case FBApiCallPostAll:
if ([self.apiCallDelegate respondsToSelector:#selector(finishPostAllResponse:)])
[self.apiCallDelegate finishPostAllResponse:result];
break;
case FBApiCallPostMessageFriendWall:
if ([self.apiCallDelegate respondsToSelector:#selector(finishPostMsgFriendWallResponse:)])
[self.apiCallDelegate finishPostMsgFriendWallResponse:result];
break;
}
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
currentApiCallType = FBApiCallNone;
}
/**
* Called when an error prevents the Facebook API request from completing
* successfully.
*/
/*- (void)request:(FBRequest *)request didFailWithError:(NSError *)error {
NSLog(#"FB error: %#", [error localizedDescription]);
}*/
- (void)request:(FBRequest *)request didFailWithError:(NSError *)error {
NSLog(#"Err message: %#", [[error userInfo] objectForKey:#"error_msg"]);
/*NSLog(#"Err code: %#", [error code]);
NSLog(#"Err desc: %#", [error description]);
NSLog(#"FB error: %#", [error localizedDescription]);*/
switch(currentApiCallType)
{
case FBApiCallGetUserInfo:
if ([self.apiCallDelegate respondsToSelector:#selector(failedUserInfoResponse:)])
[self.apiCallDelegate failedUserInfoResponse:error];
break;
case FBApiCallGetUserFriend:
if ([self.apiCallDelegate respondsToSelector:#selector(failedUserFriendResponse:)])
[self.apiCallDelegate failedUserFriendResponse:error];
break;
case FBApiCallPostMessage:
if ([self.apiCallDelegate respondsToSelector:#selector(failedPostMessageResponse:)])
[self.apiCallDelegate failedPostMessageResponse:error];
break;
case FBApiCallPostPicture:
if ([self.apiCallDelegate respondsToSelector:#selector(failedPostPictureResponse:)])
[self.apiCallDelegate failedPostPictureResponse:error];
break;
case FBApiCallShareLink:
if ([self.apiCallDelegate respondsToSelector:#selector(failedShareLinkResponse:)])
[self.apiCallDelegate failedShareLinkResponse:error];
break;
case FBApiCallPostAll:
if ([self.apiCallDelegate respondsToSelector:#selector(failedPostAllResponse:)])
[self.apiCallDelegate failedPostAllResponse:error];
case FBApiCallPostMessageFriendWall:
if ([self.apiCallDelegate respondsToSelector:#selector(failedPostMsgFriendWallResponse:)])
[self.apiCallDelegate failedPostMsgFriendWallResponse:error];
break;
}
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
currentApiCallType = FBApiCallNone;
}
/**
* Called when a UIServer Dialog successfully return.
*/
- (void)dialogDidComplete:(FBDialog *)dialog {
NSLog(#"Published successfully on FB");
}
#pragma mark -
#pragma mark Store/Remove Authentication
- (void)storeAuthData:(NSString *)accessToken expiresAt:(NSDate *)expiresAt {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:accessToken forKey:g_kFacebookAccessToken];
[defaults setObject:expiresAt forKey:g_kFacebookExpirationDate];
[defaults synchronize];
}
- (void)removeAuthData{
// Remove saved authorization information if it exists and it is
// ok to clear it (logout, session invalid, app unauthorized)
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults removeObjectForKey:g_kFacebookAccessToken];
[defaults removeObjectForKey:g_kFacebookExpirationDate];
[defaults synchronize];
/*
[[NSUserDefaults standardUserDefaults] setObject:#"" forKey:g_kFacebookAccessToken];
[[NSUserDefaults standardUserDefaults] setObject:#"" forKey:g_kFacebookExpirationDate];
[[NSUserDefaults standardUserDefaults] synchronize];*/
/*
[[NSUserDefaults standardUserDefaults] setObject:nil forKey:g_kFacebookAccessToken];
[[NSUserDefaults standardUserDefaults] setObject:nil forKey:g_kFacebookExpirationDate];
[[NSUserDefaults standardUserDefaults] synchronize];*/
}
#pragma mark -
#pragma mark Public Methods
-(BOOL)isLoggedIn
{
if(objFacebook == nil)
objFacebook = [[[Facebook alloc] initWithAppId:g_kFacebookAppId andDelegate:self] retain];
NSString *strAccessToken = [[NSUserDefaults standardUserDefaults] stringForKey:g_kFacebookAccessToken];
NSLog(#"Access Token = %#", strAccessToken);
//if(![strAccessToken isEmptyString])
if(![self isEmptyString:strAccessToken])
{
objFacebook.accessToken = [[NSUserDefaults standardUserDefaults] stringForKey:g_kFacebookAccessToken];
objFacebook.expirationDate = (NSDate *) [[NSUserDefaults standardUserDefaults] objectForKey:g_kFacebookExpirationDate];
}
if([objFacebook isSessionValid])
return YES;
else
return NO;
return NO;
}
-(void)loginToFacebook
{
if(![self isLoggedIn])
[objFacebook authorize:arrPermission];
}
-(void)logoutFromFacebook {
[objFacebook logout:self];
[self removeAuthData];
[self releaseObjects];
}
#pragma mark -
#pragma mark Facebook Methods
-(void)getUserInfo
{
currentApiCallType = FBApiCallGetUserInfo;
if([self isLoggedIn])
{
NSMutableDictionary *params = [NSMutableDictionary dictionaryWithObjectsAndKeys:#"name,picture",#"fields",nil];
[objFacebook requestWithGraphPath:#"me" andParams:params andDelegate:self];
}
else
[self loginToFacebook];
}
-(void)getUserFriends
{
currentApiCallType = FBApiCallGetUserFriend;
if([self isLoggedIn])
{
NSMutableDictionary *params = [NSMutableDictionary dictionaryWithObjectsAndKeys:#"picture,id,name,birthday,link,gender,last_name,first_name",#"fields",nil];
[objFacebook requestWithGraphPath:#"me/friends" andParams:params andDelegate:self];
}
else
[self loginToFacebook];
}
-(void)postMessageToWall
{
currentApiCallType = FBApiCallPostMessage;
if([self isLoggedIn])
{
NSMutableDictionary *params = [[[NSMutableDictionary alloc] init] autorelease];
[params setObject:#"status" forKey:#"type"];
[params setObject:self.strMessage forKey:#"message"];
[objFacebook requestWithGraphPath:#"me/feed" andParams:params andHttpMethod:#"POST" andDelegate:self];
}
else
[self loginToFacebook];
}
-(void)postPictureToWall
{
currentApiCallType = FBApiCallPostPicture;
if([self isLoggedIn])
{
NSMutableDictionary *params = [[[NSMutableDictionary alloc] init] autorelease];
[params setObject:self.image forKey:#"source"];
[params setObject:self.strMessage forKey:#"message"];
[objFacebook requestWithGraphPath:#"me/photos" andParams:params andHttpMethod:#"POST" andDelegate:self];
}
else
[self loginToFacebook];
}
-(void)shareLinkOnWall
{
currentApiCallType = FBApiCallShareLink;
if([self isLoggedIn])
{
NSMutableDictionary *params = [[[NSMutableDictionary alloc] init] autorelease];
[params setObject:#"link" forKey:#"type"];
[params setObject:self.strUrl forKey:#"link"];
[params setObject:self.strMessage forKey:#"description"];
[objFacebook requestWithGraphPath:#"me/feed" andParams:params andHttpMethod:#"POST" andDelegate:self];
}
else
[self loginToFacebook];
}
-(void)postAllToWall
{
currentApiCallType = FBApiCallPostAll;
if([self isLoggedIn])
{
NSMutableDictionary *params = [[[NSMutableDictionary alloc] init] autorelease];
[params setObject:self.strMessage forKey:#"description"];
[params setObject:self.strUrlTitle forKey:#"name"];
[params setObject:self.strUrl forKey:#"link"];
[params setObject:self.strCaption forKey:#"caption"];
[params setObject:self.strPictureUrl forKey:#"picture"];
[objFacebook requestWithGraphPath:#"me/feed" andParams:params andHttpMethod:#"POST" andDelegate:self];
}
else
[self loginToFacebook];
}
-(void)postMessageToFriendWall
{
currentApiCallType = FBApiCallPostMessageFriendWall;
if([self isLoggedIn])
{
NSString *strGraphPath = [NSString stringWithFormat:#"%#/feed", #"100002305497328"];
//NSString *strGraphPath = [NSString stringWithFormat:#"%#/feed", #"100002560928461"];
NSMutableDictionary *params = [[[NSMutableDictionary alloc] init] autorelease];
[params setObject:self.strMessage forKey:#"message"];
[objFacebook requestWithGraphPath:strGraphPath andParams:params andHttpMethod:#"POST" andDelegate:self];
}
else
[self loginToFacebook];
}
#pragma mark -
#pragma mark String Methods
- (BOOL)isEmptyString:(NSString *)strValue
{
NSString *copy;
if (strValue == nil)
return (YES);
if ([strValue isEqualToString:#""])
return (YES);
if ([strValue isEqualToString:#"(null)"])
return (YES);
copy = [[strValue copy] autorelease];
//if ([[copy trimWhiteSpace] isEqualToString: #""])
if ([[self trimWhiteSpace:copy] isEqualToString: #""])
return (YES);
return (NO);
} /*stringIsEmpty*/
- (NSString *) trimWhiteSpace:(NSString *)strValue
{
NSMutableString *s = [[strValue mutableCopy] autorelease];
CFStringTrimWhitespace ((CFMutableStringRef) s);
return (NSString *) [[s copy] autorelease];
} /*trimWhiteSpace*/
#pragma mark -
#pragma mark Default AlertView
-(void)showAlertView:(NSString *)pstrTitle withMessage:(NSString *)pstrMessage delegate:(id)pDelegate
{
UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:pstrTitle message:pstrMessage delegate:pDelegate cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alertView show];
[alertView release];
}
#end
That's it. Now our Facebook wrapper class is ready.
Create 3 below class for better organization.
FacebookGlobal.h
Add below data:
#define g_kFacebookAppId #"<Your AppID>"
#define g_kFacebookAppSecret #"<Your Secret>"
#define g_kPostMessage #"<Message>"
#define g_kPostUrlTitle #"<Title>"
#define g_kPostUrl #"<Url>"
#define g_kPostCaption #"<Caption>"
#define g_kPostPictureUrl #"<Image Url>"
FacebookKey.h
Add below data:
#define g_kFacebookAccessToken #"FBAccessTokenKey"
#define g_kFacebookExpirationDate #"FBExpirationDateKey"
FacebookMessage.h
Add below data:
//AppID Missing
#define msgFBAppIDMissing #"Missing AppID"
//Session Invalidated
#define msgFBSessionInvalidateTitle #"Auth Exception"
#define msgFBSessionInvalidateMessage #"Your session has expired."
Import below files in your .pch files:
#import "FacebookGlobal.h"
#import "FacebookKey.h"
#import "FacebookMessage.h"
#import "FacebookHelper.h"
Now, you are ready for use.
In your view controller, add below delegate:
<FBApiCallDelegate>
In your view controller .m file, add below methods to call as well delegate:
- (void)viewWillAppear:(BOOL)animated {
/*
FacebookHelper *fbHelper = [FacebookHelper sharedInstance];
[fbHelper logoutFromFacebook];
*/
//Get User Info
FacebookHelper *fbHelper = [FacebookHelper sharedInstance];
fbHelper.apiCallDelegate = self;
[fbHelper getUserInfo];
/*
//Get User's Friend List
FacebookHelper *fbHelper = [FacebookHelper sharedInstance];
fbHelper.apiCallDelegate = self;
[fbHelper getUserFriends];
*/
/*
//Post Message to FB Wall
FacebookHelper *fbHelper = [FacebookHelper sharedInstance];
fbHelper.apiCallDelegate = self;
fbHelper.strMessage = g_kPostMessage;
[fbHelper postMessageToWall];
*/
/*
//Post Picture to FB Wall
FacebookHelper *fbHelper = [FacebookHelper sharedInstance];
fbHelper.apiCallDelegate = self;
fbHelper.strMessage = g_kPostMessage;
fbHelper.image = [UIImage imageNamed:#"postPicture.jpg"];
[fbHelper postPictureToWall];
*/
/*
//Share Link on FB Wall
FacebookHelper *fbHelper = [FacebookHelper sharedInstance];
fbHelper.apiCallDelegate = self;
fbHelper.strUrl = g_kPostUrl;
fbHelper.strMessage = g_kPostMessage;
[fbHelper shareLinkOnWall];
*/
/*
//Post All to FB Wall
FacebookHelper *fbHelper = [FacebookHelper sharedInstance];
fbHelper.apiCallDelegate = self;
fbHelper.strMessage = g_kPostMessage;
fbHelper.strUrlTitle = g_kPostUrlTitle;
fbHelper.strUrl = g_kPostUrl;
fbHelper.strCaption = g_kPostCaption;
fbHelper.strPictureUrl = g_kPostPictureUrl;
[fbHelper postAllToWall];
*/
/*
//Post Message to Friend Wall
FacebookHelper *fbHelper = [FacebookHelper sharedInstance];
fbHelper.apiCallDelegate = self;
fbHelper.strMessage = g_kPostMessage;
[fbHelper postMessageToFriendWall];
*/
}
#pragma mark -
#pragma mark Get User Info Delegate
-(void)finishUserInfoResponse:(id)result{
NSLog(#"UserInfo response successed!");
NSLog(#"%#", result);
}
-(void)failedUserInfoResponse:(NSError *)error{
NSLog(#"UserInfo response failed!");
}
#pragma mark -
#pragma mark Get User's Friend List
-(void)finishUserFriendResponse:(id)result{
NSLog(#"User FriendList response successed!");
NSLog(#"%#", result);
}
-(void)failedUserFriendResponse:(NSError *)error{
NSLog(#"User FriendList response failed!");
}
#pragma mark -
#pragma mark Post Message to FB Wall Delegate
-(void)finishPostMessageResponse:(id)result{
NSLog(#"Post message successed!");
NSLog(#"%#", result);
}
-(void)failedPostMessageResponse:(NSError *)error{
NSLog(#"Post message failed!");
}
#pragma mark -
#pragma mark Post Picture to FB Wall
-(void)finishPostPictureResponse:(id)result{
NSLog(#"Post picture successed!");
NSLog(#"%#", result);
}
-(void)failedPostPictureResponse:(NSError *)error{
NSLog(#"Post picture failed!");
}
#pragma mark -
#pragma mark Share Link on FB Wall
-(void)finishShareLinkResponse:(id)result{
NSLog(#"Share link successed!");
NSLog(#"%#", result);
}
-(void)failedShareLinkResponse:(NSError *)error{
NSLog(#"Share link failed!");
}
#pragma mark -
#pragma mark Post All - Message, Link, Caption, PhotoUrl
-(void)finishPostAllResponse:(id)result{
NSLog(#"Post all successed!");
NSLog(#"%#", result);
}
-(void)failedPostAllResponse:(NSError *)error{
NSLog(#"Post all failed!");
}
#pragma mark -
#pragma mark Post Message to Friend Wall Delegate
-(void)finishPostMsgFriendWallResponse:(id)result{
NSLog(#"Post message to friend wall successed!");
NSLog(#"%#", result);
}
-(void)failedPostMsgFriendWallResponse:(NSError *)error{
NSLog(#"Post message to friend wall failed!");
}
Happy Coding :)
Enjoy.