I have imported Reachability into my application, and I have a couple of how-to questions for you all. Let me explain my application and other tools first.
This application communicates with two things AT THE SAME TIME, an ad-hoc network, and the internet through 3G. Note: The ad-hoc network IS NOT connected to the internet. This works perfectly - it's already implemented and tests out wonderfully.
With that being said, I want to implement Reachability to detect a two things.
1) Is the user connected to a wifi ad-hoc network? (Even better, if possible, is to detect if it is connected to the wifi ad-hoc network with a prefix of WXYZ. For example, if there are two networks listed, one called Linksys and the other called WXYZ-Testing_Platform, it knows whether or not it is connected to WXYZ).
2) Can the user connect to the internet through 3G (or 2G, etc) and access our server?
Thanks in advance
EDIT TO INCLUDE ANSWER FOR FUTURE LOOKERS:
For 1), my code looks like this:
.h
#import <SystemConfiguration/CaptiveNetwork.h> //for checking wifi network prefix
.m
- (BOOL) connectedToWifi
{
CFArrayRef myArray = CNCopySupportedInterfaces();
// Get the dictionary containing the captive network infomation
CFDictionaryRef captiveNtwrkDict = CNCopyCurrentNetworkInfo(CFArrayGetValueAtIndex(myArray, 0));
NSLog(#"Information of the network we're connected to: %#", captiveNtwrkDict);
NSDictionary *dict = (__bridge NSDictionary*) captiveNtwrkDict;
NSString* ssid = [dict objectForKey:#"SSID"];
if ([ssid rangeOfString:#"WXYZ"].location == NSNotFound || ssid == NULL)
{
return false;
}
else
{
return true;
}
}
And for 2), I imported Reachability and have it using this method whenever I go to connect to the server... NOTE:replace http://www.google.com with the server information
-(void) checkIfCanReachServer
{
UIAlertView *errorView;
Reachability *r = [Reachability reachabilityWithHostName:#"http://www.google.com"];
NetworkStatus internetStatus = [r currentReachabilityStatus];
if(internetStatus == NotReachable) {
errorView = [[UIAlertView alloc]
initWithTitle: #"Network Error"
message: #"Cannot connect to the server."
delegate: self
cancelButtonTitle: #"OK" otherButtonTitles: nil];
[errorView show];
}
}
Reachability only lets you know if the device can send data packets out successfully. So
for 1) you should refer to iPhone get SSID without private library. For 2) you will use Reachability only to check for an internet connection then you would need to use NSURLConnection or other network library to make sure you can reach your server.
Related
I am trying to connect 2 devices using GKSession. When the devices are on same WiFi network it is working but when i am trying to connect devices on different WiFi network it is not working.
This is my code for GKSession
chatSession = [[GKSession alloc] initWithSessionID:AppName displayName:name sessionMode:GKSessionModePeer];
[chatSession setDataReceiveHandler:self withContext:nil];
chatSession.delegate = self;
chatSession.available = YES;
-(void)session:(GKSession *)session peer:(NSString *)peerID didChangeState:(GKPeerConnectionState)state{
NSLog(#"state=%d", state);
if(state == GKPeerStateDisconnected)
{
// A peer disconnected
chatSession.available = YES;
NSLog(#"Disconnected");
[data removeAllObjects];
chatInput.hidden = YES;
}
else if(state == GKPeerStateConnected)
{
// You can now send messages to the connected peer(s)
NSData *imgData = UIImageJPEGRepresentation(myImage, 0.5);
if(imgData==nil)
NSLog(#"myImage is nil");
NSError *err;
//NSLog(#"pid=%#", pID);
NSLog(#"before sending image");
[chatSession sendData:imgData toPeers:[NSArray arrayWithObject:pID] withDataMode:GKSendDataReliable error:&err];
NSLog(#"after send data");
if(err)
NSLog(#"error:%#", err.description);
}
else if (state == GKPeerStateAvailable)
{
pID = peerID;
[session connectToPeer:peerID withTimeout:60*120];
}
}
-(void)session:(GKSession *)session didReceiveConnectionRequestFromPeer:(NSString *)peerID{
// We can now decide to deny or accept
bool shouldAccept = YES;
if(shouldAccept)
{
pID = peerID;
[session acceptConnectionFromPeer:peerID error:nil];
}
else
{
[session denyConnectionFromPeer:peerID];
}}
I am not sure if it is possible using GKSession.
According to Apple
A GKSession object provides the ability to discover and connect to nearby iOS devices using Bluetooth or Wi-fi.
Please help me ia m trying to create a chat app using GKSession.
iOS' GameKit most probably uses Bonjour for this, which works by doing broadcasts on a local network. It won't work between two separate Wi-Fi networks.
This means you'll need a central server (at least during connection set-up). Have a look at XMPP, if you haven't done so already.
Good luck!
I was using the following code for checking the internet connection in my first iOS app (almost 3 years ago).
NSError *err = nil;
NSStringEncoding encoding;
NSString * connectionstring = [[NSString alloc] initWithContentsOfURL:[NSURL URLWithString:#"http://www.google.com"] usedEncoding:&encoding error:&err];
if(connectionstring.length ==0)
{
UIAlertView * alert = [[UIAlertView alloc] initWithTitle:#"Error Occured" message:#"No Internet Connection." delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
[alert release];
}
else
{
// loading another screen
}
The problem is in India and US it is going into the else condition but in Belgium it is going into the if condition and throwing the error. So are there any restrictions for Google in Belgium?
If it is tagged incorrectly please guide me. In my current versions solved this issue by using Rechability classes to check this . Just I wanted to know about it . Thanks in advance.
As a first step, look at the encoding.
You can log the encoding right after the connectionstring is created by inserting
NSLog(#"encoding: %#", [NSString localizedNameOfStringEncoding:encoding]);
into your code.
For further debugging, you could try loading the url into an NSData object first, then inspect the data and finally create a NSString from the data.
Alternative solution: To check internet connection Use Reachability class provided by apple.
Download Apple Sample Here, add Reachability class to your project.
- (BOOL)isConnected
{
Reachability *reachability = [Reachability reachabilityForInternetConnection];
NetworkStatus networkStatus = [reachability currentReachabilityStatus];
return !(networkStatus == NotReachable);
}
Instead of using google.com use any local client server test url for internet checking it will also be very fast responsive comparatively of any other way of internet validation.
Is it possible to run a method that will return the name of the wireless network that the user is connected to? Inside of my app I want to be able to return the name of the wireless network that the user is connected to.
This worked perfect for me:
#import <SystemConfiguration/CaptiveNetwork.h>
CFArrayRef myArray = CNCopySupportedInterfaces();
CFDictionaryRef myDict = CNCopyCurrentNetworkInfo(CFArrayGetValueAtIndex(myArray, 0));
// NSLog(#"SSID: %#",CFDictionaryGetValue(myDict, kCNNetworkInfoKeySSID));
NSString *networkName = CFDictionaryGetValue(myDict, kCNNetworkInfoKeySSID);
if ([networkName isEqualToString:#"Hot Dog"])
{
self.storeNameController = [[StoreDataController alloc] init];
[self.storeNameController addStoreNamesObject];
}
else {
UIAlertView *alert = [[UIAlertView alloc]initWithTitle: #"Connection Failed"
message: #"Please connect to the Hot Dog network and try again"
delegate: self
cancelButtonTitle: #"Close"
otherButtonTitles: nil];
[alert show];
From Developer.apple , you can use CNCopyCurrentNetworkInfo
It Returns the current network info for a given network interface.
CFDictionaryRef CNCopyCurrentNetworkInfo (
CFStringRef interfaceName
);
It contains a dictionary that containing the interface’s current network info. Ownership follows the Create Rule.
Note:Available in iOS 4.1 and later.
EXAMPLE:
This example will work fine in real device, It may crash in simulator.
Add SystemConfiguration.framework
Import CaptiveNetwork header same as below
#import <SystemConfiguration/CaptiveNetwork.h>
Then write the below code.
CFArrayRef myArray = CNCopySupportedInterfaces();
// Get the dictionary containing the captive network infomation
CFDictionaryRef captiveNtwrkDict = CNCopyCurrentNetworkInfo(CFArrayGetValueAtIndex(myArray, 0));
NSLog(#"Information of the network we're connected to: %#", captiveNtwrkDict);
NSDictionary *dict = (__bridge NSDictionary*) captiveNtwrkDict;
NSString* ssid = [dict objectForKey:#"SSID"];
NSLog(#"network name: %#",ssid);
or
Using Bonjour, the application both advertises itself on the local network and displays a list of other instances of this application on the network
See the sample Witap application
The BAD NEWS: 'CNCopyCurrentNetworkInfo' is deprecated: first deprecated in iOS 9.0 - For captive network applications, this has been completely replaced by <NetworkExtension/NEHotspotHelper.h>. For other applications, there is no direct replacement. Please file a bug describing your use of this API to that we can consider your requirements as this situation evolves.
The GOOD NEWS: although deprecated, it still works in iOS 9, AND it has been un-deprecated in iOS 10.
It still works in iOS 13, but only if the app implements Location Services and the user authorizes their usage.
Here i have a very interesting topic for discussion or you can say i need a suggestion for the better approach.
Here is my code
+ (BOOL) isConnected
{
BOOL flag = TRUE;
if (![self isHostReachable])
{
flag = FALSE;
NSString* alertTitle= #"";
NSString* alertMessage= #"";
if (![self isInternetReachable])
{
alertTitle = #"Network unavailable";
alertMessage = #"We can't connect to the Internet. Check your settings/connection.";
}
else
{
alertTitle = #"Server not responding";
alertMessage = #"Server not responding at the moment. Please try again later. Sorry for inconvenience";
}
UIAlertView *alert=[[UIAlertView alloc] initWithTitle:alertTitle
message:alertMessage
delegate:self
cancelButtonTitle:#"Close"
otherButtonTitles:nil];
[alert show];
}
return flag;
}
+ (BOOL) isInternetReachable
{
Reachability *netReachability = [Reachability reachabilityForInternetConnection];
NetworkStatus netSat = [netReachability currentReachabilityStatus];
return (!(netSat == NotReachable));
}
+ (BOOL) isHostReachable
{
Reachability *hostReachability = [Reachability reachabilityWithHostname: [Connection returnHostName]];
NetworkStatus netSat = [hostReachability currentReachabilityStatus];
return (!(netSat == NotReachable));
}
In my code, I used to call the "isConnected" method in order to check the connectivity status, before asking my server for data.
In the method, I am checking the hostReachability first of all. My thinking behind doing this is to save the computation time.
If the hostReachability returns true it implies netConnectivity is also there(no need to verify). So here we are done with just one computation.
If the hostReachability returns false, then I am checking is it the net connectivity thats responsible for it. Hence two computations.
But it usually seen that the code first verifies the netConnectivity, if its ok, then verify the hostReachability.
So most of the times Two computations, thats in contrast with my way of getting the thing done.
Please suggest which one you think is better approach?
Well netConnectivity would verify if the network is in position and not down due to any of the 100 reasons, then the host is verified to be reachable as the server may be down due to some issue.
If the network is verified as failed connection it will rule out verifying the host in the first place. vice-versa is not true though..
I hope that was useful as explanation to you.
Cheers!!
I would like to check "reachability" before/when a webView is shown in my app.
So far I've included the reachbility.h/.m files as well as added the SystemConfiguration.framework to the project.
And that's about where the agreements I have found on the internet end, from all of the posts and blogs etc. etc. everyone has a different idea/opinion of what to do after that. Additionally, I have found a lot partial code snippets that aren't really a complete solution, on which reachibility methods to call etc. etc. how to use them etc.
I have also found that some warn that you should try to reconnect before checking reachability...but I haven't found a consensus or a full solution. My app seems to reconnect to wifi without any extra code... so I'm a litte confused here too...
Any help to clear the muddy waters would be appreciated. I'm just looking for a simple straightforward solution.
Answer Accepted: I would like to note to newbies who may read this q/a later... that you will want to do the following:
Add this into your .h file:
- (BOOL) connectedToNetwork: (NSString *) remoteServer;
- (void) appLoadError: (NSString *) altertTitle alertMessage: (NSString *) altertMsg;
And you will need to import these at the top of your .m file:
sys/socket.h
netinet/in.h
netinet6/in6.h
arpa/inet.h
ifaddrs.h
netdb.h
SystemConfiguration/SystemConfiguration.h
Correct me if it is wrong... It seems to work fine for me...
I have always used this method in my App Delegate when I need to require Internet access. I have tuned it for different access types over time and it has served me well. It is a variant of one of the many methods you can find after a quick Google search on this topic.
It is a tricky thing to come up with a hard and fast strategy around this. The platform itself offers different connectivity options that have pros and cons based on the needs of each specific application. The method I use below is just a general connectivity test meaning the device can reach the Internet via some connectivity mechanism.
- (BOOL) connectedToNetwork: (NSString *) remoteServer {
// Create zero addy
struct sockaddr_in zeroAddress;
bzero(&zeroAddress, sizeof(zeroAddress));
zeroAddress.sin_len = sizeof(zeroAddress);
zeroAddress.sin_family = AF_INET;
// Recover reachability flags
SCNetworkReachabilityRef defaultRouteReachability = SCNetworkReachabilityCreateWithAddress(NULL, (struct sockaddr *)&zeroAddress);
SCNetworkReachabilityFlags flags;
BOOL didRetrieveFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags);
CFRelease(defaultRouteReachability);
if (!didRetrieveFlags){
NSLog(#"Error. Could not recover network reachability flags");
return NO;
}
BOOL isReachable = flags & kSCNetworkFlagsReachable;
BOOL needsConnection = flags & kSCNetworkFlagsConnectionRequired;
BOOL nonWiFi = flags & kSCNetworkReachabilityFlagsTransientConnection;
NSURL *testURL = [NSURL URLWithString: remoteServer];
NSURLRequest *testRequest = [NSURLRequest requestWithURL:testURL cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:20.0];
NSURLConnection *testConnection = [[NSURLConnection alloc] initWithRequest:testRequest delegate:self];
return ((isReachable && !needsConnection) || nonWiFi) ? (testConnection ? YES : NO) : NO;
}
I usually call this inside applicationDidFinishLaunching. If the check returns false, I usually generate an error message (see below). This is because Apple forbids exiting the application by any means other than the home button on the device.
...
else if(![self connectedToNetwork: [NSString stringWithFormat: #"http://%#:%#/", sharedSettings.server, sharedSettings.port]]){
[self appLoadError: #"No Internet Detected" alertMessage:#"This application requires an active Internet connection. No content is available."];
}
...
- (void) appLoadError: (NSString *) altertTitle alertMessage: (NSString *) altertMsg {
UIAlertView *loadErr = [[UIAlertView alloc] initWithTitle: altertTitle message: altertMsg delegate: self cancelButtonTitle: #"OK" otherButtonTitles: nil];
[loadErr show];
[loadErr release];
// Load static screen
UIImage *image = [UIImage imageNamed:#"Error_NoInternet.png"];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
[controller setNavigationBarHidden: YES animated: NO];
[window addSubview: imageView];
[imageView release];
}
Just a quick note, I believe that method is leaking an NSURLConnection on every call (at the bottom, testConnection).
You might want to use
+ (NSURLConnection *)connectionWithRequest:
(NSURLRequest *)request delegate:(id)delegate
instead.