Update ProgressView after the view controller has been changed - ios

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!

Related

Playing continuous audio in Iphone

I have this piece of code for playing audio, but once it is finished, I want to play the same audio again and again, I think I should use numberofloops=-1, but where I need to use this directly. Please help me.
#import "JetNapMusicPlayer.h"
#import <AVFoundation/AVFoundation.h>
#interface JetNapMusicPlayer()
#property(nonatomic,strong) AVQueuePlayer *avQueuePlayer;
#end
static JetNapMusicPlayer *sharedManager = nil;
#implementation JetNapMusicPlaye
#pragma mark Singleton Methods
+ (id)sharedManager {
#synchronized(self) {
if(sharedManager == nil)
sharedManager = [[super alloc] init];
}
return sharedManager;
}
- (id)init {
if (self = [super init]) {
// [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
MPRemoteCommandCenter *rcc = [MPRemoteCommandCenter sharedCommandCenter];
MPRemoteCommand *playCommand = rcc.playCommand;
[playCommand setEnabled:YES];
[playCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent *event) {
[(JetNapMusicPlayer *)[JetNapMusicPlayer sharedManager] play];
return MPRemoteCommandHandlerStatusSuccess;
}];
MPRemoteCommand *pauseCommand = rcc.pauseCommand;
[pauseCommand setEnabled:YES];
[pauseCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent *event) {
[(JetNapMusicPlayer *)[JetNapMusicPlayer sharedManager] pause];
return MPRemoteCommandHandlerStatusSuccess;
}];
}
return self;
}
- (void)dealloc {
[super dealloc];
}
-(AVPlayer *)avQueuePlayer
{
if (!_avQueuePlayer) {
[self initSession];
_avQueuePlayer = [[AVQueuePlayer alloc] init];
}
return _avQueuePlayer;
}
-(void)initSession
{
[[NSNotificationCenter defaultCenter] addObserver: self
selector: #selector(audioSessionInterrupted:)
name: AVAudioSessionInterruptionNotification
object: [AVAudioSession sharedInstance]];
//set audio category with options - for this demo we'll do playback only
NSError *categoryError = nil;
[[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayback error:&categoryError];
if (categoryError) {
NSLog(#"Error setting category! %#", [categoryError description]);
}
//activation of audio session
NSError *activationError = nil;
BOOL success = [[AVAudioSession sharedInstance] setActive: YES error: &activationError];
if (!success) {
if (activationError) {
NSLog(#"Could not activate audio session. %#", [activationError localizedDescription]);
} else {
NSLog(#"audio session could not be activated!");
}
}
}
#pragma mark - notifications
-(void)audioSessionInterrupted:(NSNotification*)interruptionNotification
{
NSLog(#"interruption received: %#", interruptionNotification);
}
#pragma mark - player actions
-(void) pause
{
[[self avQueuePlayer] pause];
}
-(void) play
{
[[self avQueuePlayer] play];
}
-(void) clear
{
[[self avQueuePlayer] removeAllItems];
}
#pragma mark - remote control events
#pragma mark - Kony FFI
+ (BOOL)playMusic:(NSString *)filename artistname:(NSString *)artistname songname:(NSString *)songname {
NSString *name = [filename stringByDeletingPathExtension];
NSString *ext = [filename pathExtension];
AVPlayerItem *avSongItem = [[AVPlayerItem alloc] initWithURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:[[NSString alloc] initWithFormat:name] ofType:ext]]];
if (avSongItem) {
[(JetNapMusicPlayer *)[JetNapMusicPlayer sharedManager] clear];
[[[JetNapMusicPlayer sharedManager] avQueuePlayer] insertItem:avSongItem afterItem:nil];
[(JetNapMusicPlayer *)[JetNapMusicPlayer sharedManager] play];
[MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo = #{MPMediaItemPropertyTitle: songname, MPMediaItemPropertyArtist:artistname};
}
return YES;
}
+ (BOOL)stopMusic {
[(JetNapMusicPlayer *)[JetNapMusicPlayer sharedManager] pause];
[(JetNapMusicPlayer *)[JetNapMusicPlayer sharedManager] clear];
return YES;
}
#end
To loop a song use below code after alloc init of avSongItem.
avSongItem.actionAtItemEnd = AVPlayerActionAtItemEndNone;
More info : Looping a video with AVFoundation AVPlayer?
Also as mentioned in the link use notification.
avSongItem.actionAtItemEnd = AVPlayerActionAtItemEndNone;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playerItemDidReachEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:[avPlayer currentItem]];
this will prevent the player to pause at the end.
in the notification:
- (void)playerItemDidReachEnd:(NSNotification *)notification {
AVPlayerItem *p = [notification object];
[p seekToTime:kCMTimeZero];
}

bluetooth connection is immediately disconnecting multipeer framework

I am trying to establish one to one peer connection using multipeer connectivity framework. When i am sending invitation to nearby peer that device gets connected(sometime takes long time to get connected), and the peer which has send the request is always taking long time to change its state to connected & once it is connected immediately it is disconnecting from the session. I tried to debug the issue but not found anything, dont know what wrong is happening, please let me know if i am missing anything or any mistake is done in code.
Below given is the code snippet of the same.
MPCHandler.m
#define DidChangeStateNotification #"E_DidChangeStateNotification"
#define DidReceiveDataNotification #"E_DidReceiveDataNotification"
#define DidInviteNotification #"E_DidInvitedNotification"
#define DidReceivedInvetationNotification #"E_DidReceivedInvetationNotification"
static NSString * const EServiceType = #"E-service";
#interface MPCHandler ()
#property AppDelegate *appDelegate;
#end
#implementation MPCHandler
- (void)setupPeerWithDisplayName:(NSString *)displayName {
self.appDelegate = [[UIApplication sharedApplication] delegate];
self.peerID = [[MCPeerID alloc] initWithDisplayName:displayName];
self.connectedPeers = [NSMutableArray array];
self.foundPeers = [NSMutableArray array];
self.invitedPeers = [NSMutableArray array];
}
- (void)setupSession {
self.session = [[MCSession alloc] initWithPeer:self.peerID securityIdentity:nil encryptionPreference:MCEncryptionNone];
self.session.delegate = self;
}
- (void)setupBrowser {
self.browser = [[MCNearbyServiceBrowser alloc] initWithPeer:self.peerID serviceType:EServiceType];
self.browser.delegate = self;
}
- (void)advertiseSelf:(BOOL)advertise {
if (advertise) {
self.advertiser = [[MCNearbyServiceAdvertiser alloc] initWithPeer:self.peerID discoveryInfo:nil serviceType:EServiceType];
self.advertiser.delegate = self;
[self.advertiser startAdvertisingPeer];
} else {
[self.advertiser stopAdvertisingPeer];
self.advertiser = nil;
}
}
#pragma MCSessionDelegate methods
- (void)session:(MCSession *)session peer:(MCPeerID *)peerID didChangeState:(MCSessionState)state {
NSDictionary *userInfo = #{ #"peerID": peerID,
#"state" : #(state) };
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:DidChangeStateNotification
object:nil
userInfo:userInfo];
});
}
- (void)session:(MCSession *)session didReceiveData:(NSData *)data fromPeer:(MCPeerID *)peerID {
NSDictionary *userInfo = #{ #"data": data,
#"peerID": peerID };
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:DidReceiveDataNotification
object:nil
userInfo:userInfo];
});
}
- (void)session:(MCSession *)session didReceiveCertificate:(NSArray *)certificate fromPeer:(MCPeerID *)peerID certificateHandler:(void (^)(BOOL))certificateHandler{
certificateHandler(YES);
}
#pragma MCNearbyServiceAdvertiserDelegate methods
// Incoming invitation request. Call the invitationHandler block with YES and a valid session to connect the inviting peer to the session.
- (void)advertiser:(MCNearbyServiceAdvertiser *)advertiser didReceiveInvitationFromPeer:(MCPeerID *)peerID withContext:(NSData *)context invitationHandler:(void(^)(BOOL accept, MCSession *session))invitationHandler{
NSDictionary *info =[NSDictionary dictionaryWithObject:peerID.displayName forKey:#"displayName"];
NSLog(#"%# Received Invetation from : %#",self.peerID.displayName,peerID.displayName);
invitationHandler(YES,self.session);
[self.connectedPeers addObject:peerID];
self.invited = NO;
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:DidReceivedInvetationNotification
object:nil
userInfo:info];
});
}
#pragma MCNearbyServiceBrowserDelegate methods
// Found a nearby advertising peer
- (void)browser:(MCNearbyServiceBrowser *)browser foundPeer:(MCPeerID *)peerID withDiscoveryInfo: (NSDictionary *)info{
// NSLog(#"Peer : %# found.",peerID.displayName);
// if (![self.peerID isEqual:peerID]) {
NSLog(#"%# Found Peer : %#",self.peerID.displayName,peerID.displayName);
[self.foundPeers addObject:peerID];
if (![self.connectedPeers containsObject:peerID])
{
if ([self.invitedPeers count] == 0 && [self.session.connectedPeers count]==0)
{
NSLog(#"%# Invited Peer : %#",self.peerID.displayName,peerID.displayName);
[self.invitedPeers addObject:peerID];
[browser invitePeer:peerID
toSession:self.session
withContext:[#"Empath" dataUsingEncoding:NSUTF8StringEncoding]
timeout:0];
// [browser stopBrowsingForPeers];
self.invited = YES;
NSDictionary *userInfo = #{ #"peerID": peerID };
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:DidInviteNotification
object:nil
userInfo:userInfo];
});
}
}
// }
}
// A nearby peer has stopped advertising
- (void)browser:(MCNearbyServiceBrowser *)browser lostPeer:(MCPeerID *)peerID{
[self.foundPeers removeObject:peerID];
[self.invitedPeers removeAllObjects];
[browser startBrowsingForPeers];
}
- (void)browser:(MCNearbyServiceBrowser *)browser didNotStartBrowsingForPeers:(NSError *)error
{
NSLog( #"Unable to start browsing for peers. Error: %#", error );
}
ViewController.m
#define DidChangeStateNotification #"E_DidChangeStateNotification"
#define DidReceiveDataNotification #"E_DidReceiveDataNotification"
#define DidInviteNotification #"E_DidInvitedNotification"
#define DidReceivedInvetationNotification #"E_DidReceivedInvetationNotification"
#interface ViewController ()
#property (nonatomic, strong) AppDelegate *appDelegate;
#end
#implementation ViewController
#synthesize txtMessage;
#synthesize tvHistory;
#synthesize btnSend;
#synthesize lblName;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
// self.appDelegate.connectedPeers = [NSMutableArray array];
// self.appDelegate.foundPeers = [NSMutableArray array];
[self AddObservers];
[self setUserInteraction:NO];
[self.lblName setText:#""];
[self.btnSend setUserInteractionEnabled:NO];
[self performSelector:#selector(advertisePeer) withObject:nil afterDelay:0.0];
}
-(void) AddObservers{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(handleReceivedDataWithNotification:)
name:DidReceiveDataNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(peerChangedStateWithNotification:)
name:DidChangeStateNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(receivedInvetationWithNotification:)
name:DidReceivedInvetationNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(peerInvitedWithNotification:)
name:DidInviteNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(sessionDidTimeout:) name:kSessionDidTimeoutNotification object:nil];
}
-(void) advertisePeer{
[self setUserInteraction:NO];
[self.lblName setText:#""];
[self.appDelegate.mpcHandler setupPeerWithDisplayName:[UIDevice currentDevice].name];
[self.appDelegate.mpcHandler setupSession];
[self.appDelegate.mpcHandler advertiseSelf:YES];
[self.btnSend setUserInteractionEnabled:YES];
}
- (IBAction)searchAndConnectPeer:(id)sender{
[self.lblName setText:#"Searching..."];
if (self.appDelegate.mpcHandler.session != nil) {
[[self.appDelegate mpcHandler] setupBrowser];
[[[self.appDelegate mpcHandler] browser] startBrowsingForPeers];
}
}
- (IBAction)sendMessage:(id)sender{
NSString *messageToSend = [txtMessage text];
NSUInteger len = 0;
if ([messageToSend length]) {
NSData *messageAsData = [messageToSend dataUsingEncoding:NSUTF8StringEncoding];
NSError *error;
[self.appDelegate.mpcHandler.session sendData:messageAsData
toPeers:self.appDelegate.mpcHandler.session.connectedPeers
withMode:MCSessionSendDataReliable
error:&error];
// If any error occurs, just log it.
// Otherwise set the following couple of flags to YES, indicating that the current player is the creator
// of the game and a game is in progress.
len = [[tvHistory text] length];
if (error != nil) {
NSLog(#"%#", [error localizedDescription]);
[self.tvHistory setText:[NSString stringWithFormat:#"%#",[error localizedDescription]]];
} else{
NSString *history = [NSString stringWithFormat:#"Me : %#\n\n",messageToSend];
[self.tvHistory setText:[self.tvHistory.text stringByAppendingString:history]];
}
}
[self.txtMessage setText:#""];
[self.tvHistory scrollRangeToVisible:NSMakeRange([self.tvHistory.text length], len)];
}
- (void)peerInvitedWithNotification:(NSNotification *)notification{
[self setUserInteraction:NO];
MCPeerID *peerID = [[notification userInfo] objectForKey:#"peerID"];
[self.lblName setText:[NSString stringWithFormat:#"Connecting to %#...",peerID.displayName]];
}
- (void)receivedInvetationWithNotification:(NSNotification *)notification{
[self setUserInteraction:YES];
NSString *name = [[notification userInfo] objectForKey:#"displayName"];
[self.lblName setText:[NSString stringWithFormat:#"Connected : %#",name]];
}
- (void)peerChangedStateWithNotification:(NSNotification *)notification {
// Get the state of the peer.
int state = [[[notification userInfo] objectForKey:#"state"] intValue];
MCPeerID *peerID = [[notification userInfo] objectForKey:#"peerID"];
// We care only for the Connected and the Not Connected states.
// The Connecting state will be simply ignored.
if (state == MCSessionStateConnected) {
// We'll just display all the connected peers (players) to the text view.
NSString *allPlayers = #"Connected : ";
allPlayers = [allPlayers stringByAppendingString:[NSString tringWithFormat:#"%#",peerID.displayName]];
[self.lblName setText:allPlayers];
[self setUserInteraction:YES];
NSLog(#"%#...",allPlayers);
// // Fire up the timer upon first event
// if(!_idleTimer) {
// [self resetIdleTimer];
// }
}else if (state == MCSessionStateConnecting){
[self setUserInteraction:NO];
[self.lblName setText:[NSString stringWithFormat:#"Connecting to %#...",peerID.displayName]];
NSLog(#"Connecting %#...",peerID.displayName);
}else if (state == MCSessionStateNotConnected){
NSLog(#"Disconnected %#...",peerID.displayName);
// [self sessionDidTimeout:nil];
}
}
-(void) setUserInteraction:(BOOL)enabled{
[self.btnSend setUserInteractionEnabled:enabled];
[self.txtMessage setUserInteractionEnabled:enabled];
}
- (void)handleReceivedDataWithNotification:(NSNotification *)notification {
// Get the user info dictionary that was received along with the notification.
NSDictionary *userInfoDict = [notification userInfo];
// Convert the received data into a NSString object.
NSData *receivedData = [userInfoDict objectForKey:#"data"];
NSString *message = [[NSString alloc] initWithData:receivedData encoding:NSUTF8StringEncoding];
// Keep the sender's peerID and get its display name.
MCPeerID *senderPeerID = [userInfoDict objectForKey:#"peerID"];
NSString *senderDisplayName = senderPeerID.displayName;
// Add this guess to the history text view.
NSUInteger len = [[tvHistory text] length];
NSString *history = [NSString stringWithFormat:#"%# : %#\n\n", senderDisplayName, message];
[self.tvHistory setText:[self.tvHistory.text stringByAppendingString:history]];
[self.tvHistory scrollRangeToVisible:NSMakeRange([self.tvHistory.text length], len)];
}
-(BOOL)textFieldShouldReturn:(UITextField *)textField{
[textField resignFirstResponder];
return YES;
}
- (void)resetIdleTimer
{
if (_idleTimer) {
[_idleTimer invalidate];
}
// Schedule a timer to fire in kApplicationTimeoutInMinutes * 60
int timeout = kSessionTimeoutInSeconds;
_idleTimer = [NSTimer scheduledTimerWithTimeInterval:timeout
target:self
selector:#selector(idleTimerExceeded)
userInfo:nil
repeats:NO];
}
- (void)idleTimerExceeded {
/* Post a notification so anyone who subscribes to it can be notified when
* the application times out */
[[NSNotificationCenter defaultCenter]
postNotificationName:kSessionDidTimeoutNotification object:nil];
}
- (void) sessionDidTimeout:(NSNotification *) notif {
// [self setUserInteraction:NO];
//
// [self.lblName setText:#"Session expired..."];
//
// NSLog(#"============================Session Expired...=====================");
//
// [[[self.appDelegate mpcHandler] session] disconnect];
//
// if ([[[self appDelegate]mpcHandler] invited]) {
// [self performSelector:#selector(advertisePeer) withObject:nil afterDelay:0.0];
// [self performSelector:#selector(searchAndConnectPeer:) withObject:nil afterDelay:1.0];
// }
}
Check for BTM disconnection to service message in the logs .It is a problem with Apple Bluetooth which they have acknowledged.Try without Bluetooth i.e peer-peer wifi or infrastructure networks

Phonegap iOS plugin- how to access callback in didFinishWithResult

This is some code from my sms plugin for phonegap. I'm trying to make callbacks work properly: https://github.com/aharris88/phonegap-sms-plugin/issues/11
Here's the code I'm working on. You can see that I get the callback function at the beginning of the send method like this:
NSString* callback = command.callbackId;
Then I present an MFMessageComposeViewController and I need to call that callback when it finishes. So I'm using the messageComposeViewController:didFinishWithResult:, but how can I access that callback function that I need to call?
#import "Sms.h"
#import <Cordova/NSArray+Comparisons.h>
#implementation Sms
- (CDVPlugin *)initWithWebView:(UIWebView *)theWebView {
self = (Sms *)[super initWithWebView:theWebView];
return self;
}
- (void)send:(CDVInvokedUrlCommand*)command {
NSString* callback = command.callbackId;
if(![MFMessageComposeViewController canSendText]) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Notice"
message:#"SMS Text not available."
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
return;
}
MFMessageComposeViewController* composeViewController = [[MFMessageComposeViewController alloc] init];
composeViewController.messageComposeDelegate = self;
NSString* body = [command.arguments objectAtIndex:1];
if (body != nil) {
[composeViewController setBody:body];
}
NSArray* recipients = [command.arguments objectAtIndex:0];
if (recipients != nil) {
[composeViewController setRecipients:recipients];
}
[self.viewController presentViewController:composeViewController animated:YES completion:nil];
[[UIApplication sharedApplication] setStatusBarHidden:YES];
}
#pragma mark - MFMessageComposeViewControllerDelegate Implementation
- (void)messageComposeViewController:(MFMessageComposeViewController *)controller didFinishWithResult:(MessageComposeResult)result {
int webviewResult = 0;
CDVPluginResult* pluginResult = nil;
switch(result) {
case MessageComposeResultCancelled:
webviewResult = 0;
break;
case MessageComposeResultSent:
webviewResult = 1;
break;
case MessageComposeResultFailed:
webviewResult = 2;
break;
default:
webviewResult = 3;
break;
}
[self.viewController dismissViewControllerAnimated:YES completion:nil];
[[UIApplication sharedApplication] setStatusBarHidden:NO];
if (webviewResult == 1) {
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
} else {
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:#"Arg was null"];
}
[self.commandDelegate sendPluginResult:pluginResult callbackId:callback];
}
#end
You'll want to store the callback ID as a property on the class.
#interface Sms
#property (nonatomic, strong) NSString *callbackId
#end
And then store it when you are in your send method.
- (void)send:(CDVInvokedUrlCommand*)command {
self.callbackId = command.callbackId;
You can then access it again from your delegate method:
NSString *callbackId = self.callbackId;
You should be good to go.

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