views:

1025

answers:

2

I am trying to find a way to make the info screen on my app download from the web IF an internetconnection is available (so i can update it regularly). If internet is not available, it should use the default or the last downloaded one. Is this possible?

+2  A: 

Please note the method offered by James Skitmore above is a terrible way to test connectivity. Please do not use it. It will unnecessarily load google.com, and it will cause a timeout should the network not be available, which will make your app look like it has frozen.

Here's how you could check for net connectivity.

In your app, you can do this to check for whether network is available:

if (![[UIDevice currentDevice] networkAvailable])

You can now also check connectivity to a certain host, such as yourwebsite.com, or you could check for the type of conenction, 3G vs Wifi, etc. I'll let you figure this out yourself by reading the header files.

You're going to need to include the following files to your app: (courtesy of Erica Sadun)

UIDevice-Reachability.h

/*
 Erica Sadun, http://ericasadun.com
 iPhone Developer's Cookbook, 3.0 Edition
 BSD License for anything not specifically marked as developed by a third party.
 Zach Waugh and Apple's code excluded.
 Use at your own risk
 */

#import <UIKit/UIKit.h>

#define SUPPORTS_UNDOCUMENTED_API   1

@interface UIDevice (Reachability)
- (NSString *) hostname;

- (NSString *) localWiFiIPAddress;
- (NSString *) localIPAddress;
- (NSString *) whatismyipdotcom;

- (BOOL) activeWLAN;
- (BOOL) addressFromString:(NSString *)IPAddress address:(struct sockaddr_in *)address; // via Apple
- (void) forceWWAN; // via Apple
- (void) shutdownWWAN; // via Apple
@end

// Methods which rely on undocumented API methods 
#if SUPPORTS_UNDOCUMENTED_API

@interface UIDevice (UIDevice_Undocumented_Reachability)
- (BOOL) networkAvailable;
- (BOOL) activeWWAN;
@end

#endif // SUPPORTS_UNDOCUMENTED_API

UIDevice-Reachability.m

/*
 Erica Sadun, http://ericasadun.com
 iPhone Developer's Cookbook, 3.0 Edition
 BSD License for anything not specifically marked as developed by a third party.
 Apple's code excluded.
 Use at your own risk
 */

// TTD: Add async version of whatsmyip -- thanks rpetrich

#include <unistd.h>
#include <sys/sysctl.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <netinet/in.h>
#include <ifaddrs.h>

#import "UIDevice-Reachability.h"
#import "wwanconnect.h"

@implementation UIDevice (Reachability)

#pragma mark host and ip utils
- (NSString *) hostname
{
    char baseHostName[255];
    int success = gethostname(baseHostName, 255);
    if (success != 0) return nil;
    baseHostName[255] = '\0';

#if !TARGET_IPHONE_SIMULATOR
    return [NSString stringWithFormat:@"%s.local", baseHostName];
#else
    return [NSString stringWithFormat:@"%s", baseHostName];
#endif
}

// Direct from Apple. Thank you Apple
- (BOOL)addressFromString:(NSString *)IPAddress address:(struct sockaddr_in *)address
{
    if (!IPAddress || ![IPAddress length]) {
     return NO;
    }

    memset((char *) address, sizeof(struct sockaddr_in), 0);
    address->sin_family = AF_INET;
    address->sin_len = sizeof(struct sockaddr_in);

    int conversionResult = inet_aton([IPAddress UTF8String], &address->sin_addr);
    if (conversionResult == 0) {
     NSAssert1(conversionResult != 1, @"Failed to convert the IP address string into a sockaddr_in: %@", IPAddress);
     return NO;
    }

    return YES;
}

- (NSString *) getIPAddressForHost: (NSString *) theHost
{
    struct hostent *host = gethostbyname([theHost UTF8String]);

    if (host == NULL) {
        herror("resolv");
     return NULL;
    }

    struct in_addr **list = (struct in_addr **)host->h_addr_list;
    NSString *addressString = [NSString stringWithCString:inet_ntoa(*list[0])];
    return addressString;
}

