MFMailComposerViewController not Sending Email - ios

I have implemented in app Email and SMS sending functionality
but i am not being able to send email and SMS. application is crashing
well . . .
i am running it in Simulator
but it should show the AlertView
it's not supposed to crash
My Interface code is as Follows :-
#import <UIKit/UIKit.h>
#import <MessageUI/MessageUI.h>
#interface MessagingVC :UIViewController<MFMessageComposeViewControllerDelegate,MFMailComposeViewControllerDelegate,UITextFieldDelegate,UITextViewDelegate>
- (IBAction)sendEmail:(id)sender;
- (IBAction)sendSMS:(id)sender;
#property (retain, nonatomic) IBOutlet UITextField *EmailToTxtField;
#property (retain, nonatomic) IBOutlet UITextField *PhoneToTxtField;
#property (retain, nonatomic) IBOutlet UITextView *massageBodyTxtView;
#property (retain, nonatomic) NSMutableArray *toRecipentsEmail;
#property (retain, nonatomic) NSMutableArray *toRecipentsPhone;
#property (retain, nonatomic) MFMessageComposeViewController *MessageCompVC;
#property (retain, nonatomic) MFMailComposeViewController *MailCompVC;
#end
MY implementation code is as Follows :-
#import "MessagingVC.h"
#interface MessagingVC ()
#end
#implementation MessagingVC
#synthesize toRecipentsEmail,toRecipentsPhone,MailCompVC,MessageCompVC;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (BOOL)textView:(UITextView *)txtView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
if( [text rangeOfCharacterFromSet:[NSCharacterSet newlineCharacterSet]].location == NSNotFound ) {
return YES;
}
[txtView resignFirstResponder];
return NO;
}
-(BOOL)textFieldShouldReturn:(UITextField *)textField
{
if (textField == _EmailToTxtField)
{
toRecipentsEmail = [NSMutableArray arrayWithArray:[textField.text componentsSeparatedByString:#","]];
}
if (textField == _PhoneToTxtField)
{
toRecipentsPhone = [NSMutableArray arrayWithArray:[textField.text componentsSeparatedByString:#","]];
}
[_EmailToTxtField resignFirstResponder];
[_PhoneToTxtField resignFirstResponder];
NSLog(#"toRecipentsEmail Count == %d",[toRecipentsEmail count]);
return YES;
}
- (void)viewDidLoad
{
[super viewDidLoad];
_massageBodyTxtView.delegate = self;
_EmailToTxtField.delegate = self;
_PhoneToTxtField.delegate = self;
toRecipentsPhone = [[NSMutableArray alloc] init];
toRecipentsEmail = [[NSMutableArray alloc] init];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)dealloc {
[_EmailToTxtField release];
[_PhoneToTxtField release];
[_massageBodyTxtView release];
[toRecipentsEmail release];
[toRecipentsPhone release];
[MailCompVC release];
[MessageCompVC release];
[super dealloc];
}
- (void)viewDidUnload {
[self setEmailToTxtField:nil];
[self setPhoneToTxtField:nil];
[self setMassageBodyTxtView:nil];
[self setToRecipentsEmail:nil];
[self setToRecipentsPhone:nil];
[self setMailCompVC:nil];
[self setMessageCompVC:nil];
[super viewDidUnload];
}
- (IBAction)sendEmail:(id)sender {
self.MailCompVC = [[MFMailComposeViewController alloc] init];
NSString *emailTitle = #"Subject";
MailCompVC.mailComposeDelegate = self;
[MailCompVC setToRecipients:toRecipentsEmail];
[MailCompVC setMessageBody:_massageBodyTxtView.text isHTML:NO];
[MailCompVC setSubject:emailTitle];
[self presentViewController:MailCompVC animated:YES completion:nil];
}
- (void) mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error
{
switch (result)
{
case MFMailComposeResultCancelled:
NSLog(#"Mail cancelled");
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Mail cancelled" message:#"Mail cancelled" delegate:nil cancelButtonTitle:#"OK!" otherButtonTitles:nil, nil];
[alert show];
[alert release];
break;
case MFMailComposeResultSaved:
NSLog(#"Mail saved");
UIAlertView *alert1 = [[UIAlertView alloc] initWithTitle:#"Mail saved" message:#"Mail saved" delegate:nil cancelButtonTitle:#"OK!" otherButtonTitles:nil, nil];
[alert1 show];
[alert1 release];
break;
case MFMailComposeResultSent:
NSLog(#"Mail sent");
UIAlertView *alert2 = [[UIAlertView alloc] initWithTitle:#"Mail sent" message:#"Mail sent" delegate:nil cancelButtonTitle:#"OK!" otherButtonTitles:nil, nil];
[alert2 show];
[alert2 release];
break;
case MFMailComposeResultFailed:
NSLog(#"Mail sent failure: %#", [error localizedDescription]);
UIAlertView *alert3 = [[UIAlertView alloc] initWithTitle:#"Mail sent failure" message:[NSString stringWithFormat:#"%#",[error localizedDescription]] delegate:nil cancelButtonTitle:#"OK!" otherButtonTitles:nil, nil];
[alert3 show];
[alert3 release];
break;
default:
break;
}
// Close the Mail Interface
[self dismissViewControllerAnimated:YES completion:NULL];
}
- (IBAction)sendSMS:(id)sender {
self.MessageCompVC = [[MFMessageComposeViewController alloc] init];
MessageCompVC.messageComposeDelegate = self;
[MessageCompVC setBody:_massageBodyTxtView.text];
[MessageCompVC setRecipients:toRecipentsPhone];
[self presentViewController:MessageCompVC animated:YES completion:nil];
}
- (void)messageComposeViewController:(MFMessageComposeViewController *)controller didFinishWithResult:(MessageComposeResult)result
{
switch (result)
{
case MessageComposeResultCancelled:
NSLog(#"Message cancelled");
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Message cancelled" message:#"Message cancelled" delegate:nil cancelButtonTitle:#"OK!" otherButtonTitles:nil, nil];
[alert show];
[alert release];
break;
case MessageComposeResultFailed:
NSLog(#"Message Failed");
UIAlertView *alert1 = [[UIAlertView alloc] initWithTitle:#"Message Failed" message:#"Message Failed" delegate:nil cancelButtonTitle:#"OK!" otherButtonTitles:nil, nil];
[alert1 show];
[alert1 release];
break;
case MessageComposeResultSent:
NSLog(#"Message Sent");
UIAlertView *alert2 = [[UIAlertView alloc] initWithTitle:#"Message Sent" message:#"Message Sent" delegate:nil cancelButtonTitle:#"OK!" otherButtonTitles:nil, nil];
[alert2 show];
[alert2 release];
break;
default:
break;
}
[self dismissViewControllerAnimated:YES completion:nil];
}
when I click on sendEmail button
the application crashes with this output
2013-10-31 12:57:14.095 MyApp[325:c07] toRecipentsEmail Count == 2
2013-10-31 12:57:21.952 MyApp[325:c07] -[__NSMallocBlock__ countByEnumeratingWithState:objects:count:]: unrecognized selector sent to instance 0xf2ad5e0
2013-10-31 12:57:21.953 MyApp[325:c07] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSMallocBlock__ countByEnumeratingWithState:objects:count:]: unrecognized selector sent to instance 0xf2ad5e0'
*** First throw call stack:
(0x1d65012 0x1a9ce7e 0x1df04bd 0x1d54bbc 0x1d5494e 0x346343 0x346523 0x59fde 0x1ab07055 0x9e42c0 0x9e4258 0xaa5021 0xaa557f 0xaa46e8 0xa13cef 0xa13f02 0x9f1d4a 0x9e3698 0x27d1df9 0x27d1ad0 0x1cdabf5 0x1cda962 0x1d0bbb6 0x1d0af44 0x1d0ae1b 0x27d07e3 0x27d0668 0x9e0ffc 0x2882 0x27b5)
libc++abi.dylib: terminate called throwing an exception
(lldb)
Please Tell me What Am i doing wrong ?

I'm not quite sure whether this is the issue, but from your code, the line
toRecipentsEmail = [NSMutableArray arrayWithArray:[textField.text componentsSeparatedByString:#","]];
which will just return an autoreleased copy of array, so just try
self.toRecipentsEmail = [NSMutableArray arrayWithArray:[textField.text componentsSeparatedByString:#","]] ;
Since you have synthesized the property toRecipentsEmail, doing this will retain a copy of array.

Related

iOS Next Button on Keyboard Not Working

I have found this solution to making the next buttons on the ui keyboard go to the next text field, however it is not working at all for me. Is there something I have to do in the storyboard as well? Also, how do I make the next button for the final textfield call the unwind segue? Thank you
//
// AddToDoItemViewController.m
// ToDoList
//
// Copyright (c) 2015 com.example. All rights reserved.
//
#import "AddToDoItemViewController.h"
#interface AddToDoItemViewController ()
#property (weak, nonatomic) IBOutlet UITextField *textField;
#property (weak, nonatomic) IBOutlet UIBarButtonItem *saveButton;
#property (weak, nonatomic) IBOutlet UITextField *totalTextField;
#property (weak, nonatomic) IBOutlet UITextField *tipTextField;
#property (weak, nonatomic) IBOutlet UIView *singleTableView;
#property (weak, nonatomic) IBOutlet UISwitch *ccSwitch;
#end
#implementation AddToDoItemViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(BOOL)textFieldShouldReturn:(UITextField *)textField
{
if (textField == self.textField) {
[self.totalTextField becomeFirstResponder];
}
else if (textField == self.totalTextField ) {
[self.tipTextField becomeFirstResponder];
}
else{
[textField resignFirstResponder];
}
return YES;
}
/*- (BOOL)textFieldShouldReturn:(UITextField *)theTextField {
if (theTextField == self.textField) {
[theTextField resignFirstResponder];
} else if (theTextField == self.totalTextField) {
[self.tipTextField becomeFirstResponder];
}
return YES;
}
*/
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender
{
NSLog(#"sender = %#",sender);
if (sender != self.saveButton) return YES;
NSNumberFormatter *formatter1 = [[NSNumberFormatter alloc] init];
NSNumber *totalOrder = [formatter1 numberFromString:self.totalTextField.text];
NSNumber *tipOrder = [formatter1 numberFromString:self.tipTextField.text];
double tot = totalOrder.doubleValue;
double totalOrderWithTip = tipOrder.doubleValue;
if(self.textField.text.length <= 0)
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error"
message:#"Field not entered"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
return NO;
}
if(self.totalTextField.text.length <= 0)
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error"
message:#"Field not entered"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
return NO;
}
if(self.tipTextField.text.length <= 0)
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error"
message:#"Field not entered"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
return NO;
}
if(totalOrder == nil)
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error"
message:#"Invalid Order Total"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
return NO;
}
if(tipOrder == nil)
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error"
message:#"Invalid Amount Recieved"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
return NO;
}
if(totalOrderWithTip < tot)
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error"
message:#"The amount recieved must be equal to or greater than the order total."
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
return NO;
}
return YES;
}
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if (sender != self.saveButton) return;
if (self.textField.text.length > 0) {
NSNumberFormatter *formatter1 = [[NSNumberFormatter alloc] init];
// NSNumber *tipPercent = [[NSNumber alloc] initWithFloat:0.0];
NSNumber *totalOrder = [formatter1 numberFromString:self.totalTextField.text];
NSNumber *tipOrder = [formatter1 numberFromString:self.tipTextField.text];
NSNumber *actualTip = [[NSNumber alloc] initWithFloat:tipOrder.doubleValue - totalOrder.doubleValue];
double tot = totalOrder.doubleValue;
double tip1 = tipOrder.doubleValue - tot;
self.toDoItem = [[ToDoItem alloc] init];
self.toDoItem.location = self.textField.text;
NSNumber *percent1 = [[NSNumber alloc] initWithDouble:(tip1/tot)*100.0];
if(self.ccSwitch.isOn)
self.toDoItem.isCreditCard = YES;
else
self.toDoItem.isCreditCard = NO;
self.toDoItem.total = totalOrder;
self.toDoItem.tip = actualTip;
self.toDoItem.percentage = percent1;
self.toDoItem.completed = NO;
}
}
#end
In the Connections Inspector for the textField, drag from the "circle" under the section "Outlets" - "delegate", to the View Controller Icon. Or either you can set it in the viewDidLoad method. Some like:
self.textField.delegate = self;
The last textField is closing the keyboard because your are telling the keyboard to resign its first responder condition. Just replace that line for the one that you want to call:
[self shouldPerformSegueWithIdentifier:#"YOUR_IDENTIFIER" sender:your_sender];
And that's it!

Mail Compose not dismiss when click cancel button

I have this code who send a e-mail to users, Im using MFMailCompose:
.h file
#import <UIKit/UIKit.h>
#import <MessageUI/MessageUI.h>
#import <MessageUI/MFMailComposeViewController.h>
#interface TestViewController : UIViewController <MFMailComposeViewControllerDelegate, MFMessageComposeViewControllerDelegate>
#property(nonatomic,assign) id<MFMailComposeViewControllerDelegate> mailComposeDelegate;
.m file
#synthesize mailComposeDelegate
-(void)sendEmail:(NSString*)valor{
//Valor receive the email
NSString *corpoMensagem = #"No have body yet...";
// From within your active view controller
if([MFMailComposeViewController canSendMail]) {
MFMailComposeViewController *mailCont = [[MFMailComposeViewController alloc] init];
mailCont.mailComposeDelegate = self;// Required to invoke mailComposeController when send
[mailCont setSubject:#"FazerBem - Recibo de Compra"];
[mailCont setToRecipients:[NSArray arrayWithObject:valor]];
[mailCont setMessageBody:corpoMensagem isHTML:YES];
[self presentViewController:mailCont animated:YES completion:nil];
}
}
- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error {
if (error){
NSString *errorTitle = #"Erro to send";
NSString *errorDescription = [error localizedDescription];
UIAlertView *errorView = [[UIAlertView alloc]initWithTitle:errorTitle message:errorDescription delegate:self cancelButtonTitle:nil otherButtonTitles:#"OK", nil];
[errorView show];
}
[self becomeFirstResponder];
[self dismissViewControllerAnimated:YES completion:nil];
}
-(void)messageComposeViewController:(MFMessageComposeViewController *)controller didFinishWithResult:(MessageComposeResult)result{
}
when I click the send button he can send the email to the recipient and can close the screen, now when I click the cancel button, it opens 2 more options 'delete draft' and 'save draft' when I click on one of the app crashes and returns me the following error:
[TestViewController respondsToSelector:]: message sent to deallocated instance 0x16b3b470
How I can solve this problem?
Use Switch case to for performing actions:
- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error
{
UIAlertView *alert;
switch (result)
{
case MFMailComposeResultCancelled:
break;
case MFMailComposeResultSaved:
alert = [[UIAlertView alloc] initWithTitle:#"Draft Saved" message:#"Composed Mail is saved in draft." delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil];
[alert show];
break;
case MFMailComposeResultSent:
alert = [[UIAlertView alloc] initWithTitle:#"Success" message:#"You have successfully referred your friends." delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil];
[alert show];
break;
case MFMailComposeResultFailed:
alert = [[UIAlertView alloc] initWithTitle:#"Failed" message:#"Sorry! Failed to send." delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil];
[alert show];
break;
default:
break;
}
// Close the Mail Interface
[self dismissViewControllerAnimated:YES completion:nil];
}
Remove this line:
[self becomeFirstResponder];
That should fix your problem.
If above solution didn't work for you as it was for me, here, instead of self use instance of MFMailComposeViewController:
Instead Of
[self dismissViewControllerAnimated:YES completion:nil];
Use
[mailComposer dismissViewControllerAnimated:YES completion:nil];

object cannot be nil - On a segue?

All I want to do in my code is move from one view to another. No matter how many different ways I try to go around it, any segue or any change from the current view causes this error:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException',
reason: '*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil'
There's no indication of what object it's talking about at all, and the app is crashing on my segue line. Oh and I'm using Xcode 5-DP and iOS 7. Here's my source:
LoginViewController.h
#import <UIKit/UIKit.h>
#import "ECSlidingViewController.h"
#import "MenuViewController.h"
#import <MessageUI/MessageUI.h>
#import <CoreData/CoreData.h>
#interface LoginViewController : UIViewController <UITextFieldDelegate>
#property (weak, nonatomic) IBOutlet UITextField *userTextField;
#property (weak, nonatomic) IBOutlet UITextField *passwordTextField;
- (IBAction)signupTouched:(UIButton *)sender;
- (IBAction)logInPressed:(id)sender;
- (IBAction)backgroundTouched:(id)sender;
- (IBAction)revealMenu:(id)sender;
#end
LoginViewController.m (Exception on line 8)
#import "LoginViewController.h"
#import "RegisterView.h"
#import <Parse/Parse.h>
#implementation LoginViewController
- (IBAction)signupTouched:(UIButton *)sender {
[self performSegueWithIdentifier:#"signup" sender:self];
// THE APPLICATION CRASHES ON THIS LINE ABOVE
}
//Login button pressed
-(IBAction)logInPressed:(id)sender
{
//If user logged succesful:
//[self performSegueWithIdentifier:#"LoginSuccesful" sender:self];
if (![self.userTextField.text isEqual:#""]) {
[PFUser logInWithUsernameInBackground:self.userTextField.text password:self.passwordTextField.text block:^(PFUser *user, NSError *error) {
if (user) {
//Open the wall
//[self performSegueWithIdentifier:#"LoginSuccesful" sender:self];
UIAlertView *loginAlertView = [[UIAlertView alloc] initWithTitle:#"Great!" message:#"You have logged in" delegate:nil cancelButtonTitle:#"Get roaming" otherButtonTitles:nil, nil];
[loginAlertView show];
} else {
//Something bad has ocurred
NSString *errorString = [[error userInfo] objectForKey:#"error"];
UIAlertView *errorAlertView = [[UIAlertView alloc] initWithTitle:#"Error" message:errorString delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil];
[errorAlertView show];
}
}];
} else {
//Something bad has ocurred
NSString *errorString = #"You did not type any credentials!";
UIAlertView *errorAlertView = [[UIAlertView alloc] initWithTitle:#"Error" message:errorString delegate:nil cancelButtonTitle:#"Okay" otherButtonTitles:nil, nil];
[errorAlertView show];
}
}
- (IBAction)backgroundTouched:(id)sender {
[self.userTextField resignFirstResponder];
[self.passwordTextField resignFirstResponder];
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
[textField resignFirstResponder];
return NO;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if (![self.slidingViewController.underLeftViewController isKindOfClass:[MenuViewController class]]) {
self.slidingViewController.underLeftViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"Menu"];
}
self.slidingViewController.underRightViewController = nil;
[self.view addGestureRecognizer:self.slidingViewController.panGesture];
}
- (IBAction)revealMenu:(id)sender
{
[self.slidingViewController anchorTopViewTo:ECRight];
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.userTextField.delegate = self;
self.passwordTextField.delegate = self;
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)viewDidUnload {
[self setUserTextField:nil];
[self setPasswordTextField:nil];
[super viewDidUnload];
}
#end
I've googled, and I've googled. No-one else seems to be having an issue like this. I tried cleaning out my entire iOS Simulator and everything to no avail, following a solution that appeared to work for others. I've tried every type of segue possible and none are working.
Thanks in advance!
Recheck your configuration and use of the ECSlidingViewController, and it's topViewController.
Check in signup.xib if the UIView is connected to an outlet.

Game Center Integration and Program Design

I'm new to iOS so forgive a question that might seem obvious to most of you.
I've read the Game Center Programming Guide
but I am confused about the flow between and among view controllers.
Using figure 1-2 in the above link as an example, I can see that the Credits view, Authentication view, etc will be modal. What I can't get my head around is the loop of views: Main Menu > Configure Game Play > Matchmaking > Configure Game Play > Game Play > Game End > Main Menu.
In this scenario, What would be considered the root controller? What type of controller would it be? What segue would you use to go to the next view and how would you navigate back to the Main Menu once you are several views deep? What would be a typical design for this scenario?
This is the code for multiplayer. All Game center views are managed by Game Center .
Your Just add following code for multiplayer after adding this code. you will call this helper class when you click on Game button and you can visit this link for more details [ http://www.raywenderlich.com/3276/how-to-make-a-simple-multiplayer-game-with-game-center-tutorial-part-12 ]
AppController * delegate = (AppController *) [UIApplication sharedApplication].delegate;
[[GCHelper sharedInstance] findMatchWithMinPlayers:2 maxPlayers:2 viewController:delegate.viewController];
pimple_->ourRandom = arc4random();
setGameState(kGameStateWaitingForMatch);
#import <Foundation/Foundation.h>
#import <GameKit/Gamekit.h>
#interface GCHelper : NSObject<GKMatchmakerViewControllerDelegate, GKMatchDelegate>
{
BOOL isUserAuthenticated;
UIViewController *presentingViewController;
GKMatch *match;
BOOL matchStarted;
GKInvite *pendingInvite;
NSArray *pendingPlayersToInvite;
NSMutableDictionary *playersDict;
NSString *MultiplayerID;
NSData *MultiData;
NSString *otherPlayerID;
char AlertMessageBoxNo;
BOOL isDataRecieved;
}
//variables
#property (assign, readonly) BOOL gameCenterAvailable;
#property (retain) UIViewController *presentingViewController;
#property (retain) GKMatch *match;
#property (retain) GKInvite *pendingInvite;
#property (retain) NSArray *pendingPlayersToInvite;
#property (retain) NSMutableDictionary *playersDict;
#property (retain) NSString *MultiplayerID;
#property (retain) NSData *MultiData;
-(NSString*)getOtherPlayerId;
-(void)setOtherPlayerId;
//Functions
+ (GCHelper *)sharedInstance;
-(BOOL)isGameCenterAvailable;
-(void)authenticationChanged;
-(void)authenticateLocalUser;
-(void)gameOver:(NSString*)message;
-(void)setDataRecieved:(BOOL)d;
-(BOOL)getDataRecieved;
- (void)findMatchWithMinPlayers:(int)minPlayers maxPlayers:(int)maxPlayers viewController:(UIViewController *)viewController;
#end
///////
#import "GCHelper.h"
#import "IPadSharebleClass.h"
#implementation GCHelper
#synthesize gameCenterAvailable;
#synthesize presentingViewController;
#synthesize match;
#synthesize pendingInvite;
#synthesize pendingPlayersToInvite;
#synthesize playersDict;
#synthesize MultiData;
#synthesize MultiplayerID;
static GCHelper *sharedHelper = nil;
+(GCHelper *) sharedInstance
{
if (!sharedHelper)
{
sharedHelper = [[GCHelper alloc] init];
}
return sharedHelper;
}
- (BOOL)isGameCenterAvailable
{
Class gcClass = (NSClassFromString(#"GKLocalPlayer"));
NSString *reqSysVer = #"4.1";
NSString *currSysVer = [[UIDevice currentDevice] systemVersion];
BOOL osVersionSupported = ([currSysVer compare:reqSysVer options:NSNumericSearch] != NSOrderedAscending);
return (gcClass && osVersionSupported);
}
- (id)init
{
if ((self = [super init]))
{
gameCenterAvailable = [self isGameCenterAvailable];
if (gameCenterAvailable)
{
NSNotificationCenter *nc =
[NSNotificationCenter defaultCenter];
[nc addObserver:self
selector:#selector(authenticationChanged)
name:GKPlayerAuthenticationDidChangeNotificationName
object:nil];
}
else
{
UIAlertView* alert=[[UIAlertView alloc]initWithTitle:#"Game Center Alert" message:#"Game Center Not Available" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alert show];
[alert release];
}
}
return self;
}
-(void)authenticationChanged
{
if ([GKLocalPlayer localPlayer].isAuthenticated && !isUserAuthenticated)
{
NSLog(#"Authentication changed: player authenticated.");
isUserAuthenticated = TRUE;
[GKMatchmaker sharedMatchmaker].inviteHandler = ^(GKInvite *acceptedInvite, NSArray *playersToInvite)
{
NSLog(#"Received invite");
self.pendingInvite = acceptedInvite;
self.pendingPlayersToInvite = playersToInvite;
IPadCallAnyWhereF.inviteReceived();
};
}
else if (![GKLocalPlayer localPlayer].isAuthenticated && isUserAuthenticated)
{
NSLog(#"Authentication changed: player not authenticated");
isUserAuthenticated = FALSE;
}
}
- (void)authenticateLocalUser
{
if (!gameCenterAvailable) return;
NSLog(#"Authenticating local user...");
if ([GKLocalPlayer localPlayer].authenticated == NO)
{
[[GKLocalPlayer localPlayer] authenticateWithCompletionHandler:nil];
}
else
{
NSLog(#"Already authenticated!");
}
}
-(void)findMatchWithMinPlayers:(int)minPlayers maxPlayers:(int)maxPlayers viewController:(UIViewController *)viewController
{
if (!gameCenterAvailable) return;
matchStarted = NO;
self.match = nil;
self.presentingViewController = viewController;
if (pendingInvite != nil)
{
[presentingViewController dismissModalViewControllerAnimated:NO];
GKMatchmakerViewController *mmvc = [[[GKMatchmakerViewController alloc] initWithInvite:pendingInvite] autorelease];
mmvc.matchmakerDelegate = self;
[presentingViewController presentModalViewController:mmvc animated:YES];
self.pendingInvite = nil;
self.pendingPlayersToInvite = nil;
}
else
{
[presentingViewController dismissModalViewControllerAnimated:NO];
GKMatchRequest *request = [[[GKMatchRequest alloc] init] autorelease];
request.minPlayers = minPlayers;
request.maxPlayers = maxPlayers;
request.playersToInvite = pendingPlayersToInvite;
GKMatchmakerViewController *mmvc = [[[GKMatchmakerViewController alloc] initWithMatchRequest:request] autorelease];
mmvc.matchmakerDelegate = self;
[presentingViewController presentModalViewController:mmvc animated:YES];
self.pendingInvite = nil;
self.pendingPlayersToInvite = nil;
}
}
#pragma mark GKMatchmakerViewControllerDelegate
- (void)matchmakerViewControllerWasCancelled:(GKMatchmakerViewController *)viewController
{
[presentingViewController dismissModalViewControllerAnimated:YES];
UIAlertView* alert=[[UIAlertView alloc]initWithTitle:#"Game Center Alert" message:#"Game Cancel By you" delegate:self cancelButtonTitle:#"Try Again" otherButtonTitles:#"Main Menu", nil];
[alert show];
[alert release];
AlertMessageBoxNo='E';
}
- (void)matchmakerViewController:(GKMatchmakerViewController *)viewController didFailWithError:(NSError *)error
{
[presentingViewController dismissModalViewControllerAnimated:YES];
NSLog(#"Error finding match: %#", error.localizedDescription);
UIAlertView* alert=[[UIAlertView alloc]initWithTitle:#"Game Center Alert" message:#"Connection Time out" delegate:self cancelButtonTitle:#"Try Again" otherButtonTitles:#"Main Menu", nil];
[alert show];
[alert release];
AlertMessageBoxNo='A';
}
- (void)matchmakerViewController:(GKMatchmakerViewController *)viewController didFindMatch:(GKMatch *)theMatch
{
[presentingViewController dismissModalViewControllerAnimated:YES];
self.match = theMatch;
match.delegate = self;
if (!matchStarted && match.expectedPlayerCount == 0)
{
NSLog(#"***************Ready to start match!**************");
[self lookupPlayers];
}
}
- (void)lookupPlayers
{
NSLog(#"Looking up %d players...", match.playerIDs.count);
[GKPlayer loadPlayersForIdentifiers:match.playerIDs withCompletionHandler:^(NSArray *players, NSError *error)
{
if (error != nil)
{
NSLog(#"Error retrieving player info: %#", error.localizedDescription);
matchStarted = NO;
//IPadCallAnyWhereF.matchEnded();
UIAlertView* alert=[[UIAlertView alloc]initWithTitle:#"Game Center Alert" message:#"Error retrieving player info" delegate:self cancelButtonTitle:#"Try Again" otherButtonTitles:#"Main Menu", nil];
[alert show];
[alert release];
AlertMessageBoxNo='F';
}
else
{
self.playersDict = [NSMutableDictionary dictionaryWithCapacity:players.count];
for (GKPlayer *player in players)
{
NSLog(#"Found player: %#", player.alias);
[playersDict setObject:player forKey:player.playerID];
}
NSLog(#"Total Number of Players : %d",players.count);
matchStarted = YES;
IPadCallAnyWhereF.matchStarted();
}
}];
}
#pragma mark GKMatchDelegate
- (void)match:(GKMatch *)theMatch didReceiveData:(NSData *)data fromPlayer:(NSString *)playerID
{
if (match != theMatch) return;
MultiData=data;
MultiplayerID=playerID;
if(otherPlayerID==nil)
{
otherPlayerID=[playerID retain];
}
IPadCallAnyWhereF.match();
}
-(void)setDataRecieved:(BOOL)d
{
isDataRecieved=d;
}
-(BOOL)getDataRecieved
{
return isDataRecieved;
}
-(NSString*)getOtherPlayerId
{
return otherPlayerID;
}
-(void)setOtherPlayerId
{
otherPlayerID=nil;
}
- (void)match:(GKMatch *)theMatch player:(NSString *)playerID didChangeState:(GKPlayerConnectionState)state
{
if (match != theMatch) return;
switch (state)
{
case GKPlayerStateConnected:
NSLog(#"New Player connected!");
if (!matchStarted && theMatch.expectedPlayerCount == 0)
{
NSLog(#"&&&&&&&&&& Ready to start match in the match!");
[self lookupPlayers];
}
break;
case GKPlayerStateDisconnected:
NSLog(#"--------Player disconnected!--------");
matchStarted = NO;
UIAlertView* alert=[[UIAlertView alloc]initWithTitle:#"Game Center Alert" message:#"Player Disconnected" delegate:self cancelButtonTitle:#"Try Again" otherButtonTitles:#"Main Menu", nil];
[alert show];
[alert release];
AlertMessageBoxNo='B';
//IPadCallAnyWhereF.matchDisconnect();
break;
}
}
- (void)match:(GKMatch *)theMatch connectionWithPlayerFailed:(NSString *)playerID withError:(NSError *)error
{
if (match != theMatch) return;
NSLog(#"Failed to connect to player with error: %#", error.localizedDescription);
matchStarted = NO;
//IPadCallAnyWhereF.matchEnded();
UIAlertView* alert=[[UIAlertView alloc]initWithTitle:#"Game Center Alert" message:#"Failed to connect to player" delegate:self cancelButtonTitle:#"Try Again" otherButtonTitles:#"Main Menu", nil];
[alert show];
[alert release];
AlertMessageBoxNo='C';
}
- (void)match:(GKMatch *)theMatch didFailWithError:(NSError *)error
{
if (match != theMatch) return;
NSLog(#"Match failed with error: %#", error.localizedDescription);
matchStarted = NO;
//IPadCallAnyWhereF.matchEnded();
UIAlertView* alert=[[UIAlertView alloc]initWithTitle:#"Game Center Alert" message:#"Match failed" delegate:self cancelButtonTitle:#"Try Again" otherButtonTitles:#"Main Menu", nil];
[alert show];
[alert release];
AlertMessageBoxNo='D';
}
-(void)gameOver:(NSString*)message
{
UIAlertView* alert=[[UIAlertView alloc]initWithTitle:#"Game Center Alert" message:message delegate:self cancelButtonTitle:#"Try Again" otherButtonTitles:#"Main Menu", nil];
[alert show];
[alert release];
AlertMessageBoxNo='G';
}
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
NSString *title = [alertView buttonTitleAtIndex:buttonIndex];
if([title isEqualToString:#"Try Again"])
{
IPadCallAnyWhereF.matchDisconnect();
}
else if([title isEqualToString:#"Main Menu"])
{
IPadCallAnyWhereF.gotoMainMenu();
}
}
#end
Thanks

Why iOS GameCenter multiplayer real-time match each time creates new threads?

I created absolutely simple multiplayer app which just finds the match (with only 1 opponent) and terminates it in 5 seconds:
-(void)matchmakerViewController:(GKMatchmakerViewController *)viewController
didFindMatch:(GKMatch *)match {
[self.presentingViewController dismissModalViewControllerAnimated:YES];
self.match = match;
self.match.delegate = self;
if (!self.matchStarted && self.match.expectedPlayerCount == 0)
{
NSLog(#"Ready to start match!");
//[self lookupPlayers];
// My Own Test Code Begin
if (!self.delegate) NSLog(#"No delegate on match invite.");
// Notify delegate match can begin
self.matchStarted = YES;
[self.delegate matchStarted];
// My Own Test Code End
}
}
And some methods in AppDelegate:
-(void)matchStarted
{
CCLOG(#"Match started. Delay 5 seconds...");
[self performSelector:#selector(matchEnded) withObject:nil afterDelay:5];
}
-(void)matchEnded
{
CCLOG(#"Match ended");
[[GameCenterMatchHelper sharedInstance].match disconnect];
[GameCenterMatchHelper sharedInstance].match = nil;
}
All works fine for the first match. But when the match is finished there are 3 additional threads (I can see them pausing execution):
Thread com.apple.gamekitservices.gcksession.recvproc
Thread com.apple.gamekitservices.gcksession.sendproc
Thread com.apple.gamekitservices.eventcallback.eventcbproc
And if I start two matches - there are already 6 (3 and 3) threads after match finish.
And the main reason why it is so bad - app crashes and it is like all players are disconnected.
I use iPod touch 4g and iPad 2 for tests with the newest iOS 6. And these threads are created at both devices.
I thought it is because I use a singleton-class for GCHelper and all delegates but I tryed to extract delegates to other one-time-using classes - each time the additional threads appear.
Maybe smb know how can I fix it?
I think this is helpful for you.
#import <Foundation/Foundation.h>
#import <GameKit/Gamekit.h>
#interface GCHelper : NSObject<GKMatchmakerViewControllerDelegate, GKMatchDelegate>
{
BOOL isUserAuthenticated;
UIViewController *presentingViewController;
GKMatch *match;
BOOL matchStarted;
GKInvite *pendingInvite;
NSArray *pendingPlayersToInvite;
NSMutableDictionary *playersDict;
NSString *MultiplayerID;
NSData *MultiData;
NSString *otherPlayerID;
char AlertMessageBoxNo;
BOOL isDataRecieved;
}
//variables
#property (assign, readonly) BOOL gameCenterAvailable;
#property (retain) UIViewController *presentingViewController;
#property (retain) GKMatch *match;
#property (retain) GKInvite *pendingInvite;
#property (retain) NSArray *pendingPlayersToInvite;
#property (retain) NSMutableDictionary *playersDict;
#property (retain) NSString *MultiplayerID;
#property (retain) NSData *MultiData;
-(NSString*)getOtherPlayerId;
-(void)setOtherPlayerId;
//Functions
+ (GCHelper *)sharedInstance;
-(BOOL)isGameCenterAvailable;
-(void)authenticationChanged;
-(void)authenticateLocalUser;
-(void)gameOver:(NSString*)message;
-(void)setDataRecieved:(BOOL)d;
-(BOOL)getDataRecieved;
- (void)findMatchWithMinPlayers:(int)minPlayers maxPlayers:(int)maxPlayers viewController:(UIViewController *)viewController;
#end
/////////
#import "GCHelper.h"
#import "IPadSharebleClass.h"
#implementation GCHelper
#synthesize gameCenterAvailable;
#synthesize presentingViewController;
#synthesize match;
#synthesize pendingInvite;
#synthesize pendingPlayersToInvite;
#synthesize playersDict;
#synthesize MultiData;
#synthesize MultiplayerID;
static GCHelper *sharedHelper = nil;
+(GCHelper *) sharedInstance
{
if (!sharedHelper)
{
sharedHelper = [[GCHelper alloc] init];
}
return sharedHelper;
}
- (BOOL)isGameCenterAvailable
{
Class gcClass = (NSClassFromString(#"GKLocalPlayer"));
NSString *reqSysVer = #"4.1";
NSString *currSysVer = [[UIDevice currentDevice] systemVersion];
BOOL osVersionSupported = ([currSysVer compare:reqSysVer options:NSNumericSearch] != NSOrderedAscending);
return (gcClass && osVersionSupported);
}
- (id)init
{
if ((self = [super init]))
{
gameCenterAvailable = [self isGameCenterAvailable];
if (gameCenterAvailable)
{
NSNotificationCenter *nc =
[NSNotificationCenter defaultCenter];
[nc addObserver:self
selector:#selector(authenticationChanged)
name:GKPlayerAuthenticationDidChangeNotificationName
object:nil];
}
else
{
UIAlertView* alert=[[UIAlertView alloc]initWithTitle:#"Game Center Alert" message:#"Game Center Not Available" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alert show];
[alert release];
}
}
return self;
}
-(void)authenticationChanged
{
if ([GKLocalPlayer localPlayer].isAuthenticated && !isUserAuthenticated)
{
NSLog(#"Authentication changed: player authenticated.");
isUserAuthenticated = TRUE;
[GKMatchmaker sharedMatchmaker].inviteHandler = ^(GKInvite *acceptedInvite, NSArray *playersToInvite)
{
NSLog(#"Received invite");
self.pendingInvite = acceptedInvite;
self.pendingPlayersToInvite = playersToInvite;
IPadCallAnyWhereF.inviteReceived();
};
}
else if (![GKLocalPlayer localPlayer].isAuthenticated && isUserAuthenticated)
{
NSLog(#"Authentication changed: player not authenticated");
isUserAuthenticated = FALSE;
}
}
- (void)authenticateLocalUser
{
if (!gameCenterAvailable) return;
NSLog(#"Authenticating local user...");
if ([GKLocalPlayer localPlayer].authenticated == NO)
{
[[GKLocalPlayer localPlayer] authenticateWithCompletionHandler:nil];
}
else
{
NSLog(#"Already authenticated!");
}
}
-(void)findMatchWithMinPlayers:(int)minPlayers maxPlayers:(int)maxPlayers viewController:(UIViewController *)viewController
{
if (!gameCenterAvailable) return;
matchStarted = NO;
self.match = nil;
self.presentingViewController = viewController;
if (pendingInvite != nil)
{
[presentingViewController dismissModalViewControllerAnimated:NO];
GKMatchmakerViewController *mmvc = [[[GKMatchmakerViewController alloc] initWithInvite:pendingInvite] autorelease];
mmvc.matchmakerDelegate = self;
[presentingViewController presentModalViewController:mmvc animated:YES];
self.pendingInvite = nil;
self.pendingPlayersToInvite = nil;
}
else
{
[presentingViewController dismissModalViewControllerAnimated:NO];
GKMatchRequest *request = [[[GKMatchRequest alloc] init] autorelease];
request.minPlayers = minPlayers;
request.maxPlayers = maxPlayers;
request.playersToInvite = pendingPlayersToInvite;
GKMatchmakerViewController *mmvc = [[[GKMatchmakerViewController alloc] initWithMatchRequest:request] autorelease];
mmvc.matchmakerDelegate = self;
[presentingViewController presentModalViewController:mmvc animated:YES];
self.pendingInvite = nil;
self.pendingPlayersToInvite = nil;
}
}
#pragma mark GKMatchmakerViewControllerDelegate
- (void)matchmakerViewControllerWasCancelled:(GKMatchmakerViewController *)viewController
{
[presentingViewController dismissModalViewControllerAnimated:YES];
UIAlertView* alert=[[UIAlertView alloc]initWithTitle:#"Game Center Alert" message:#"Game Cancel By you" delegate:self cancelButtonTitle:#"Try Again" otherButtonTitles:#"Main Menu", nil];
[alert show];
[alert release];
AlertMessageBoxNo='E';
}
- (void)matchmakerViewController:(GKMatchmakerViewController *)viewController didFailWithError:(NSError *)error
{
[presentingViewController dismissModalViewControllerAnimated:YES];
NSLog(#"Error finding match: %#", error.localizedDescription);
UIAlertView* alert=[[UIAlertView alloc]initWithTitle:#"Game Center Alert" message:#"Connection Time out" delegate:self cancelButtonTitle:#"Try Again" otherButtonTitles:#"Main Menu", nil];
[alert show];
[alert release];
AlertMessageBoxNo='A';
}
- (void)matchmakerViewController:(GKMatchmakerViewController *)viewController didFindMatch:(GKMatch *)theMatch
{
[presentingViewController dismissModalViewControllerAnimated:YES];
self.match = theMatch;
match.delegate = self;
if (!matchStarted && match.expectedPlayerCount == 0)
{
NSLog(#"***************Ready to start match!**************");
[self lookupPlayers];
}
}
- (void)lookupPlayers
{
NSLog(#"Looking up %d players...", match.playerIDs.count);
[GKPlayer loadPlayersForIdentifiers:match.playerIDs withCompletionHandler:^(NSArray *players, NSError *error)
{
if (error != nil)
{
NSLog(#"Error retrieving player info: %#", error.localizedDescription);
matchStarted = NO;
//IPadCallAnyWhereF.matchEnded();
UIAlertView* alert=[[UIAlertView alloc]initWithTitle:#"Game Center Alert" message:#"Error retrieving player info" delegate:self cancelButtonTitle:#"Try Again" otherButtonTitles:#"Main Menu", nil];
[alert show];
[alert release];
AlertMessageBoxNo='F';
}
else
{
self.playersDict = [NSMutableDictionary dictionaryWithCapacity:players.count];
for (GKPlayer *player in players)
{
NSLog(#"Found player: %#", player.alias);
[playersDict setObject:player forKey:player.playerID];
}
NSLog(#"Total Number of Players : %d",players.count);
matchStarted = YES;
IPadCallAnyWhereF.matchStarted();
}
}];
}
#pragma mark GKMatchDelegate
- (void)match:(GKMatch *)theMatch didReceiveData:(NSData *)data fromPlayer:(NSString *)playerID
{
if (match != theMatch) return;
MultiData=data;
MultiplayerID=playerID;
if(otherPlayerID==nil)
{
otherPlayerID=[playerID retain];
}
IPadCallAnyWhereF.match();
}
-(void)setDataRecieved:(BOOL)d
{
isDataRecieved=d;
}
-(BOOL)getDataRecieved
{
return isDataRecieved;
}
-(NSString*)getOtherPlayerId
{
return otherPlayerID;
}
-(void)setOtherPlayerId
{
otherPlayerID=nil;
}
- (void)match:(GKMatch *)theMatch player:(NSString *)playerID didChangeState:(GKPlayerConnectionState)state
{
if (match != theMatch) return;
switch (state)
{
case GKPlayerStateConnected:
NSLog(#"New Player connected!");
if (!matchStarted && theMatch.expectedPlayerCount == 0)
{
NSLog(#"&&&&&&&&&& Ready to start match in the match!");
[self lookupPlayers];
}
break;
case GKPlayerStateDisconnected:
NSLog(#"--------Player disconnected!--------");
matchStarted = NO;
UIAlertView* alert=[[UIAlertView alloc]initWithTitle:#"Game Center Alert" message:#"Player Disconnected" delegate:self cancelButtonTitle:#"Try Again" otherButtonTitles:#"Main Menu", nil];
[alert show];
[alert release];
AlertMessageBoxNo='B';
//IPadCallAnyWhereF.matchDisconnect();
break;
}
}
- (void)match:(GKMatch *)theMatch connectionWithPlayerFailed:(NSString *)playerID withError:(NSError *)error
{
if (match != theMatch) return;
NSLog(#"Failed to connect to player with error: %#", error.localizedDescription);
matchStarted = NO;
//IPadCallAnyWhereF.matchEnded();
UIAlertView* alert=[[UIAlertView alloc]initWithTitle:#"Game Center Alert" message:#"Failed to connect to player" delegate:self cancelButtonTitle:#"Try Again" otherButtonTitles:#"Main Menu", nil];
[alert show];
[alert release];
AlertMessageBoxNo='C';
}
- (void)match:(GKMatch *)theMatch didFailWithError:(NSError *)error
{
if (match != theMatch) return;
NSLog(#"Match failed with error: %#", error.localizedDescription);
matchStarted = NO;
//IPadCallAnyWhereF.matchEnded();
UIAlertView* alert=[[UIAlertView alloc]initWithTitle:#"Game Center Alert" message:#"Match failed" delegate:self cancelButtonTitle:#"Try Again" otherButtonTitles:#"Main Menu", nil];
[alert show];
[alert release];
AlertMessageBoxNo='D';
}
-(void)gameOver:(NSString*)message
{
UIAlertView* alert=[[UIAlertView alloc]initWithTitle:#"Game Center Alert" message:message delegate:self cancelButtonTitle:#"Try Again" otherButtonTitles:#"Main Menu", nil];
[alert show];
[alert release];
AlertMessageBoxNo='G';
}
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
NSString *title = [alertView buttonTitleAtIndex:buttonIndex];
if([title isEqualToString:#"Try Again"])
{
IPadCallAnyWhereF.matchDisconnect();
}
else if([title isEqualToString:#"Main Menu"])
{
IPadCallAnyWhereF.gotoMainMenu();
}
}
#end
the right answer is that XCode doesn't show a real thread picture. It shows a lot of different threads but if I try to see them in profiler then I understand there are no additional threads. So I should suggest, no problem with threads but there is a problem with debug process. As a topic starter I think it is closed.
Thank you all.

Resources