Динамический информационный экран - PullRequest
1 голос
/ 30 мая 2009

Я пытаюсь найти способ загрузки информационного экрана моего приложения из Интернета, ЕСЛИ доступно подключение к Интернету (поэтому я могу регулярно его обновлять). Если Интернет недоступен, он должен использовать по умолчанию или последний загруженный. Возможно ли это?

1 Ответ

2 голосов
/ 30 мая 2009

Обратите внимание, что метод, предложенный Джеймсом Скитмором выше, является ужасным способом проверки подключения. Пожалуйста делайте не используйте это. Это приведет к ненужной загрузке google.com и истечет время ожидания, если сеть будет недоступна, что сделает ваше приложение замороженным.

Вот как вы можете проверить сетевое соединение.

В вашем приложении вы можете сделать это, чтобы проверить, доступна ли сеть:

if (![[UIDevice currentDevice] networkAvailable])

Теперь вы также можете проверить подключение к определенному хосту, например yourwebsite.com, или вы можете проверить тип соединения, 3G против Wi-Fi и т. Д. Я позволю вам выяснить это самостоятельно, прочитав заголовочные файлы .

Вам потребуется включить в приложение следующие файлы: (любезно предоставлено Эрикой Садун)

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);
}
...