#if ! defined(IFT_ETHER)
#define IFT_ETHER 0x6   // Ethernet CSMACD
#endif

// Matt Brown's get WiFi IP addy solution
// Author gave permission to use in Cookbook under cookbook license
// http://mattbsoftware.blogspot.com/2009/04/how-to-get-ip-address-of-iphone-os-v221.html
- (NSString *) localWiFiIPAddress
{
    BOOL success;
    struct ifaddrs * addrs;
    const struct ifaddrs * cursor;

    success = getifaddrs(&addrs) == 0;
    if (success) {
     cursor = addrs;
     while (cursor != NULL) {
      // the second test keeps from picking up the loopback address
      if (cursor->ifa_addr->sa_family == AF_INET && (cursor->ifa_flags & IFF_LOOPBACK) == 0) 
      {
       NSString *name = [NSString stringWithUTF8String:cursor->ifa_name];
       if ([name isEqualToString:@"en0"]) { // found the WiFi adapter
        return [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)cursor->ifa_addr)->sin_addr)];
       }
      }
      cursor = cursor->ifa_next;
     }
     freeifaddrs(addrs);
    }
    return nil;
}

// Return the local IP address
- (NSString *) localIPAddress
{
    struct hostent *host = gethostbyname([[self hostname] UTF8String]);
    if (host == NULL)
    {
        herror("resolv");
     return nil;
    }
    else {
        struct in_addr **list = (struct in_addr **)host->h_addr_list;
     return [NSString stringWithCString:inet_ntoa(*list[0])];
    }
    return nil;
}

- (NSString *) whatismyipdotcom
{
    NSError *error;
    NSURL *ipURL = [NSURL URLWithString:@"http://www.whatismyip.com/automation/n09230945.asp"];
    NSString *ip = [NSString stringWithContentsOfURL:ipURL encoding:1 error:&error];
    if (!ip) return [error localizedDescription];
    return ip;
}

- (BOOL) activeWLAN
{
    return ([self localWiFiIPAddress] != nil);
}

#pragma mark Forcing WWAN connection

MyStreamInfoPtr myInfoPtr;

static void myClientCallback(void *refCon)
{
    int  *val = (int*)refCon;
    printf("myClientCallback entered - value from refCon is %d\n", *val);
}

- (void) forceWWAN
{
    int value = 0;
    myInfoPtr = (MyStreamInfoPtr) StartWWAN(myClientCallback, &value);
    if (myInfoPtr) 
    {
     printf("Started WWAN\n");
    }
    else
    {
     printf("Failed to start WWAN\n");
    }
}

- (void) shutdownWWAN
{
    if (myInfoPtr) StopWWAN((MyInfoRef) myInfoPtr);
}
@end

#if SUPPORTS_UNDOCUMENTED_API
@implementation UIDevice (UIDevice_Undocumented_Reachability)
- (BOOL) networkAvailable
{
    // Unavailable has only one address: 127.0.0.1
    return !(([[[NSHost currentHost] addresses] count] == 1) && [[self localIPAddress] isEqualToString:@"127.0.0.1"]);
}

- (BOOL) activeWWAN
{
    return ([self networkAvailable] && ![self localWiFiIPAddress]);
}
@end
#endif  // SUPPORTS_UNDOCUMENTED_API

wwanconnect.h

// Direct from Apple. Thank you Apple

#if !defined(__WWAN_CONNECT__)
#define __WWAN_CONNECT__    1

#include <CoreFoundation/CoreFoundation.h>
#include <assert.h>

#define kTestHost   "www.whatismyip.com"
#define kTestPort   80  

typedef void (*ConnectClientCallBack)(void *refCon);


struct MyStreamInfoStruct{
    CFWriteStreamRef  wStreamRef;
    CFReadStreamRef   rStreamRef;
    ConnectClientCallBack clientCB;
    void     *refCon;
    CFStreamError   error;
    Boolean     errorOccurred;
    Boolean     isConnected;
    Boolean     isStreamInitd;
    Boolean     isClientSet;
};

