I've done more than three days worth of research to fix my problem and I haven't seen anyone with a solution to my problem. The Browser invites an Advertiser, the Advertiser accepts, and the MCSession changes to a connected state. However, once the MCBrowserViewController is closed (by either the cancel or the done button), the MCSession disconnects. As long as I don't close the MCBrowserViewController, the MCSession stays connected. I don't understand why or how this works and I've even tried debugging the process, but it got too deep into threads for me to understand.
Please tell me it's just something wrong with my code.
-(void)setUpMultiPeer{
self.myPeerID = [[MCPeerID alloc] initWithDisplayName:pos];
self.mySession = [[MCSession alloc] initWithPeer:self.myPeerID];
self.browserVC = [[MCBrowserViewController alloc] initWithServiceType:#"svctype" session:self.mySession];
self.advertiser = [[MCAdvertiserAssistant alloc] initWithServiceType:#"svctype" discoveryInfo:nil session:self.mySession];
self.browserVC.delegate = self;
self.mySession.delegate = self;
}
-(void)dismissBrowserVC{
[self.browserVC dismissViewControllerAnimated:YES completion:nil];
}
-(void)browserViewControllerDidFinish:(MCBrowserViewController *)browserVC{
[self dismissBrowserVC];
}
-(void)browserViewControllerWasCancelled:(MCBrowserViewController *)browserViewController{
[self dismissBrowserVC];
}
-(void)session:(MCSession *)session peer:(MCPeerID *)peerID didChangeState:(MCSessionState)state{
if (state == MCSessionStateConnected) {
NSLog(#"Connected!");
//Not entirely sure about this next line...
self.mySession = session;
}
else if (state == MCSessionStateNotConnected){
NSLog(#"Disconnected");
dispatch_async(dispatch_get_main_queue(), ^(void) {
UIAlertView *alert = [[UIAlertView alloc]initWithTitle: #"Somebody Left!"
message: [[NSString alloc] initWithFormat:#"%#", [peerID displayName]]
delegate: nil
cancelButtonTitle:#"Got it"
otherButtonTitles:nil];
[alert show];
});
}
}
//Called by a UIButton
-(IBAction)browseGO:(id)sender {
[self setUpMultiPeer];
[self presentViewController:self.browserVC animated:YES completion:nil];
}
//Called by a UISwitch
-(IBAction)advertiseSwitch:(id)sender {
if (_advertiseSwitcher.on) {
[self setUpMultiPeer];
[self.advertiser start];
}
else{
[self.advertiser stop];
}
}
I have also attempted using a unique MCSession for each the Browser and the Advertiser, but with no success.
What I did to solve my problem was start over from ground zero. Waiting on an answer from StackOverflow and from the Apple Developer Forum was taking too long, so I went back to what worked at the very beginning and I will build up from there once more.
Here is a link for an awesome tutorial that I found. I hope this helps someone solve their problem.
However, if anyone sees anything utterly wrong with my code in the question PLEASE DO TELL! I want to know what was causing that bug so that I can learn from my mistakes.
Thank you for stopping by and reading this question.
Related
I am using this method to ask a nearby device to join the session:
When I do it I also start spinning an indicator
[browser invitePeer:key
toSession:session
withContext:nil
timeout:30];
Is there a method called in the moment of timeout? what if the other device goes out of range?
EDIT:
I notice that this state is never called:
if (state == MCSessionStateConnecting) {
NSLog(#"CONNECTING %#", peerID);
}
in case of timeouts on the browser side, you need to watch for the MCSessionStateNotConnected state. i do something like this:
- (void)session:(MCSession *)session
peer:(MCPeerID *)peerID
didChangeState:(MCSessionState)state
{
if (state == MCSessionStateNotConnected)
{
if (self.isWaitingForInvitation)
{
UIAlertView *alertView = [[UIAlertView alloc]
initWithTitle:NSLocalizedString(#"ERROR_TITLE", nil)
message:NSLocalizedString(#"ERROR_TEXT", nil)
delegate:self
cancelButtonTitle:NSLocalizedString(#"NO", #"Não")
otherButtonTitles:NSLocalizedString(#"YES", #"Sim"),
nil];
dispatch_sync(dispatch_get_main_queue(), ^{
[alertView show];
});
self.isWaitingForInvitation = NO;
}
}
use the dispatch_sync to make the alert popup right away.
Using a timer with a timer interval matching timeout parameter could be better idea.
I hope I am not violating NDA by posting this question.
I am using the new multipeer connectivity to send using bluetooth some files to nearby devices. I have managed to send invitations, but I don't seem to get how to display a UIAlertView where the user can accept or decline the invite. Right now when a user sends, the file is automatically saved and there is no accept/decline alert.
The code is:
- (void) advertiser:(MCNearbyServiceAdvertiser *)advertiser
didReceiveInvitationFromPeer:(MCPeerID *)peerID
withContext:(NSData *)context
invitationHandler:(void(^)(BOOL accept,
MCSession *session))invitationHandler{
... save the data context
but with the alert:
- (void) advertiser:(MCNearbyServiceAdvertiser *)advertiser
didReceiveInvitationFromPeer:(MCPeerID *)peerID
withContext:(NSData *)context
invitationHandler:(void(^)(BOOL accept,
MCSession *session))invitationHandler{
DevicePeer = [MCPeerID alloc];
DevicePeer = peerID;
ArrayInvitationHandler = [NSArray arrayWithObject:[invitationHandler copy]];
// ask the user
UIAlertView *alertView = [[UIAlertView alloc]
initWithTitle:#""
message:#""
delegate:self
cancelButtonTitle:#"NO"
otherButtonTitles:#"YES", nil];
[alertView show];
alertView.tag = 2;
}
and the alert view method:
- (void) alertView:(UIAlertView *)alertView
clickedButtonAtIndex:(NSInteger)buttonIndex
{
// retrieve the invitationHandler
// get user decision
BOOL accept = (buttonIndex != alertView.cancelButtonIndex) ? YES : NO;
// respond
MCSession *session = [ArrayInvitationHandler objectAtIndex:0];
void (^invitationHandler)(BOOL, MCSession *) = [ArrayInvitationHandler objectAtIndex:0];
invitationHandler(accept, session);
}
When the user press YES the app crashes and I get the error:
[__NSMallocBlock__ nearbyConnectionDataForPeer:withCompletionHandler:]: unrecognized selector sent to instance 0x14d4e3b0'
I have looked up at the IOS developer library and there is no such method apart from
- (void)nearbyConnectionDataForPeer:(id)arg1 withCompletionHandler:(id)arg2{
}
which does no work. No info on the IOS developer forums. Any ideas?
Alessandro is right, this is not explained in the WWDC 2013 video. I struggled with this myself.
I think you're on the right track, you just have a couple logic errors.
I don't understand these two lines:
MCSession *session = [ArrayInvitationHandler objectAtIndex:0];
void (^invitationHandler)(BOOL, MCSession *) = [ArrayInvitationHandler objectAtIndex:0];
The object stored in your array is just your handler. The reason you're getting that crash is that the browser is seeing that accept is true and trying to connect the peer to the session, but the session you're giving it back is nil. To fix this, you want to pass back a new session that you create.
At first I was confused by the notion of creating a new session when one has already been created by the browser side, but then I realized that we don't get that session from the browser anywhere, and we can't really pass it back into the invitation handler if it doesn't exist!
So yeah, do this instead:
BOOL accept = (buttonIndex != alertView.cancelButtonIndex) ? YES : NO;
// respond
MCSession *session;
if(accept) {
session = [[MCSession alloc] initWithPeer:peer];
session.delegate = self;
}
void (^invitationHandler)(BOOL, MCSession *) = [ArrayInvitationHandler objectAtIndex:0];
invitationHandler(accept, session);
I suggest you to watch Nearby Networking with Multipeer Connectivity WWDC 2013 video on the Apple developers center. There is an example about this stuff and this is very well explained.
PS : Yes you break NDA (september 14) but now it's ok :)
I am getting stream on iPad from an IP camera named Foscam. If I open IP in iPad browser then it is working good but when I fetch stream in my application then it gets on frist screen shot and stuck on it. App is not getting continuous streaming from cam. I am using this way
- (IBAction)powerChanged:(id)sender {
UISwitch * s = (UISwitch *)sender;
if( s.on == TRUE){
[self initializeData];
if(![deviceClient connect]){
UIAlertView *message = [[UIAlertView alloc] initWithTitle:#"Can not connect to T-Rex device!"
message:[[NSString alloc] initWithFormat:#"Could not connec to %# and %d port",deviceClient.host,deviceClient.port]
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[message show];
[s setOn:FALSE animated:TRUE];
} else {
[self adjustLayout:TRUE];
[self startFrequencyTimer];
}
}else {
[self stopFrequencyTimer];
[self adjustLayout:FALSE];
}
}
-(void)initializeData {
[cameraViewFront loadRequest:[NSURLRequest requestWithURL:[frontCameraClient getVideoStreamURL]]];
}
-(BOOL)connect {
if (udpSocket == nil){
udpSocket = [[GCDAsyncUdpSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
}
NSLog(#"host %# and port %i", self.host, self.port);
return [udpSocket connectToHost:self.host onPort:self.port error:nil];
}
- (NSURL *) getURL:(NSString *)forPage {
NSLog(#"front cam IP %#" ,self.host);
return [[NSURL alloc] initWithString:[[NSString alloc] initWithFormat:#"http://%#/%#user=%#&pwd=%#",self.host,forPage,self.username, self.password]];
}
-(NSURL *)getVideoStreamURL{
return [self getURL:#"videostream.cgi?"];
}
I figured it out. I am using foscam and foscam provides a list of URLs on this link so I searched my cam model and then tried all the given URLs for MPEG connection type and http://IPADDRESS/videostream.cgi?user=[USERNAME]&pwd=[PASSWORD]&resolution=32&rate=0 URL worked for my case. But still there is a problem that it is working awesome on iOS5 and on iOS6 it still stuck on first frame.
To fix this problem this link helped me a lot. This method works for iOS5 and 6 both.
Got a problem with the new Twitter.framework that I haven't been able to find a solution for yet.
Here is my code:
if ([TWTweetComposeViewController canSendTweet]){
TWTweetComposeViewController *twitter = [[TWTweetComposeViewController alloc] init];
[twitter addImage:tweetImage];
[twitter setInitialText:initalString];
[twitter addURL:url];
twitter.completionHandler = ^(TWTweetComposeViewControllerResult result) {
if (result == TWTweetComposeViewControllerResultDone) {
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertView* alertView = [[UIAlertView alloc] initWithTitle:#"Tweeted"
message:#"You successfully tweeted"
delegate:self cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alertView show];
[alertView release];
});
} else if (result == TWTweetComposeViewControllerResultCancelled) {
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertView* alertView = [[UIAlertView alloc] initWithTitle:#"Twitter"
message:#"Tweet has been canceled"
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alertView show];
[alertView release];
});
}
dispatch_async(dispatch_get_main_queue(), ^{
[self dismissModalViewControllerAnimated:YES];
});
};
[self presentViewController:twitter animated:YES completion:nil];
[twitter release];
}
Seems to be the standard way of implementing this although I made the addition of queuing the UI stuff on the main thread. The addImage, setInitialText and addURL parameters are all good. In, fact this works most of the time. The problem I am having is that occasionally when the TWTweetComposeViewController is alloc'd init the app freezes and I can see "twitterd session interrupted, restarting... " in the console. The app will hang sometimes for only a few seconds but more often it will hang for unreasonable amount of time (20 - 30 secs or more), I will get numerous of these messages and then the twitter controller will finally slide up. Occasionally, as well, it will just hang and never come back.
Was wondering it anybody has see this problem before or has any ideas on a solution to this problem?
Thanks in advance.
I never add these problems with the twitter view controller. I used my code pasted in this post: https://stackoverflow.com/questions/9314308/can-twtweetcomposeviewcontroller-tweet-sheet-rotate-to-landscape
You can try it, just change it to "self" when you dismiss or present in modal view, as I'm using a different view controller.
The issue I have is that after users tap on the "Share" button of the FBDialog, they do not get any visual feedback until the request completes or fail... and over 3G that can takes a while. During this time users do not know if they tapped "Share" correctly or not, and there's risk of posting content twice.
Is there a way I can get a callback so I can display a loading indicator during this time? Where should I put this piece in?
Thank you!
Below code you write to fbAgent.m and also obser code here u get one "PID" at that time u write to alert msg ....... user can understand to that msg was success posted Facebook.
I hope this helps.
- (void)request:(FBRequest*)request didLoad:(id)result {
if ([request.method isEqualToString:#"facebook.fql.query"]) {
NSArray* users = result;
NSDictionary* user = [users objectAtIndex:0];
NSString* name = [user objectForKey:#"name"];
// Calling the delegate callback
[delegate facebookAgent:self didLoadName:name];
} else if ([request.method isEqualToString:#"facebook.users.setStatus"]) {
newStatus = nil;
NSString* success = result;
if ([success isEqualToString:#"1"]) {
// Calling the delegate callback
[delegate facebookAgent:self statusChanged:YES];
} else {
[delegate facebookAgent:self statusChanged:NO];
}
} else if ([request.method isEqualToString:#"facebook.photos.upload"]) {
NSDictionary* photoInfo = result;
NSString* pid = [photoInfo objectForKey:#"pid"];
UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:nil message:[NSString stringWithFormat:#"success Uploaded Your image please check your FaceBook", pid] delegate:self cancelButtonTitle:nil otherButtonTitles:#"Ok", nil];
[alertView show];
[alertView release];
fbbut.enabled=YES;
[fbact stopAnimating];
self.uploadImageData = nil;
self.uploadImageCaption = nil;
self.uploadImageAlbum = nil;
//[delegate facebookAgent:self photoUploaded:pid];
if(newStatus){
[self setStatus:newStatus];
}
}
}
Start your indicator when you click on button and stop on Delegate of Facebook function
- (void)dialogDidComplete:(FBDialog *)dialog
Hope it helps....