iOS Multi peer connectivity showing same device name twice - ios

I am using iOS 7 multi peer technology for connecting my iPad and iPod touch. But whenever iPod touch or iPad goes to sleep it gets disconnected which is fine because multi peer dont work in background mode, but when i discover again it shows iPods name twice in the MCBrowserViewController list. Tried this with every sample code and every code has same issue any one know how to fix this bug.
Also there is one weird issue with MCBrowserViewController if i connect a device and other device accepts it, even though it gets connected MCBrowserViewController will still show as connecting and "Done" button is disabled. I am using MCBrowserViewController and no custom code for this so i guess this is issue from apple.
Also any one knows how to directly connect to the device when app comes back to active state from sleep mode?

Discovering your same name twice is because you do "init" the peerID (withDisplayName) each time you init your session.
From apple's documentation, it's a known bug and you should not do so. Rather, save your peerID somewhere (such as NSUserDefaults), and when you init your session, verify if peerID exists, load it, else create/save it.
The simplest code will look like this:
In the init of your session, replace:
_peerID = [[MCPeerID alloc] initWithDisplayName:XXX];
by:
//If there is no PeerID save, create one and save it
if ([[NSUserDefaults standardUserDefaults] dataForKey:#"PeerID"] == nil)
{
_peerID = [[MCPeerID alloc] initWithDisplayName:XXX];
[[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:_peerID] forKey:#"PeerID"];
}
//Else, load it
else
{
_peerID = [NSKeyedUnarchiver unarchiveObjectWithData:[[NSUserDefaults standardUserDefaults] dataForKey:#"PeerID"]];
}
Of course, you can make a more sophisticated code, such deallocating it and create it from a dynamic variable in case you wanna change name, etc.

I had the same issue and this is how I solved it,,
In my case I used a UIViewController to handle the connections and every time I open the view I alloc and init the view -viewDidLoad will be called each time- , then in viewDidLoad I initial the MCPeerID & MCSession and thats the problem and this is why we see multi peer connectivity showing twice, so I solved it by doing the initialisation of MCPeerID & MCSession only once in the AppDelegate or a global Class.

Related

Alamofire - detect internet connection

How to detect connection to internet?
I have a viewController that displays data fetched from a server. It is presented modally.
I display a message asking for an internet connection if a user is not connected to the internet. Otherwise, I show the data.
When a user connects to the internet without leaving the app, how to detect it? I assume it is possible to do it in Alamofire.
I have used the following code in viewDidLoad and viewWillAppear, but the listener is not called - If I connect & disconnect to WIFI, startListening is not called.
let network = NetworkReachabilityManager()
network!.startListening { status in
if status == .reachable(.cellular) || status == .reachable(.ethernetOrWiFi) {
self.fetchProducts()
} else {
self.noInternetConnnection()
}
}
FYI, you can unable both WIFI and Mobile Data in control center.
If you're declaring network inside a function it's probably being deallocated as soon as the scope ends.
Make sure you store it in an instance variable.

When you close the application, boolean not set to no

I use Firebase in the application. I want to record the user's status in the database, it is currently in the application or not. I use the code from the official document, which must ensure this task. When the application is opened, the value is set to NO, and then changed to YES. But at the close of the application as well as the transition to the background, the block is not called and the value does not change. Here is my code...
FIRDatabaseReference *connectedRef = [[FIRDatabase database] referenceWithPath:#".info/connected"];
[connectedRef observeEventType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot * _Nonnull snapshot) {
if([snapshot.value boolValue]) {
NSLog(#"CONNECTED");
} else {
NSLog(#"NOT CONNECTED");
}
}];
2017-01-17 13:36:38.203 Tricker[6293:817528] NOT CONNECTED
2017-01-17 13:36:40.863 Tricker[6293:817528] CONNECTED
The console comes the following information, which shows that the value changes after two seconds after the opening of the application with NO to YES. But nothing happens when the application is closed...
Prompt in what could be the problem?
You seem to be confusing connection state and application lifecycle.
When it is active, the Firebase Database client will fire information about its connection state on the .info/connected path.
But when the app is not active, the client has no way of firing this information anymore. You will need to use regular iOS application lifecycle events to detect when the app becomes inactive.
Recording the user's presence in the database works as a two-step process:
when the application starts you write a value to the database to signal that the user is online
when the application starts, you tell the database server to write another value to the database that signals that the user is gone.
Step 2 is accomplished by onDisconnectSetValue:
[presenceRef onDisconnectSetValue:#"I disconnected!"];
The trick is that you call this method early on, typically when your app starts. The write operation will be executed when the Firebase Database server detects that the client disconnected.
This disconnect can happen in two ways:
when the client closes the connection explicitly
when the the socket that the server uses to communicate with the client times out
When your app crashes, you're in situation 2. In that case it can take a few minutes before the server detects that the client is gone, since you're waiting for a socket to time-out.
You close the app means ? two things possibility
User can put app in Background :
Here you can set a NSUSEDefault value like :
(void)applicationDidEnterBackground:(UIApplication *)application {
[[NSUserDefaults standardUserDefaults] setBool:NO
forKey:#"YOUR_KEY"]; [[NSUserDefaults standardUserDefaults]
synchronize];
}
User can remove app from background :
(void)applicationWillTerminate:(UIApplication *)application {
[[NSUserDefaults standardUserDefaults] setBool:NO forKey:#"YOUR_KEY"];
[[NSUserDefaults standardUserDefaults] synchronize];
}

watchOS 2.0 - Can't cancel WCSessionFileTransfer

Our app lets a user select records on iPhone that they want to be displayed in the watch app.
It works like this:
The user taps "Add to watch" on a record from their iPhone
A new version of the watch database is generated and sent to the watch
The watch app receives and saves the file and updates its interface
A new database file is sent to the watch and processed for each change. This is fine if the watch is awake since it will give the user live updates, but if the watch is asleep while the user makes 7 changes, it means the watch is accepting and processing 7 new files as soon as it wakes up.
We really only care about the most recent version of the watch database, so I'm trying to cancel all old outstanding file transfers.
Code:
On iPhone, each time a record is added/removed from watch database, we attempt (unsuccessfully) to cancel pending file transfers and then queue the latest database file:
// create watch database and store it at self.urlToDatabase
[self generateNewWatchDatabase];
if ([WCSession isSupported])
{
WCSession *session = [WCSession defaultSession];
session.delegate = self;
[session activateSession];
// this is the problem - cancel doesn't seem to do anything
for (WCSessionFileTransfer *fileTransfer in session.outstandingFileTransfers)
[fileTransfer cancel];
[session transferFile:self.urlToDatabase metadata:nil];
}
In the above code, calling [fileTransfer cancel] successfully removes the WCSessionFileTransfer object from session.outstandingFileTransfers, but didReceiveFile is still being called multiple times below.
Accepting the file on the watch:
- (void)session:(WCSession *)session didReceiveFile:(WCSessionFile *)file
{
// this method gets called once for every time -transferFile:metadata: is called above,
// even after cancelling outstanding file transfers
[self replaceDatabaseWithFile:file];
[self refreshItemsTable];
}
How do we cancel outstanding file transfers?
Edit
As per #ccjensen's recommendation, I tried the following in the method that fires when the user adds/removes a record to/from the watch:
// store a reference to the file transfer and immediately cancel it
WCSessionFileTransfer *transfer = [session transferFile:self.urlToDatabase metadata:nil];
[transfer cancel];
This still results in the file being sent to the watch, instead of cancelling it as one would expect.
I also tried the following:
Kill watch app (by holding the side button until 'Power Off' appears, and then holding it again)
Add/remove records from iPhone
Relaunch watch app
Even in this scenario the watch receives all 'cancelled' file transfers.
The documentation for the cancel method says:
Use this method to cancel a file transfer before it completes. If the file has already been transferred, calling this method has no effect.
So it sounds like the cancels are "best effort" and might not end up being able to cancel them in all cases, especially if the file has already been transferred.
Are you seeing it never work, even if you call cancel immediately (try testing without the watch app running as that seems to expedite the transfers)?
turned out the reason this worked was that the file url did not match the transfer's url I was checking 🙈
I recently found that keeping hold of the WCSessionFileTransfer in my own array and canceling them proved more reliable than using [WCSession defaultSession].outstandingFileTransfers.
NSMutableArray<WCSessionFileTransfer*>* inProgressTransfers = [NSMutableArray array];
So each time you call TransfeFile: metaData:
WCSessionFileTransfer* transfer = [[WCSession defaultSession] transferFile:url metadata:metadata];
if (transfer)
{
[self.inProgressTransfers addObject:transfer];
}
then at an appropriate time
for (WCSessionFileTransfer* ourTransfer in self.inProgressTransfers)
{
[ourTransfer cancel];
[self.inProgressTransfers removeObject:ourTransfer];
}
For some reason, keeping hold of the transfers ourself makes calling cancel work much more reliably.. hope that helps someone

Why won't disconnect work?

I'm writing an application which is based on Apple's Temperature Sensor Application for iOS devices. I'm trying to implement a Disconnect button which will disconnect the currently connected device from the iPhone, however when the disconnect button is pressed there is a BAD_ACCESS error, I know this is memory based but I'm completely at a loss on how to fix it. Code follows.
- (IBAction)clickbutton:(id)sender
{
[[LEConnect sharedInstance] startScan:AccelerometerServiceUUID];
}
- (IBAction)disconnectButton:(id)sender
{
CBPeripheral *peripheral;
if(CBPeripheralStateDisconnected)
{
[[LEConnect sharedInstance] disconnectPeripheral:peripheral];
}
}
The startScan button works correctly but the disconnect button does not. The code in the button is based on the code for finding devices shown below:
if (CBPeripheralStateConnected)
{
[[LEConnect sharedInstance] connectPeripheral:peripheral];
[currentlyConnectedDevice setText:[peripheral name]];
}
earlier in this function the same CBPeripheral *peripheral; pointer is made.
Sorry if this is a dumb question or has been asked before, I'm just really struggling and in desperate need of help! Thanks
The disconnectButton method has two errors. First the peripheral variable is used without being initialized (are you ignoring compiler warnings?). Second, the if statement checks if the peripheral is disconnected and then disconnects it again (you should be checking that the peripheral is connected).

How to make gamekit connection without needing both peers to press the same button

I'm trying to develop an app where I send the details of a contact to another device. I can connect the two devices but only if the same button on both devices is pressed.
My question is: Can I use gamekit to make the app listen for devices in the background so that the listening device doesn't have to press a button to allow the searching device to find it. So basically:
Searching Device(x): Presses Button -> Finds Other Device
Listening Device(y): Device x would like to connect with you. (without needing to press a button to make itself visible)
Well, I'm not sure I'm understanding your question completely, but you can make your app visible by setting the 'available' property of the GKSession object to true.
GKSession *session = [[GKSession alloc] initWithSessionID:#"MyApp" displayName:#"Name" sessionMode:GKSessionModePeer];
session.delegate = self;
[session setDataReceiveHandler:self withContext:NULL];
session.available = YES;
Or maybe you're using the GKPicker to set up connections, and referring to the modal window that says "XX wants to connect"?
It is perfectly possible to silently auto-accept incoming connections. Take a look at the GKSessionDelegate protocol.
Basically, you do:
- (void)session:(GKSession *)session didReceiveConnectionRequestFromPeer:(NSString *)peerID
{
NSError *err;
if (![session acceptConnectionFromPeer:peerID error:&err])
//Deal with error
}

Resources