MultipeerConnectivity Session management - ios7

I am really stuck at the moment trying to get the grasp of invites in the MultipeerConnectivityFramework.
Right now I have an ipad App acting as the Advertiser and an iphone App acting as Browser.
I have implemented a sharedService for the MultipeerFramework and did the following:
Advertiser
#implementation MultipeerConnectivityService {
MCNearbyServiceAdvertiser *_advertiser;
MCSession *_session;
MCNearbyServiceBrowser *_browser;
}
- (void)automaticAdvertiseWithName:(NSString *)name {
MCPeerID *peerID = [[MCPeerID alloc] initWithDisplayName:name];
_session = [[MCSession alloc] initWithPeer:peerID];
_session.delegate = self;
_advertiser = [[MCNearbyServiceAdvertiser alloc] initWithPeer:peerID discoveryInfo:nil serviceType:kServiceType];
_advertiser.delegate = self;
[_advertiser startAdvertisingPeer];
}
- (void)advertiser:(MCNearbyServiceAdvertiser *)advertiser didReceiveInvitationFromPeer:(MCPeerID *)peerID withContext:(NSData *)context invitationHandler:(void (^)(BOOL, MCSession *))invitationHandler {
invitationHandler([#YES boolValue], _session);
NSLog(#"Invitation accepted");
}
Browser
- (void)automaticBrowsingWithName:(NSString *)name {
MCPeerID *peerID = [[MCPeerID alloc] initWithDisplayName:name];
_browser = [[MCNearbyServiceBrowser alloc] initWithPeer:peerID serviceType:kServiceType];
_browser.delegate = self;
[_browser startBrowsingForPeers];
}
- (void)browser:(MCNearbyServiceBrowser *)browser didNotStartBrowsingForPeers:(NSError *)error {
if ([_delegate respondsToSelector:#selector(browser:didNotStartBrowsingForPeers:)]) {
[_delegate browserDidNotStartBrowsingForPeers];
}
}
- (void)browser:(MCNearbyServiceBrowser *)browser foundPeer:(MCPeerID *)peerID withDiscoveryInfo:(NSDictionary *)info {
[browser invitePeer:peerID toSession:[self getMCSession] withContext:nil timeout:10];
if ([_delegate respondsToSelector:#selector(browser:foundPeer:)]) {
[_delegate browser:browser foundPeer:peerID];
}
}
- (void)browser:(MCNearbyServiceBrowser *)browser lostPeer:(MCPeerID *)peerID {
if ([_delegate respondsToSelector:#selector(browserLostPeer:)]) {
[_delegate browserLostPeer:peerID];
}
}
- (MCSession *) getMCSession {
return _session;
}
But then I am getting as feedback in the console:
-[MCNearbyServiceBrowser invitePeer:toSession:withContext:timeout:] Bad argument session=nil
When I check for the found Advertisers, everything is OK. My advertiser ipad is being found. But how can I manage the invite?
So I don't get it right now... When I send an invitation by the browser, what session do I have to use? On the iPad I set up the session like you can see in the "automaticAdvertiseWithName" method. but on the iphone I don't do this when calling "automaticBrowsingWithName"... Is that the problem? And don't they have to be the same session to successfully connect them? And how can I successfully invite my advertiser ipad to the browser?
I am confused by the notion of creating a new session when one has already been created by the advertiser.
I am actually not sure, if the delegate didReceiveInvitation is adding the peer to the connectedPeers at all.
- (void)automaticAdvertiseWithName:(NSString *)name {
MCPeerID *peerID = [[MCPeerID alloc] initWithDisplayName:name];
self.session = [[MCSession alloc] initWithPeer:peerID];
self.session.delegate = self;
_advertiser = [[MCNearbyServiceAdvertiser alloc] initWithPeer:peerID discoveryInfo:nil serviceType:kServiceType];
_advertiser.delegate = self;
[_advertiser startAdvertisingPeer];
}
- (void)advertiser:(MCNearbyServiceAdvertiser *)advertiser didReceiveInvitationFromPeer:(MCPeerID *)peerID withContext:(NSData *)context invitationHandler:(void (^)(BOOL, MCSession *))invitationHandler {
BOOL accept = YES;
invitationHandler(accept, self.session);
NSLog(#"Invitation accepted: %#", self.session);
}
And when I call the property "connectedPeers" on my session, there are no connected peers at all, even though the delegate found one. Did I make a mistake there?

Your problem is that your session is null at the time you call invitePeer:toSession:withContext:timeout...Anyway you have two options to fix this.
You have two options :
Option 1
- move the peerID creation, session creation and session delegate assignment in a place where its executed at all time. For example in the init code for MultipeerConnectivityService class of if its a UIViewController in the viewDidLoad.
Option 2
- add the following snippet before you call "invitePeer:toSession:withContext:timeout:"
if (!_session) {
MCPeerID *peerID = [[MCPeerID alloc] initWithDisplayName:#"Browser"]; // you can customize the name here
_session = [[MCSession alloc] initWithPeer:peerID];
_session.delegate = self;
}
Hope this helps ...good luck!

Related

IOS App Action extension is not closing

I am facing app extension close issues , please tell me if anyone know what wrong am doing.I am using action extension after preform some action inside extension i need to return response back.
Sample Code
// With Success Case
- (void) completeActionWithItems: (NSString *) response {
NSExtensionItem *extensionItem = [[NSExtensionItem alloc] init];
extensionItem.attachments = #[[[NSItemProvider alloc] response typeIdentifier: (NSString *)kUTTypePlainText]];
[self.extensionContext completeRequestReturningItems: #[extensionItem] completionHandler: nil];
}
// With Error Case
- (void) completeActionWithError: (NSError *) error {
[self.extensionContext cancelRequestWithError: error];
}
With Success Case working fine but some time is not closing,
With Error Case not working above code.
Please let me know what went wrong.Thanks
When you create an action extension, this is the default method which will close the Action Extension View Controller:
- (IBAction)done {
// Return any edited content to the host app.
// This template doesn't do anything, so we just echo the passed in items.
[self.extensionContext completeRequestReturningItems:self.extensionContext.inputItems completionHandler:nil];
}
Since this method is already provided, you should just try calling it from your success method.
// With Success Case
- (void) completeActionWithItems: (NSString *) response {
NSExtensionItem *extensionItem = [[NSExtensionItem alloc] init];
extensionItem.attachments = #[[[NSItemProvider alloc] response typeIdentifier: (NSString *)kUTTypePlainText]];
[self.extensionContext completeRequestReturningItems: #[extensionItem] completionHandler: nil];
// Call to "done" method
[self done];
}

How to interrupt AVPlayer for incoming call manually

I've used this method How do I get my AVPlayer to play while app is in background? to have an AVPlayer play in the background so the user can listen while browsing safari etc. Only problem is now AVPlayer continues to play when there is an incoming call and stays playing during the call. Is there a way to catch the incoming call and end call events so AVPlayer can be stopped and started manually?
Through coreTelephony framework we have to find or detect the incoming call. From there you have to initiate your local notification to stop your AVPlayer.
after importing do like this
CTCallCenter * _callCenter = [[CTCallCenter alloc] init];
_callCenter.callEventHandler = ^(CTCall* call)
{
if ([call.callState isEqualToString:CTCallStateDisconnected])
{
NSLog(#"Call has been disconnected");
}
else if([call.callState isEqualToString:CTCallStateDialing])
{
NSLog(#"Call start");
}
else if ([call.callState isEqualToString:CTCallStateConnected])
{
NSLog(#"Call has just been connected");
}
else if([call.callState isEqualToString:CTCallStateIncoming])
{
NSLog(#"Call is incoming");
// You have to initiate/post your local notification through NSNotification center like this
[[NSNotificationCenter defaultCenter] postNotificationName:#"stopAVPlayer" object:nil];
} else
{
NSLog(#"None of the conditions");
}
};
Refer this :https://developer.apple.com/library/ios/navigation/#section=Frameworks&topic=CoreTelephony
You could use the CallKit for getting phone call event now. (iOS 10.0+)
This is good for me.
#import <CallKit/CallKit.h>
#interface ViewController ()<CXCallObserverDelegate> {
CXCallObserver *_center;
}
-(void)viewDidLoad
_center = [[CXCallObserver alloc] init];
dispatch_queue_t queue = dispatch_queue_create("THIS_IS_A_CALL",NULL);
[_center setDelegate:self queue:queue];
Delegate
- (void)callObserver:(CXCallObserver *)callObserver callChanged:(CXCall *)call {
NSString *tag = #"callObserver";
NSString *content = #"";
if (call.isOutgoing) {
content = #"call.isOutgoing";
}
if (call.hasEnded) {
content = #"call.hasEnded";
}
if (call.hasConnected) {
content = #"call.hasConnected";
}
if (call.isOnHold) {
content = #"call.isOnHold";
}
NSLog(#"%# - %#", tag, content);
}
More detail: https://developer.apple.com/documentation/callkit

ShareKit: Customizing text for different sharers (SHKActionSheet)

According to the official FAQ from ver.2 to customize your text/content depending on what sharer was selected by the user, you need:
subclass from SHKActionSheet and override
dismissWithClickedButtonIndex
set your new subclass name in
configurator (return it in (Class)SHKActionSheetSubclass;).
It doesn't work for me. But even more: I put
NSLog(#"%#", NSStringFromSelector(_cmd));
in (Class)SHKActionSheetSubclass to see if it's even got called. And it's NOT ;(( So ShareKit doesn't care about this config option...
Has anybody worked with this before?
thank you!
UPD1: I put some code here.
Here's how my subclass ITPShareKitActionSheet looks like. According to the docs I need to override dismissWithClickedButtonIndex:animated:, but to track if my class gets called I also override the actionSheetForItem::
+ (ITPShareKitActionSheet *)actionSheetForItem:(SHKItem *)item
{
NSLog(#"%#", NSStringFromSelector(_cmd));
ITPShareKitActionSheet *as = (ITPShareKitActionSheet *)[super actionSheetForItem:item];
return as;
}
- (void)dismissWithClickedButtonIndex:(NSInteger)buttonIndex animated:(BOOL)animate
{
NSLog(#"%#", NSStringFromSelector(_cmd));
NSString *sharersName = [self buttonTitleAtIndex:buttonIndex];
[self changeItemForService:sharersName];
[super dismissWithClickedButtonIndex:buttonIndex animated:animate];
}
And here's what I do in code to create an action sheet when user presses 'Share' button:
- (IBAction)shareButtonPressed:(id)sender
{
// Create the item to share
SHKItem *item = [SHKItem text:#"test share text"];
// Get the ShareKit action sheet
ITPShareKitActionSheet *actionSheet = [ITPShareKitActionSheet actionSheetForItem:item];
// Display the action sheet
[actionSheet showInView:self.view]; // showFromToolbar:self.navigationController.toolbar];
}
When I run this code, press 'Share' button and select any sharer I expect to get two lines in log:
actionSheetForItem: - custom action sheet got created
dismissWithClickedButtonIndex:animated: - custom mechanics to
process action sheet's pressed button got called.
But for some reason I get only the first line logged.
I was having the same issues but I've suddenly got it to call my Subclass successfully.
Firstly My Configurator is setup as so:
-(Class) SHKActionSheetSubclass{
return NSClassFromString(#"TBRSHKActionSheet");
}
Now My Subclass:
.h File
#import "SHKActionSheet.h"
#interface TBRSHKActionSheet : SHKActionSheet
#end
.m implementation override:
#import "TBRSHKActionSheet.h"
#import "SHKActionSheet.h"
#import "SHKShareMenu.h"
#import "SHK.h"
#import "SHKConfiguration.h"
#import "SHKSharer.h"
#import "SHKShareItemDelegate.h"
#implementation TBRSHKActionSheet
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
+ (SHKActionSheet *)actionSheetForItem:(SHKItem *)i
{
NSLog(#"%#", NSStringFromSelector(_cmd));
SHKActionSheet *as = [self actionSheetForType:i.shareType];
as.item = i;
return as;
}
- (void)dismissWithClickedButtonIndex:(NSInteger)buttonIndex animated:(BOOL)animated
{
NSInteger numberOfSharers = (NSInteger) [sharers count];
// Sharers
if (buttonIndex >= 0 && buttonIndex < numberOfSharers)
{
bool doShare = YES;
SHKSharer* sharer = [[NSClassFromString([sharers objectAtIndex:buttonIndex]) alloc] init];
[sharer loadItem:item];
if (shareDelegate != nil && [shareDelegate respondsToSelector:#selector(aboutToShareItem:withSharer:)])
{
doShare = [shareDelegate aboutToShareItem:item withSharer:sharer];
}
if(doShare)
[sharer share];
}
// More
else if ([SHKCONFIG(showActionSheetMoreButton) boolValue] && buttonIndex == numberOfSharers)
{
SHKShareMenu *shareMenu = [[SHKCONFIG(SHKShareMenuSubclass) alloc] initWithStyle:UITableViewStyleGrouped];
shareMenu.shareDelegate = shareDelegate;
shareMenu.item = item;
[[SHK currentHelper] showViewController:shareMenu];
}
[super dismissWithClickedButtonIndex:buttonIndex animated:animated];
}
Finally on my implementation file I've not modified the call to SHKActionSheet as Vilem has suggested because of some dependancies that seemed to cause conflicts for me.
So this is my caller (straight from tutorial):
NSURL *url = [NSURL URLWithString:#"http://getsharekit.com"];
SHKItem *item = [SHKItem URL:url title:#"ShareKit is Awesome!" contentType:SHKURLContentTypeWebpage];
// Get the ShareKit action sheet
SHKActionSheet *actionSheet = [SHKActionSheet actionSheetForItem:item];
// ShareKit detects top view controller (the one intended to present ShareKit UI) automatically,
// but sometimes it may not find one. To be safe, set it explicitly
[SHK setRootViewController:self];
// Display the action sheet
[actionSheet showFromToolbar:self.navigationController.toolbar];
This Calls no problems for me.
edit: by far the best way to achieve this is to use SHKShareItemDelegate. More info is in ShareKit's FAQ.

Connect to devices on local network using cocoa

I have been trying to develop an app for my ipad which i can use to connect to my local network devices in order to get files and so on without any progress.
What i want to do is to first detect the devices on the network and then select which i want to browse files from by connecting to it.
What i got so far is
#import "BrowseForNetworkDevices.h"
#implementation BrowseForNetworkDevices
- (id)init
{
self = [super init];
if (self)
{
services = [[NSMutableArray alloc] init];
serviceBrowser = [[NSNetServiceBrowser alloc] init];
[serviceBrowser setDelegate: delegateObject];
searching = NO;
}
return self;
}
- (void)dealloc
{
[services release];
[serviceBrowser release];
[super dealloc];
}
// Sent when browsing begins
- (void)netServiceBrowserWillSearch:(NSNetServiceBrowser *)browser
{
searching = YES;
[serviceBrowser searchForServicesOfType:#" " inDomain:#"local."];
[self updateUI];
}
// Sent when browsing stops
- (void)netServiceBrowserDidStopSearch:(NSNetServiceBrowser *)browser
{
searching = NO;
[self updateUI];
}
// Sent if browsing fails
- (void)netServiceBrowser:(NSNetServiceBrowser *)browser
didNotSearch:(NSDictionary *)errorDict
{
searching = NO;
[self handleError:[errorDict objectForKey:NSNetServicesErrorCode]];
}
// Sent when a service appears
- (void)netServiceBrowser:(NSNetServiceBrowser *)browser
didFindService:(NSNetService *)aNetService
moreComing:(BOOL)moreComing
{
//[services addObject:aNetService];
if (![services containsObject:aNetService]) {
[self willChangeValueForKey:#"services"];
[services addObject:aNetService];
[self didChangeValueForKey:#"service"];
}
if(!moreComing)
{
[self updateUI];
}
}
// Sent when a service disappears
- (void)netServiceBrowser:(NSNetServiceBrowser *)browser
didRemoveService:(NSNetService *)aNetService
moreComing:(BOOL)moreComing
{
//[services removeObject:aNetService];
if ([services containsObject:aNetService]) {
[self willChangeValueForKey:#"service"];
[services removeObject:aNetService];
[self didChangeValueForKey:#"service"];
}
if(!moreComing)
{
[self updateUI];
}
}
// Error handling code
- (void)handleError:(NSNumber *)error
{
NSLog(#"An error occurred. Error code = %d", [error intValue]);
// Handle error here
}
// UI update code
- (void)updateUI
{
if(searching)
{
// Update the user interface to indicate searching
for(Class obj in services){
NSLog(#"service found %#", obj);
}
// Also update any UI that lists available services
}
else
{
// Update the user interface to indicate not searching
}
}
#end
Any good tutorials on how to perform such task?
Thanks in advance for all the help
You will need both the network devices and your iPad to be speaking the same protocol. You will need to pick an appropriate protocol. It sounds like what you want to do is file transfer - there are many protocols that can handle this that are implemented in libraries for iOS:
- WebDAV
- HTTP
- FTP
- SFTP
- AFP
- SAMBA
In order for the devices to detect one another you will need to use something like Apple's Bonjour

Twitter SDK for IPad, weired error on twitter login

when I try to share on twitter I do the following steps
if (!twitterEngine)
{
twitterEngine = [[SA_OAuthTwitterEngine alloc] initOAuthWithDelegate: self];
twitterEngine.consumerKey = kOAuthConsumerKey;
twitterEngine.consumerSecret = kOAuthConsumerSecret;
UIViewController *controller = [SA_OAuthTwitterController controllerToEnterCredentialsWithTwitterEngine: twitterEngine delegate: self];
if (controller)
[currentController presentModalViewController: controller animated: YES];
else
[self postTwitterStatus];
}
else {
[self postTwitterStatus];
}
after I got the twitter login view and after I put my pass and user and click add app
I get crash on asembly code, which I don't have an error message, but I saw something weiered also that in the delegates
- (void) OAuthTwitterController: (SA_OAuthTwitterController *) controller authenticatedWithUsername: (NSString *) username {
NSLog(#"Authenicated for %#", username);
[self postTwitterStatus];
}
I got that authintication succeeded but I get that the username == null
how can I solve this issue
Thanks
I did NOT get THIS error, but a similar one using Twitter+OAuth by Ben Gottlieb. If you are using this thing, it is required to use https and not http to connect to twitter.
go to: SA_OAuthTwitterEngine.m and change to:
- (SA_OAuthTwitterEngine *) initOAuthWithDelegate: (NSObject *) delegate {
if (self = (id) [super initWithDelegate: delegate]) {
//CHANGED FROM 'HTTP' TO 'HTTPS':
self.requestTokenURL = [NSURL URLWithString: #"https://twitter.com/oauth/request_token"];
self.accessTokenURL = [NSURL URLWithString: #"https://twitter.com/oauth/access_token"];
self.authorizeURL = [NSURL URLWithString: #"https://twitter.com/oauth/authorize"];
}
return self;
}
give it a try, i'm not sure it's the same as your problem..
Every thing looks fine to me. check with this example and see weather you are getting username in this.