typedef struct MyStreamInfoStruct MyStreamInfo;
typedef struct MyStreamInfoStruct *MyStreamInfoPtr;
typedef struct __MyInfoRef *MyInfoRef;

/*
 *  StartWWAN()
 *  
 *  Discussion:
 *    This function will initiate a Wireless Wide Area Network (WWAN)
 *     connection by using the CFSocketStream API to connect with a 
 *     server system defined by kTestHost:kTestPort above.
 *     No communications are expected to happen over the CFSocketStream
 *     connection.
 *  
 *    clientCB:
 *     if the connection is opened, the callback routine, if not NULL
 *     will be called. function defintion - see ConnectClientCallBack above
 *     
 *    refCon:
 *     if a client callback, clientCB is defined, then the refCon
 *      parameter will be the argument to the client callback
 *
 *    return:
 *     if the WWAN connection is successful, a MyInfoRef value is returned
 *     The MyInfoRef value must be passed to StopWWAN to stop the WWAN
 *     connection.
 *     A NULL result indicates that the connection was unsuccessful
 *    
 */
extern MyInfoRef StartWWAN(ConnectClientCallBack clientCB, void *refCon);

/*
 *  StopWWAN()
 *  
 *  Discussion:
 *    This function closes the CFSocketStream which was used to establish the
 *    WWAN connection. Once the WWAN connection has been started, BSD
 *    network functions can be used to communicate across the WWAN connection.
 *    As of the writing of this sample, there is no guarantee that the use of
 *    only BSD socket API's will maintain the WWAN connection.
 *  
 *    infoRef:
 *     pass in the MyInfoRef result from the StartWWAN function.
 *     
 */

extern void StopWWAN(MyInfoRef infoRef);

#endif // __WWAN_CONNECT__

wwanconnect.c

// Direct from Apple. Thank you Apple

#include "wwanconnect.h"
#include <CFNetwork/CFSocketStream.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <ifaddrs.h>
#include <stdio.h>

static Boolean TestGetIFAddrs(void);
static void MyCFWriteStreamClientCallBack(CFWriteStreamRef stream, CFStreamEventType type, void *clientCallBackInfo);
static void CleanupAfterWAAN(MyStreamInfoPtr myInfoPtr);
static void CloseStreams(MyStreamInfoPtr myInfoPtr);

static Boolean TestGetIFAddrs(void)
{
    int    result;
    struct  ifaddrs *ifbase, *ifiterator;
    int    done = 0;
    Boolean   addrFound = FALSE;
    char   loopbackname[] = "lo0/0";

    result = getifaddrs(&ifbase);
    ifiterator = ifbase;
    while (!done && (ifiterator != NULL))
    {
     if (ifiterator->ifa_addr->sa_family == AF_INET)
     {
      if (memcmp(ifiterator->ifa_name, loopbackname, 3))
      {
       struct sockaddr *saddr, *netmask, *daddr;
       saddr = ifiterator->ifa_addr;
       netmask = ifiterator->ifa_netmask;
       daddr = ifiterator->ifa_dstaddr;

       // we've found an entry for the IP address
       struct sockaddr_in *iaddr;
       char    addrstr[64];
       iaddr = (struct sockaddr_in *)saddr;
       inet_ntop(saddr->sa_family, &iaddr->sin_addr, addrstr, sizeof(addrstr));
       fprintf(stderr, "ipv4 interface name %s, source IP addr %s ", ifiterator->ifa_name, addrstr);

       iaddr = (struct sockaddr_in *)netmask;
       if (iaddr)
       {
        inet_ntop(saddr->sa_family, &iaddr->sin_addr, addrstr, sizeof(addrstr));
        fprintf(stderr, "netmask IP addr %s ", addrstr);
       }

       iaddr = (struct sockaddr_in *)daddr;
       if (iaddr)
       {
        inet_ntop(saddr->sa_family, &iaddr->sin_addr, addrstr, sizeof(addrstr));
        fprintf(stderr, "dest/broadcast IP addr %s.\n\n", addrstr);
       }
       return TRUE;
      }

     }
     else if (ifiterator->ifa_addr->sa_family == AF_INET6)
     {
      // we've found an entry for the IP address
      struct sockaddr_in6 *iaddr6 = (struct sockaddr_in6 *)ifiterator->ifa_addr;
      char    addrstr[256];
      inet_ntop(ifiterator->ifa_addr->sa_family, iaddr6, addrstr, sizeof(addrstr));
      fprintf(stderr, "ipv6 interface name %s, source IP addr %s \n\n", ifiterator->ifa_name, addrstr);
     }
     ifiterator = ifiterator->ifa_next;
    }
    if (ifbase)
     freeifaddrs(ifbase); /* done with the memory allocated by getifaddrs */

    return addrFound;
}

