I make use of Apples Reachability class and it works if I keep it on the main thread(bad approach). If I instead move it to a separate thread the notification is never called.
In didFinishLaunchingWithOptions i call the following:
[NSThread detachNewThreadSelector:#selector(checkConnection) toTarget:self withObject: nil];
checkConnection looks like follows:
-(void)checkConnection {
//Test for Internet Connection
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(reachabilityChanged:) name:kReachabilityChangedNotification object:nil];
Reachability *r = [[Reachability reachabilityWithHostName:#"appspot.com"] retain];
[r updateReachability:appDelegate.reachability];
[r startNotifier];
[pool release];
}
and reachabilityChanged looks like this:
- (void)reachabilityChanged:(NSNotification *)note {
Reachability* curReach = [note object];
NSParameterAssert([curReach isKindOfClass: [Reachability class]]);
[self updateReachability: curReach];
}
and finally updateReachability looks like this:
- (void)updateReachability:(Reachability *)curReach {
NetworkStatus internetStatus = [curReach currentReachabilityStatus];
if ((internetStatus != ReachableViaWiFi) && (internetStatus != ReachableViaWWAN)) {
NSLog(#"No net");
} else {
NSLog(#"Lots of net");
}}
Hope you guys can help me to understand why reachabilityChanged is never called.
Cheers...
I've used Apple's reachability example for the Mac and have had it working fine. You are suppose to run reachability on the main thread. The whole point of the reachability is that you don't have to pool for an internet connection. The system automatically spawns a background thread to monitor for changes in interent connection and will notify you of any changes.
Have your app ever stalled because of reachability or are you just postulating that it might happen?
See this answer:
Reachability sometimes fails, even when we do have an internet connection
Related
I'm doing this test app where I want to receive notification when the iPod changes the now playing item (song), the test is working nice while app is in foreground but as soon as the app goes to the background it stop getting notifications which is OK, when I tap on the app again (comes to foreground) I get all notifications according to all the times the now playing changed while the app was in background but everytime I'm getting the same song information, so how can I get the correct song information for each notification?
This is the test I did, in the AppDelegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
MPMusicPlayerController *player = [MPMusicPlayerController iPodMusicPlayer];
[notificationCenter addObserver:self
selector:#selector(nowPlayingItemChanged:)
name:MPMusicPlayerControllerNowPlayingItemDidChangeNotification
object:player];
[player beginGeneratingPlaybackNotifications];
return YES;
}
-(void) nowPlayingItemChanged:(NSNotification *)notification {
MPMusicPlayerController *player = (MPMusicPlayerController *)notification.object;
MPMediaItem *song = [player nowPlayingItem];
if (song) {
NSString *title = [song valueForProperty:MPMediaItemPropertyTitle];
NSString *album = [song valueForProperty:MPMediaItemPropertyAlbumTitle];
NSString *artist = [song valueForProperty:MPMediaItemPropertyArtist];
NSString *playCount = [song valueForProperty:MPMediaItemPropertyPlayCount];
NSLog(#"title: %#", title);
NSLog(#"album: %#", album);
NSLog(#"artist: %#", artist);
NSLog(#"playCount: %#", playCount);
}
}
See this post your options in the background are pretty restricted:
StackOverFlow Post
And the Apple Docs regarding that state it is not really possible:
Apple Documentation on Background states
Be sure to remove the observer when going into the background:
[[NSNotificationCenter defaultCenter] removeObserver:self name:MPMusicPlayerControllerNowPlayingItemDidChangeNotification object:musicPlayer];[player endGeneratingPlaybackNotifications];
Add it again when entering the foreground.
I would like to know if there's an automatic way of knowing when the connection was recovered.
My app connects to a webservice, lets say the network is not available in that moment so the app won't get the info from the server, but I would like the app to automactily try to reconect to the server if it "feels" that the connection was recovered.
Is there such a callback?
In whatever class you handle your NSURLConnection you need to add some connection check. So below I have posted an example
Create a Reachability instance
Add an observer to the Reachability did change notification
When the connection will change the - (void)networkReachabilityDidChange:(NSNotification *)notification will be fired.
You obviously check the networkStatus before firing off a connection in the first place.
-(id)init
{
self = [super init];
if(self)
{
Reachability* newInternetReachability = [Reachability reachabilityForInternetConnection];
[newInternetReachability startNotifier];
self.networkReachability = newInternetReachability;
networkStatus = [self.networkReachability currentReachabilityStatus];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(networkReachabilityDidChange:) name:kReachabilityChangedNotification object:nil];
}
return self;
}
- (void) startHTTPRequest
{
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:YOUR_URL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:YOUR_REQUEST_TIMEOUT];
NSURLConnection *urlConnection = [[NSURLConnection alloc] initWithRequest: delegate:self];
}
- (void)networkReachabilityDidChange:(NSNotification *)notification
{
Reachability *currReach = [notification object];
NSParameterAssert([currReach isKindOfClass: [Reachability class]]);
int currStatus = [currReach currentReachabilityStatus];
// Check that current reachability is not the same as the old one
if(currReach != self.networkReachability)
{
switch (currStatus) {
case ReachableViaWiFi:
// fire off connection
[self startHTTPRequest];
break;
case ReachableViaWWAN:
// Fire off connection (3G)
[self startHTTPRequest];
break;
case NotReachable:
// Don't do anything internet not reachable
break;
default:
break;
}
[self updateReachability];
}
This is only a simple example but you probably need to persist the request until the connection has become available so you can fire it off later. This could be done via NSOperationQueue or something similar.
There isn't such a thing from the standard library perspective. You will have to implement that yourself. You could use apple's Reachability code to listen for network changes. So once you receive a notification from the Reachability code saying that the internet is now connected, you could fire off an URL connection. If you need an example I could mock something up quickly for you.
I would like to know how to get local notifications while my application's NSTimer is firing in the background. My NSTimer checks a particular folder for files every second for 10 minutes in the background. How would I go about receiving a local notification if a file is found?
EDIT : Code :
- (void) createTimeThread: (float) pIntervalTime
{
[NSThread detachNewThreadSelector:#selector(startTimerThread)
toTarget:self withObject:nil];
}
- (void) startTimerThread
{
UIBackgroundTaskIdentifier bgTask = [[UIApplication sharedApplication]
beginBackgroundTaskWithExpirationHandler:^{}];
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSRunLoop* runLoop = [NSRunLoop currentRunLoop];
myTimer = [NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:#selector(conditionChecking:)
userInfo:nil
repeats:YES];
[runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
[pool release];
[[UIApplication sharedApplication] endBackgroundTask:bgTask];
}
- (void)conditionChecking:(NSIndexPath *)indexPath
{
NSString *pathForFile = #"/User/Library/Logs/CrashReporter";
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:pathForFile]) { // Directory exists
NSArray *listOfFiles = [fileManager contentsOfDirectoryAtPath:pathForFile error:nil];
if (!listOfFiles || !listOfFiles.count)
{
NSLog(#"No Core Dumps found.....");
}
else
{
NSLog(#"Core Dump(s) found! :%#", listOfFiles);
}
}
}
I believe that you want to notify all other classes that folder is filled with files.
Following steps can do that for you.
write following line in initialization of class where you want to receive notification.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(checkFiles:) name:#"FILES_AVAILABLE" object:nil];
Write methods checkFiles with following signature in same class.
-(void)checkFiles:(id)sender
Add following line in timer class when files are available.
[[NSNotificationCenter defaultCenter] postNotificationName:#"FILES_AVAILABLE" object:self];
If this is not helpful then you can use NSUserDefault to store status of application(Files are available or not in you case). OR With if you are interested in design patterns read about Observer Pattern.
In case you want to post notification when your application is in background mode and some process that is still running gets some update then that can be achieved using notification queue. read following link. I am not writing code because code is given in link itself.
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Notifications/Articles/NotificationQueues.html#//apple_ref/doc/uid/20000217-CJBCECJC
Post here if you need more help.
this code is to check if internet connection is available or not.If internet connection is available then username and password should get validated from server database i.e send request method should get called and if not then username and password should get validated from local database i.e check method should get called.but the prob here is when the internet is off then also it gets into the send request method instead of getting in check nethod.What may be the prob.Please help me in solving this problem. i have added reachability files and imported the CFNetwork.framework.
- (void) showNetWorkAlert {
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
UIAlertView *networkAlert = [[UIAlertView alloc] initWithTitle:#"Error" message:#"Network connection unavailable."
delegate:self cancelButtonTitle:#"OK" otherButtonTitles:#"Retry", nil];
[networkAlert show];
[networkAlert release];
}
#pragma mark To Check Network Connection.
- (BOOL) currentNetworkStatus {
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
BOOL connected;
const char *host = "www.apple.com";
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(NULL, host);
SCNetworkReachabilityFlags flags;
connected = SCNetworkReachabilityGetFlags(reachability, &flags);
BOOL isConnected = YES;
isConnected = connected && (flags & kSCNetworkFlagsReachable) && !(flags & kSCNetworkFlagsConnectionRequired);
CFRelease(reachability);
if(!isConnected) {
// sleep(1);
[self showNetWorkAlert];
//[self check];
}
else
return isConnected;
//[self sendRequest];
return isConnected;
}
-(IBAction)buttonPressed:(id)sender
{
//[self sendRequest];
//[[Reachability sharedReachability] setHostName:kHostName];
//Set Reachability class to notifiy app when the network status changes.
//[[Reachability sharedReachability] setNetworkStatusNotificationsEnabled:YES];
//Set a method to be called when a notification is sent.
//[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(reachabilityChanged:) name:#"kNetworkReachabilityChangedNotification" object:nil];
//[self updateStatus];
//[self sendRequest];
//NSLog(<#NSString *format#>)
//this is to select username and password from database.
//[self check];
if ([self currentNetworkStatus]) {
[self sendRequest];
}
else {
[self check];
}
}
Rocky your code is right .Just check the URL is proper or not .
there is other way also
On button check the host like this
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(reachabilityChanged:)
name:kReachabilityChangedNotification
object:nil];
hostReach = [[Reachability reachabilityWithHostName:#"www.google.com"]retain];
[hostReach startNotifier];
and in the (reachabilityChanged:) method call make like this
-(void)reachabilityChanged:(NSNotification*)note
{
static BOOL showNotConnnected =NO;
Reachability *curReach =[note object];
NSParameterAssert([curReach isKindOfClass:[Reachability class]]);
NetworkStatus netStatus = [curReach currentReachabilityStatus];
if(netStatus != NotReachable)
{
UIAlertView *notconnect1 = [[UIAlertView alloc]initWithTitle:#"Server is connected" message:#"Server is connected" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:#"Cancel",nil];
[notconnect1 show];
[notconnect1 release];
}
else if(showNotConnnected == NO)
{
showNotConnnected =YES;
UIAlertView *notconnect = [[UIAlertView alloc]initWithTitle:#"Server is Not connected" message:#"Server may be slow or not connected" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:#"Cancel",nil];
[notconnect show];
[notconnect release];
}
}
I hope this will help you
I do think there is a sample code called Reachability from apple can help you. see the URL below
http://developer.apple.com/library/ios/samplecode/Reachability/Introduction/Intro.html
You should look at Apple's reachability code to check for a connection to the Internet. There is a sample project you can get from them in the iOS Developer Portal.
Reachability should not be used to detect if the network is available before making a network request because the act of making a network request can bring up the network if needed. Reachability should only be used to detect when the network becomes available after being unavailable.
Just try making the network request -- you'll get an error back if the network is unavailable. Usually the response will come back right away, but if the network is spotty it can take a while, so you shouldn't ever make synchronous network calls on the main thread. Use NSURLConnection if possible and you'll get callbacks when something happens.
When my app launches, I check for reachability because I need an immediate internet connection. My problem, though, is that it appears there's no immediate confirmation for the NetworkStatus, which means right after the Reachability is setup, I check whether there's a connection, and it returns that there isn't, regardless of whether I am in fact on WiFi/3G, or have turned the radio off.
I can confirm that I am in fact getting an Internet connection, because instantly after applicationDidFinishLaunching, there's a notification which then logs "ReachableViaWiFi"..
What am I doing wrong? Why is it not confirming a valid Internet connection right away?
- (void)applicationDidFinishLaunching:(UIApplication *)application {
NetworkStatus netStatus = [hostReach currentReachabilityStatus];
if (netStatus == NotReachable) {
ErrorViewController *errorViewController = [[ErrorViewController alloc] initWithNibName:#"ErrorView" bundle:[NSBundle mainBundle]];
[tabBarController.view removeFromSuperview];
[window addSubview:[errorViewController view]];
return;
}
}
-(void)setupReachability {
[[NSNotificationCenter defaultCenter] addObserver: self selector: #selector(reachabilityChanged:) name:kReachabilityChangedNotification object: nil];
hostReach = [[Reachability reachabilityWithHostName:#"www.google.com"] retain];
[hostReach startNotifier];
}
-(void)reachabilityChanged:(NSNotification *)notification {
Reachability* curReach = [notification object];
NSParameterAssert([curReach isKindOfClass: [Reachability class]]);
NetworkStatus netStatus = [curReach currentReachabilityStatus];
BOOL connectionRequired = [curReach connectionRequired];
switch (netStatus)
{
case NotReachable:
{
[[NSUserDefaults standardUserDefaults] setInteger:kNOTREACHABLE forKey:kREACHABILITYSTATUS];
NSLog(#"NotReachable");
connectionRequired = NO;
break;
}
case ReachableViaWWAN:
{
[[NSUserDefaults standardUserDefaults] setInteger:kREACHABLEVIAWWAN forKey:kREACHABILITYSTATUS];
NSLog(#"ReachableViaWWAN");
break;
}
case ReachableViaWiFi:
{
[[NSUserDefaults standardUserDefaults] setInteger:kNOTREACHABLE forKey:kREACHABILITYSTATUS];
NSLog(#"ReachableViaWiFi");
break;
}
}
}
OK, so after trying a few things out myself, I actually got it to work, by adding one extra line of code:
-(void)setupReachability {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(reachabilityChanged:) name:kReachabilityChangedNotificationV2 object:nil];
hostReach = [[ReachabilityV2 reachabilityWithHostName:#"www.google.com"] retain];
[hostReach connectionRequired]; // this line was added, and apparently forces a connection requirement..
[hostReach startNotifier];
}
The Reachability sample code provides you with asynchronous callbacks/notifications to inform you of how/when the reachability has changed. In order for your code to work, you should modify your code as follows:
- (void) applicationDidFinishLaunching:(UIApplication *)application {
// setup reachability
[self setupReachability];
}
Then in your callback, when you get the notification you react as needed by your application.
In other words, you can not check immediately for the network status in applicationDidFinishLaunching(). If you want to do so, then you must use a synchronous/blocking method, for instance you can use the code provided in my answer to this question.
You have to make hostReach as class level variable.