Game Center Integration and Program Design - ios

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

Related

UIAlertView button action not working

I have one alert view and when I click on yes button it is supposed to produce another alert view and a toast message,but it is not happening. I couldn't figure it out. Here is my code:
-(void)myMethod {
UIAlertView *saveAlert = [[UIAlertView alloc] initWithTitle:#"First Message"
message:#"My First message"
delegate:nil
cancelButtonTitle:#"No"
otherButtonTitles:#"Yes", nil];
saveAlert.tag=0;
[saveAlert performSelectorOnMainThread:#selector(show) withObject:nil waitUntilDone:NO];
}
This is the method I am using to provide the functionality for different alert views.
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if(alertView.tag==0) {
if (buttonIndex == 0)
{
//Code for Cancel button
}
if (buttonIndex == 1)
{
//code for yes button
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.navigationController.view animated:YES];
hud.mode = MBProgressHUDModeText;
hud.labelText = #"Successfully displayed First Message";
hud.margin = 10.f;
hud.yOffset = 150.f;
hud.removeFromSuperViewOnHide = YES;
[hud hide:YES afterDelay:3];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Second Message"
message:#"My second message"
delegate:nil
cancelButtonTitle:#"No"
otherButtonTitles:#"Yes",nil];
alert.tag=1;
[alert performSelectorOnMainThread:#selector(show) withObject:nil waitUntilDone:YES];
}
}
if (alertView.tag==1) {
if (buttonIndex == 0)
{
//Code for Cancel button
}
if (buttonIndex == 1)
{
//Code for yes Button
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.navigationController.view animated:YES];
hud.mode = MBProgressHUDModeText;
hud.labelText = #"Succesfully displayed Second Message";
hud.margin = 10.f;
hud.yOffset = 150.f;
hud.removeFromSuperViewOnHide = YES;
[hud hide:YES afterDelay:3];
}
}
}
Can anyone help in finding the issue. Why I cannot get my second alert after clicking yes button in first alert?
You have not set the delegate for your UIAlertView and also make sure your delegate conforms to UIAlertViewDelegate protocol. Find the code snippet below.
You controller conforms to UIAlertViewDelegate protocol:
#interface YourViewController : UIViewController <UIAlertViewDelegate>
Create UIAlertView and set the deleagte:
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"First Message"
message:#"Show second message"
delegate:self
cancelButtonTitle:#"No"
otherButtonTitles:#"Yes", nil];
[alertView show];
Implement UIAlertViewDelegate delegate method:
- (void) alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if( 0 == buttonIndex ){ //cancel button
[alertView dismissWithClickedButtonIndex:buttonIndex animated:YES];
} else if ( 1 == buttonIndex ){
[alertView dismissWithClickedButtonIndex:buttonIndex animated:YES];
UIAlertView * secondAlertView = [[UIAlertView alloc] initWithTitle:#"Second Message"
message:#"Displaying second message"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[secondAlertView show];
}
}
You are specifying nil as the delegate for your alert views. You need to specify an object so the alertView:clickedButtonAtIndex: method can be called!
If you'd like to handle this more clear, you could use a block-based AlertView.
Create new file->Subclass of->UIAlertView
SuperAlertView.h
#import <UIKit/UIKit.h>
#class MySuperAlertView;
typedef void (^MySuperAlertViewBlock) (MySuperAlertView *alertView);
#interface MySuperAlertView : UIAlertView
- (instancetype) initWithTitle:(NSString *)title message:(NSString *)message;
- (void) addButtonWithTitle:(NSString *)buttonTitle block:(MySuperAlertViewBlock) block;
#end
SuperAlertView.m
#import "MySuperAlertView.h"
#interface MySuperAlertView()<UIAlertViewDelegate>
#property NSMutableArray *blocks;
#end
#implementation MySuperAlertView
- (instancetype)initWithTitle:(NSString *)title message:(NSString *)message
{
if (self = [super initWithTitle:title message:message delegate:self cancelButtonTitle:nil otherButtonTitles:nil])
{
self.blocks = [NSMutableArray array];
}
return self;
}
- (void)addButtonWithTitle:(NSString *)buttonTitle block:(MySuperAlertViewBlock)block
{
[self addButtonWithTitle:buttonTitle];
[self.blocks addObject:block ? [block copy] : [NSNull null]];
}
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
{
MySuperAlertViewBlock block = self.blocks[buttonIndex];
if ((id) block != [NSNull null]){
block(self);
}
}
#end
Usage:
MySuperAlertView *alertView = [[MySuperAlertView alloc] initWithTitle:#"Info" message:NSLocalizedString(#"EMAIL_SENT_SUCCESSFULL", nil)];
[alertView addButtonWithTitle:#"Ok" block:^(MySupertAlertView *alertView) {
// handle result from ok button here
}];
[alertView addButtonWithTitle:#"cancel" block:NULL];
dispatch_async(dispatch_get_main_queue(), ^{
[alertView show];
});

Update ProgressView after the view controller has been changed

I currently have a progress view which shows the current amount of how much of a file has been downloaded so far. When the user clicks the download button the progress succesfully updates and when they leave the view controller and return to it while it is still downloading the progress still updates successfuly. However my problem is when the download finishes and the user has left and returned to the view the progress view no longer responds to any updates. I have did some research into multi-threading and seen that many people suggest to do any UI updates in the main thread like so:
[self performSelectorOnMainThread:#selector(progressUpdate) withObject:nil waitUntilDone:NO];
So I tried it in my code and the still got the same results (once the user leaves and returns to the view controller after the download has finished the progress view does not respond to any updates). I then added a NSLog to see if the method was being called and the debugger outputted the NSLog. So what is going on? Here is the code for the view controller:
VideoTest.m
#import "VideoTest.h"
#import "AppDelegate.h"
#import "FileDownloadInfo.h"
#interface VideoTest ()
#property (nonatomic, strong) NSURLSession *session;
#property (nonatomic, strong) NSMutableArray *arrFileDownloadData;
#property (nonatomic, strong) NSURL *docDirectoryURL;
-(void)initializeFileDownloadDataArray;
-(int)getFileDownloadInfoIndexWithTaskIdentifier:(unsigned long)taskIdentifier;
#end
#implementation VideoTest{
NSString *url;
}
#synthesize moviePlayer;
#synthesize download;
#synthesize videoAlreadyPlaying, progressView;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
//Setting video URl
AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
url = #"http://therosary.info/AppVideos/TheChaplet/Information%20on%20Divine%20Mercy.mp4";
//selectors set
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(MPMoviePlayerPlaybackStateDidChange:)
name:MPMoviePlayerPlaybackStateDidChangeNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(willEnterFullscreen:) name:MPMoviePlayerWillEnterFullscreenNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(willExitFullscreen:) name:MPMoviePlayerWillExitFullscreenNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(enteredFullscreen:) name:MPMoviePlayerDidEnterFullscreenNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(exitedFullscreen:) name:MPMoviePlayerDidExitFullscreenNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(playbackFinished:) name:MPMoviePlayerPlaybackDidFinishNotification object:nil];
//Play the movie
videoAlreadyPlaying=#"TRUE";
self.moviePlayer = [[MPMoviePlayerController alloc] initWithContentURL:[NSURL URLWithString:url]];
[self.view addSubview:moviePlayer.view];
self.moviePlayer.view.frame = CGRectMake(0,64,320,220);
[moviePlayer play];
[self initializeFileDownloadDataArray];
NSArray *URLs = [[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask];
self.docDirectoryURL = [URLs objectAtIndex:0];
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration backgroundSessionConfiguration:#"com.jjdoherty98.Marion_s_Net"];
sessionConfiguration.HTTPMaximumConnectionsPerHost = 5;
self.session = [NSURLSession sessionWithConfiguration:sessionConfiguration
delegate:self
delegateQueue:nil];
if([appDelegate.isAlreadyDownloading isEqual:#"FALSE"] || appDelegate.isAlreadyDownloading==nil){
self.progressView.hidden = YES;
progressView.progress=0;
}
if([appDelegate.isAlreadyDownloading isEqual:#"TRUE"]){
[self performSelectorOnMainThread:#selector(progressUpdate) withObject:nil waitUntilDone:NO];
}
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
if (self.isMovingFromParentViewController || self.isBeingDismissed) {
NSLog(#"Left View");
[moviePlayer stop];
}
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationPortrait | UIInterfaceOrientationPortraitUpsideDown;
}
- (BOOL)shouldAutorotate{
AppDelegate *mainDelegate = (AppDelegate*)[[UIApplication sharedApplication]delegate];
mainDelegate.fullScreenVideoIsPlaying = #"TRUE";
return YES;
}
- (void)MPMoviePlayerPlaybackStateDidChange:(NSNotification *)notification
{
if (moviePlayer.playbackState == MPMoviePlaybackStatePlaying)
{ //playing
videoAlreadyPlaying = #"TRUE";
}
if (moviePlayer.playbackState == MPMoviePlaybackStateStopped)
{ //stopped
}if (moviePlayer.playbackState == MPMoviePlaybackStatePaused)
{ //paused
}if (moviePlayer.playbackState == MPMoviePlaybackStateInterrupted)
{ //interrupted
}if (moviePlayer.playbackState == MPMoviePlaybackStateSeekingForward)
{ //seeking forward
}if (moviePlayer.playbackState == MPMoviePlaybackStateSeekingBackward)
{ //seeking backward
}
}
- (void)willEnterFullscreen:(NSNotification*)notification {
NSLog(#"willEnterFullscreen");
AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
appDelegate.fullScreenVideoIsPlaying = #"TRUE";
}
- (void)enteredFullscreen:(NSNotification*)notification {
NSLog(#"enteredFullscreen");
}
- (void)willExitFullscreen:(NSNotification*)notification {
NSLog(#"willExitFullscreen");
AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
appDelegate.fullScreenVideoIsPlaying = #"FALSE";
}
- (void)exitedFullscreen:(NSNotification*)notification {
AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
NSLog(#"exitedFullscreen");
if(appDelegate.CurrentProgress==0){
progressView.hidden=TRUE;
}else{
[self performSelectorOnMainThread:#selector(progressUpdate) withObject:nil waitUntilDone:NO];
}
}
- (void)playbackFinished:(NSNotification*)notification {
NSNumber* reason = [[notification userInfo] objectForKey:MPMoviePlayerPlaybackDidFinishReasonUserInfoKey];
switch ([reason intValue]) {
case MPMovieFinishReasonPlaybackEnded:
NSLog(#"playbackFinished. Reason: Playback Ended");
videoAlreadyPlaying=#"FALSE";
[moviePlayer stop];
[moviePlayer play];
break;
case MPMovieFinishReasonPlaybackError:
NSLog(#"playbackFinished. Reason: Playback Error");
videoAlreadyPlaying=#"FALSE";
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Video Failed To Load!"
message:#"Unable to connect to server, please try again later!"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
[alert release];
break;
case MPMovieFinishReasonUserExited:
NSLog(#"playbackFinished. Reason: User Exited");
videoAlreadyPlaying=#"FALSE";
break;
default:
break;
}
[self.moviePlayer setFullscreen:NO animated:YES];
}
-(IBAction)buttonPressed:(id)sender{
AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
if([appDelegate.isThereInternet isEqual:#"TRUE"]){
if([appDelegate.isAlreadyDownloading isEqual: #"FALSE"]){
//If there is internet and not already downloading
progressView.hidden=FALSE;
progressView.progress=0;
FileDownloadInfo *fdi = [self.arrFileDownloadData objectAtIndex:0];
fdi.downloadTask = [self.session downloadTaskWithURL:[NSURL URLWithString:fdi.downloadSource]];
fdi.taskIdentifier = fdi.downloadTask.taskIdentifier;
// Start the task.
[fdi.downloadTask resume];
}}
if ([appDelegate.isThereInternet isEqual:#"FALSE"]) {
//No internet available
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Unable to connect to server"
message:#"Internet connection appears to be offline, please try again later!"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
[alert release];
}
if([appDelegate.isAlreadyDownloading isEqual:#"TRUE"]){
//Is already downloaing
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"File Already Downloading"
message:#"Multiple files can not be downloaded at the same time!"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
[alert release];
}
}
-(void)initializeFileDownloadDataArray{
self.arrFileDownloadData = [[NSMutableArray alloc] init];
[self.arrFileDownloadData addObject:[[FileDownloadInfo alloc] initWithFileTitle:nil andDownloadSource:url]];
}
-(int)getFileDownloadInfoIndexWithTaskIdentifier:(unsigned long)taskIdentifier{
int index = 0;
for (int i=0; i<[self.arrFileDownloadData count]; i++) {
FileDownloadInfo *fdi = [self.arrFileDownloadData objectAtIndex:i];
if (fdi.taskIdentifier == taskIdentifier) {
index = i;
break;
}
}
return index;
}
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location{
NSError *error;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *destinationFilename = downloadTask.originalRequest.URL.lastPathComponent;
NSURL *destinationURL = [self.docDirectoryURL URLByAppendingPathComponent:destinationFilename];
AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
if ([fileManager fileExistsAtPath:[destinationURL path]]) {
[fileManager removeItemAtURL:destinationURL error:nil];
}
BOOL success = [fileManager copyItemAtURL:location
toURL:destinationURL
error:&error];
if (success) {
int index = [self getFileDownloadInfoIndexWithTaskIdentifier:downloadTask.taskIdentifier];
FileDownloadInfo *fdi = [self.arrFileDownloadData objectAtIndex:index];
fdi.isDownloading = NO;
fdi.downloadComplete = YES;
// Set the initial value to the taskIdentifier property of the fdi object,
// so when the start button gets tapped again to start over the file download.
fdi.taskIdentifier = -1;
// In case there is any resume data stored in the fdi object, just make it nil.
fdi.taskResumeData = nil;
}
else{
NSLog(#"Unable to copy temp file. Error: %#", [error localizedDescription]);
}
}
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{
AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
if (error != nil) {
NSLog(#"Download completed with error: %#", [error localizedDescription]);
appDelegate.isAlreadyDownloading=#"FALSE";
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Failed to download!"
message:#"Unable to connect to the server!"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
[alert release];
});
}
else{
appDelegate.isAlreadyDownloading=#"FALSE";
NSLog(#"Download finished successfully.");
dispatch_async(dispatch_get_main_queue(), ^{
FileDownloadInfo *fdi = [self.arrFileDownloadData objectAtIndex:0];
fdi.isDownloading = NO;
fdi.downloadComplete = YES;
fdi.taskIdentifier = -1;
fdi.taskResumeData = nil;
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Download Complete!"
message:#"Go to downloads section to view the file now!"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
[alert release];
});
}
}
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{
if (totalBytesExpectedToWrite == NSURLSessionTransferSizeUnknown) {
NSLog(#"Unknown transfer size");
}
else{
int index = [self getFileDownloadInfoIndexWithTaskIdentifier:downloadTask.taskIdentifier];
FileDownloadInfo *fdi = [self.arrFileDownloadData objectAtIndex:index];
AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
appDelegate.isAlreadyDownloading=#"TRUE";
fdi.downloadProgress = (double)totalBytesWritten / (double)totalBytesExpectedToWrite;
appDelegate.CurrentProgress = fdi.downloadProgress;
[self performSelectorOnMainThread:#selector(progressUpdate) withObject:nil waitUntilDone:NO];
}
}
-(void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session{
AppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
[self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
if ([downloadTasks count] == 0) {
if (appDelegate.backgroundTransferCompletionHandler != nil) {
// Make nil the backgroundTransferCompletionHandler.
appDelegate.backgroundTransferCompletionHandler = nil;
progressView.hidden=TRUE;
// Show a local notification when all downloads are over.
UILocalNotification *localNotification = [[UILocalNotification alloc] init];
localNotification.alertBody = #"All files have been downloaded!";
[[UIApplication sharedApplication] presentLocalNotificationNow:localNotification];
}
}
}];
}
-(void)progressUpdate{
AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
if ([appDelegate.isAlreadyDownloading isEqual:#"TRUE"]) {
progressView.progress = appDelegate.CurrentProgress;
[NSTimer scheduledTimerWithTimeInterval:0 target:self selector:#selector(progressUpdate) userInfo:nil repeats:NO];
NSLog(#"Is this method being called");
if (appDelegate.CurrentProgress==1) {
progressView.hidden=TRUE;
}
}
}
#end
Any help would be appreciated!

In App Purchase Code Crashing during testing

I have added InApp Purchasing to my app but when trying to complete a test transaction using a Test User account, the app keeps crashing at the following code saying that Unrecognised Selector sent to Instance.
I have read up about this and feel it may be something to do with the fact I am using Auto Renewal Subscription product.
The code that seems to be related to the crash is this line:
[[NSNotificationCenter defaultCenter] postNotificationName:#"TransCancel" object: self];
I have provided my InAppPurchase code incase anyone can please please help me with this!!
InAppPurchaseSS.h
#import <Foundation/Foundation.h>
#import "StoreKit/StoreKit.h"
#define kProductPurchasedNotification #"ProductPurchased"
#define kProductPurchaseFailedNotification #"ProductPurchaseFailed"
#define kProductPurchaseCancelledNotification #"ProductPurchaseCancelled"
#interface InAppPurchaseSS : NSObject <SKProductsRequestDelegate,SKPaymentTransactionObserver,UIAlertViewDelegate>
{
SKProductsRequest* productsRequest;
SKProduct *proUpgradeProduct;
UIAlertView* waitingAlert;
BOOL isTransactionOngoing;
}
#property (retain) SKProductsRequest* productsRequest;
#property (retain) NSArray * products;
#property (retain) SKProductsRequest *request;
#property (assign) BOOL isTransactionOngoing;
+ (InAppPurchaseSS *) sharedHelper;
-(id)init;
- (void)buyProductIdentifier:(NSString *)productIdentifier;
- (BOOL)canMakePurchases;
-(void)restoreInAppPurchase;
- (void)collectProducts;
#end
InAppPurchaseSS.m
#import "InAppPurchaseSS.h"
#import "Reachability.h"
#implementation InAppPurchaseSS
#synthesize products;
#synthesize request;
#synthesize productsRequest;
#synthesize isTransactionOngoing;
static InAppPurchaseSS * _sharedHelper;
+ (InAppPurchaseSS *) sharedHelper {
if (_sharedHelper != nil) {
return _sharedHelper;
}
_sharedHelper = [[InAppPurchaseSS alloc] init];
return _sharedHelper;
}
-(id)init {
if( (self=[super init]))
{
isTransactionOngoing=NO;
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
}
return self;
}
- (void)collectProducts
{
self.request = [[SKProductsRequest alloc] initWithProductIdentifiers:[NSSet setWithObject:#"my.inappads"]];
self.request.delegate = self;
[self.request start];
}
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
NSLog(#"Received products results...");
self.products = response.products;
self.request = nil;
NSLog(#"Number of product:%i : %#",[response.products count],response.products);
NSArray *product = response.products;
proUpgradeProduct = [product count] == 1 ? [[product firstObject] retain] : nil;
if (proUpgradeProduct)
{
NSLog(#"Product title: %#" , proUpgradeProduct.localizedTitle);
NSLog(#"Product description: %#" , proUpgradeProduct.localizedDescription);
NSLog(#"Product price: %#" , proUpgradeProduct.price);
NSLog(#"Product id: %#" , proUpgradeProduct.productIdentifier);
}
for (NSString *invalidProductId in response.invalidProductIdentifiers)
{
NSLog(#"Invalid product id: %#" , invalidProductId);
}
}
-(void)restoreInAppPurchase
{
Reachability *reach = [Reachability reachabilityForInternetConnection];
NetworkStatus netStatus = [reach currentReachabilityStatus];
if (netStatus == NotReachable) {
NSLog(#"No internet connection!");
[[[UIAlertView alloc] initWithTitle:#"No Internet" message:#"Sorry, no internet connection found" delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles: nil] show];
return;
}
waitingAlert = [[UIAlertView alloc] initWithTitle:#"Restoring..." message:#"Please Wait...\n\n" delegate:nil cancelButtonTitle:nil otherButtonTitles: nil];
[waitingAlert show];
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
- (void)buyProductIdentifier:(NSString *)productIdentifier {
if ([productIdentifier isEqual: #"my.inappads"]) {
NSLog(#"No IAP Product ID specified");
return;
}
Reachability *reach = [Reachability reachabilityForInternetConnection];
NetworkStatus netStatus = [reach currentReachabilityStatus];
if (netStatus == NotReachable) {
NSLog(#"No internet connection!");
[[[UIAlertView alloc] initWithTitle:#"No Internet" message:#"Sorry, no internet connection found" delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles: nil] show];
return;
}
isTransactionOngoing=YES;
waitingAlert = [[UIAlertView alloc] initWithTitle:#"Purchasing..." message:#"Please Wait...\n\n" delegate:nil cancelButtonTitle:nil otherButtonTitles: nil];
[waitingAlert show];
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 6.0) {
SKMutablePayment *payment = [[SKMutablePayment alloc] init];
payment.productIdentifier = productIdentifier;
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
else {
SKPayment *payment = [SKPayment paymentWithProductIdentifier:productIdentifier];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
}
-(void)enableFeature
{
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"PurchaseSuccess"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
- (void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error
{
[waitingAlert dismissWithClickedButtonIndex:0 animated:YES];
NSLog(#"Restore completed transaction failed");
}
- (BOOL)canMakePurchases
{
return [SKPaymentQueue canMakePayments];
}
//
- (void)finishTransaction:(SKPaymentTransaction *)transaction wasSuccessful:(BOOL)wasSuccessful
{
isTransactionOngoing=NO;
[waitingAlert dismissWithClickedButtonIndex:0 animated:YES];
// remove the transaction from the payment queue.
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
if (wasSuccessful)
{
[self enableFeature];
[[[UIAlertView alloc] initWithTitle:#"Congratulations!!" message:#"You have succesfully Purchases." delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles: nil] show];
[[NSNotificationCenter defaultCenter] postNotificationName:#"successbuy" object:self];
}
else
{
[[[UIAlertView alloc] initWithTitle:#"Error!" message:transaction.error.localizedDescription delegate:nil cancelButtonTitle:nil otherButtonTitles:#"OK", nil] show];
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
[[NSNotificationCenter defaultCenter] postNotificationName:#"TransCancel" object: self];
}
}
- (void)completeTransaction:(SKPaymentTransaction *)transaction
{
NSLog(#"succesfull transaction");
[self finishTransaction:transaction wasSuccessful:YES];
}
- (void)restoreTransaction:(SKPaymentTransaction *)transaction
{
NSLog(#"transaction is restored");
[self finishTransaction:transaction wasSuccessful:YES];
}
// called when a transaction has failed
- (void)failedTransaction:(SKPaymentTransaction *)transaction
{
isTransactionOngoing=NO;
NSLog(#"failed transaction called");
if (transaction.error.code != SKErrorPaymentCancelled)
{
NSLog(#"Transaction failed called");
NSLog(#"Transaction error: %#", transaction.error.localizedDescription);
[self finishTransaction:transaction wasSuccessful:NO];
}
else
{
[waitingAlert dismissWithClickedButtonIndex:0 animated:YES];
NSLog(#"user cancel transaction");
// this is fine, the user just cancelled, so don’t notify
[[NSNotificationCenter defaultCenter] postNotificationName:#"TransCancel" object: self];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
}
#pragma mark -
#pragma mark SKPaymentTransactionObserver methods
// called when the transaction status is updated
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
NSLog(#"transaction status updated");
for (SKPaymentTransaction *transaction in transactions)
{
switch (transaction.transactionState)
{
case SKPaymentTransactionStatePurchased:
[self completeTransaction:transaction];
break;
case SKPaymentTransactionStateFailed:
[self failedTransaction:transaction];
break;
case SKPaymentTransactionStateRestored:
[self restoreTransaction:transaction];
break;
default:
break;
}
}
}
- (void) dealloc
{
[[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
[super dealloc];
}
#end
I am using the #TransCancel in the view that the user makes the purchase in, the code is like this:
// Purchase
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver: self selector: #selector(TransactionCancel) name:#"TransCancel" object: nil];
[center addObserver: self selector: #selector(TransactionComplete) name:#"successbuy" object: nil];
NSLog(#"%d",[[NSUserDefaults standardUserDefaults] boolForKey:#"PurchaseSuccess"]);
if([[NSUserDefaults standardUserDefaults] boolForKey:#"PurchaseSuccess"])
btn.hidden=YES;
else
btn.hidden=NO;
Error Log
2014-01-06 00:25:40.694 MyApp[2764:60b] transaction status updated
2014-01-06 00:25:40.695 MyApp[2764:60b] failed transaction called
2014-01-06 00:25:40.696 MyApp[2764:60b] Transaction failed called
2014-01-06 00:25:40.696 MyApp[2764:60b] Transaction error: Cannot connect to iTunes Store
2014-01-06 00:25:40.730 MyApp[2764:60b] -[MyViewController TransactionCancel]: unrecognized selector sent to instance 0x1666a350
MyViewController
#import "UpgradeViewController.h"
#import "ECSlidingViewController.h"
#import "MenuViewController.h"
#import "InAppPurchaseSS.h"
#define ProductIdentifier #"<my.inappads>"
#interface UpgradeViewController ()
#property (retain, nonatomic) IBOutlet JSAnimatedImagesView *animatedImagesView;
#property (weak, nonatomic) IBOutlet UIButton *installFullAppButton;
#end
#implementation UpgradeViewController
#synthesize menuBtn, animatedImagesView = _animatedImagesView, scrolly, bannerView, labelPrice;
- (NSString *)publisherIdForAdSdkBannerView:(AdSdkBannerView *)banner {
return #"e0616d4190bff65279ed5c20de1b5653";
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Price
SKProduct *product = [[[InAppPurchaseSS sharedHelper] products] lastObject];
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
[numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
[numberFormatter setLocale:product.priceLocale];
NSString *formattedString = [numberFormatter stringFromNumber:product.price];
// [self.installFullAppButton setTitle:formattedString forState:UIControlStateNormal];
labelPrice.text = formattedString;
// Purchase
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver: self selector: #selector(TransactionCancel) name:#"TransactionCancel" object: nil];
[center addObserver: self selector: #selector(TransactionComplete) name:#"successbuy" object: nil];
NSLog(#"%d",[[NSUserDefaults standardUserDefaults] boolForKey:#"PurchaseSuccess"]);
if([[NSUserDefaults standardUserDefaults] boolForKey:#"PurchaseSuccess"])
btn.hidden=YES;
else
btn.hidden=NO;
// Do any additional setup after loading the view.
//UIScrollView
self.scrolly.contentSize = CGSizeMake(320, 600);
//Image Transition
// self.animatedImagesView.delegate = self;
self.view.layer.shadowOpacity = 0.75f;
self.view.layer.shadowRadius = 10.0f;
self.view.layer.shadowColor = [UIColor blackColor].CGColor;
if (![self.slidingViewController.underLeftViewController isKindOfClass:[MenuViewController class]]) {
self.slidingViewController.underLeftViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"Menu"];
}
[self.view addGestureRecognizer:self.slidingViewController.panGesture];
self.menuBtn = [UIButton buttonWithType:UIButtonTypeCustom];
menuBtn.frame = CGRectMake(8, 30, 34, 24);
[menuBtn setBackgroundImage:[UIImage imageNamed:#"menuButton.png"] forState:UIControlStateNormal];
[menuBtn addTarget:self action:#selector(revealMenu:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:self.menuBtn];
myWebView.opaque = NO;
myWebView.backgroundColor = [UIColor clearColor];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)revealMenu:(id)sender
{
[self.slidingViewController anchorTopViewTo:ECRight];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
self.screenName = #"Upgrade";
}
#pragma mark - Memory Management
- (void)viewDidUnload
{
// [self setAnimatedImagesView:nil];
[self setScrolly:nil];
// [self setAnimatedImagesView:nil];
[super viewDidUnload];
[self setBannerView:nil];
[super viewDidUnload];
}
- (void)dealloc
{
bannerView.delegate = nil;
}
-(IBAction)but:(id)sender
{
[[InAppPurchaseSS sharedHelper] buyProductIdentifier:ProductIdentifier];
}
-(IBAction)restore:(id)sender
{
[[InAppPurchaseSS sharedHelper] restoreInAppPurchase];
}
- (IBAction)dismissView:(id)sender {
[self dismissViewControllerAnimated:YES completion:NULL];
}
-(void)TransactionComplete
{
btn.hidden=YES;
}
-(void)TransactionCancel
{
btn.hidden=NO;
}
You have this line:
[center addObserver: self selector: #selector(TransactionCancel) name:#"TransCancel" object: nil];
Which means that self must implement the TransactionCancel method. The error indicates that the method doesn't exist on the MyViewController class.
The solution is to add the TransactionCancel method to the MyViewController class.

MFMailComposerViewController not Sending Email

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.

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