static void MyCFWriteStreamClientCallBack(CFWriteStreamRef stream, CFStreamEventType type, void *clientCallBackInfo)
{
    MyStreamInfoPtr myInfoPtr = (MyStreamInfoPtr) clientCallBackInfo;

    printf("MyCFWriteStreamClientCallBack entered - event is %d\n", type);

    switch (type)
    {
     case kCFStreamEventOpenCompleted:
      myInfoPtr->isConnected = TRUE;
      TestGetIFAddrs();  // call the test function to return the local ip address associated with this connection.
      if (myInfoPtr->clientCB)
      {
       // call client callback routine
       myInfoPtr->clientCB(myInfoPtr->refCon);
      }
      printf("write stream connected\n");
      break;

     case kCFStreamEventErrorOccurred:
      myInfoPtr->errorOccurred = TRUE;
      myInfoPtr->error = CFWriteStreamGetError(myInfoPtr->wStreamRef);
      printf("write stream error %d .. giving up\n", myInfoPtr->error.error);
      break;

     default:
      printf("event type %d occurred\n");
      break;
    }
    // stop the run loop at this point
    CFRunLoopStop(CFRunLoopGetCurrent());
}

extern MyInfoRef StartWWAN(ConnectClientCallBack clientCB, void *refCon)
{ 
    char      host[] = kTestHost;
    int       portNum = kTestPort;
    CFDataRef     addressData;
    MyStreamInfoPtr    myInfoPtr;
    CFStreamClientContext  ctxt = {0, NULL, NULL, NULL, NULL};
    Boolean      errorOccurred = FALSE;

    myInfoPtr = malloc(sizeof(MyStreamInfo));
    if (!myInfoPtr)
    {
     return NULL;
    }

    // init the allocated memory
    memset(myInfoPtr, 0, sizeof(MyStreamInfo));
    myInfoPtr->clientCB = clientCB;
    myInfoPtr->refCon = refCon; 
    ctxt.info = myInfoPtr;

    // Check for a dotted-quad address, if so skip any host lookups 
    in_addr_t addr = inet_addr(host); 
    if (addr != INADDR_NONE) { 
     // Create the streams from numberical host 
     struct sockaddr_in sin; 
     memset(&sin, 0, sizeof(sin)); 

     sin.sin_len= sizeof(sin); 
     sin.sin_family = AF_INET; 
     sin.sin_addr.s_addr = addr; 
     sin.sin_port = htons(portNum); 

     addressData = CFDataCreate(NULL, (UInt8 *)&sin, sizeof(sin)); 
     CFSocketSignature sig = { AF_INET, SOCK_STREAM, IPPROTO_TCP, addressData }; 

     // Create the streams. 
     CFStreamCreatePairWithPeerSocketSignature(kCFAllocatorDefault, &sig, &(myInfoPtr->rStreamRef), &(myInfoPtr->wStreamRef));   
     CFRelease(addressData); 
    } else { 
     // Create the streams from ascii host name 
     CFStringRef hostStr = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, host, kCFStringEncodingUTF8, kCFAllocatorNull); 
     CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, hostStr, portNum, &(myInfoPtr->rStreamRef), &(myInfoPtr->wStreamRef)); 
    } 

    myInfoPtr->isConnected = FALSE;
    myInfoPtr->isStreamInitd = TRUE;
    myInfoPtr->isClientSet = FALSE;

    // Inform the streams to kill the socket when it is done with it. 
    // This effects the write stream too since the pair shares the 
    // one socket. 
    CFWriteStreamSetProperty(myInfoPtr->wStreamRef, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue); 

    // set up the client
    if (!CFWriteStreamSetClient(myInfoPtr->wStreamRef, kCFStreamEventOpenCompleted | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered, 
           MyCFWriteStreamClientCallBack, &ctxt))
    {
     printf("CFWriteStreamSetClient failed\n");
     errorOccurred = TRUE;
    }
    else
     myInfoPtr->isClientSet = TRUE;

    if (!errorOccurred)
    {
     // schedule the stream
     CFWriteStreamScheduleWithRunLoop(myInfoPtr->wStreamRef, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);

     // Try to open the stream.
     if (!CFWriteStreamOpen(myInfoPtr->wStreamRef))
     {
      printf("CFWriteStreamOpen failed\n");
      errorOccurred = TRUE;
     }
    }

    if (!errorOccurred)
    {
     // everything worked so far, so run the runloop - when the callback gets called, it will stop the run loop
     printf("CFWriteStreamOpen returned with no error - calling CFRunLoopRun\n");
     CFRunLoopRun();
     if (myInfoPtr->errorOccurred)
      errorOccurred = TRUE;
     printf("after CFRunLoopRun - returning\n");
    }

    if (errorOccurred)
    {
     myInfoPtr->isConnected = FALSE;
     CleanupAfterWAAN(myInfoPtr);
     CloseStreams(myInfoPtr);

     if (myInfoPtr->isStreamInitd)
     {
      CFRelease(myInfoPtr->rStreamRef);
      CFRelease(myInfoPtr->wStreamRef);
      myInfoPtr->isStreamInitd = FALSE;
     }
     free(myInfoPtr);
     return NULL;
    }
    return (MyInfoRef)myInfoPtr;
} 

static void CleanupAfterWAAN(MyStreamInfoPtr myInfoPtr)
{
    assert(myInfoPtr != NULL);
    if (myInfoPtr->isClientSet)
    {
     CFWriteStreamSetClient(myInfoPtr->wStreamRef, 0, NULL, NULL);
     CFWriteStreamUnscheduleFromRunLoop(myInfoPtr->wStreamRef, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
     myInfoPtr->isClientSet = FALSE;
    }
}

static void CloseStreams(MyStreamInfoPtr myInfoPtr)
{
    assert(myInfoPtr != NULL);
    if (myInfoPtr->rStreamRef)
    {
     CFReadStreamClose(myInfoPtr->rStreamRef);
     myInfoPtr->rStreamRef = NULL;
    }
    if (myInfoPtr->wStreamRef)
    {
     CFWriteStreamClose(myInfoPtr->wStreamRef);
     myInfoPtr->wStreamRef = NULL;
    }
}

extern void StopWWAN(MyInfoRef infoRef)
{
    MyStreamInfoPtr myInfoPtr = (MyStreamInfoPtr)infoRef;

    printf("stopWWAN entered\n");
    assert(myInfoPtr != NULL);
    myInfoPtr->isConnected = FALSE;
    CleanupAfterWAAN(myInfoPtr);
    CloseStreams(myInfoPtr);
    free(myInfoPtr);
}
Kenneth Ballenegger
A: 

Hi Kenneth Ballenegger , how to fetch a Google Data API feed only over the cellular connection, avoiding any WiFi connection in Iphone App? can we do that by using the above code or is there any other way to do that . if you have any sample code to demonstrate that please send me.

Thank you

Sandhya

sandhya