7
\$\begingroup\$

I have a small function in Objective-C which uses the C Socket API to check if the device has an active internet connection:

#include <netdb.h>

- (BOOL)isNetworkAvailable {
    char *hostname;
    struct hostent *hostinfo;
    hostname = "youtube.com";
    hostinfo = gethostbyname(hostname);
    if (hostinfo == NULL) {
        return NO;
    } else {
        return YES;
    }
}

I have rewritten this function in Swift, using an UnsafeMutablePointer type for the hostent struct:

func isNetworkAvailable() -> Bool {
    var hostinfo: UnsafeMutablePointer<hostent>
    let hostString: String = "youtube.com"
    var hostname = (hostString.cStringUsingEncoding(NSUTF8StringEncoding))!
    hostinfo = gethostbyname(hostname)
    if (hostinfo == nil) {
        hostinfo.destroy()
        return false
    } else {
        hostinfo.destroy()
        return true
    }
}

Is there a better way to check for network connectivity in Swift without using the C API? How can I improve this function, possibly removing the need to manually retain and release an UnsafeMutablePointer?

\$\endgroup\$
8
\$\begingroup\$

Wrong memory management

gethostbyname() returns a pointer to an internal structure. You don't have initialized that memory and don't own it, therefore you must not call destroy().

Things that can be improved

var hostinfo: UnsafeMutablePointer<hostent>
// ...
hostinfo = gethostbyname(...)

can be combined to

let hostinfo = gethostbyname(...)

There is no need to declare the variable in advance, and the type can be inferred automatically. Also you can define it as a constant with let because it is not modified subsequently.

let hostString: String = "youtube.com"
var hostname = (hostString.cStringUsingEncoding(NSUTF8StringEncoding))!
let hostinfo = gethostbyname(hostname)

can be simplified to

let hostname = "youtube.com"
let hostinfo = gethostbyname(hostname)

because a Swift string is automatically converted to a C string when passed to a C API taking a const char * parameter.

Finally, since the destroy() has been removed,

if (hostinfo == nil) {
    return false
} else {
    return true
}

can be simplified to

return hostinfo != nil

So your function is now

func isNetworkAvailable() -> Bool {
    let hostname = "youtube.com"
    let hostinfo = gethostbyname(hostname)
    return hostinfo != nil
}

BUT: This does not what you expect it to do!

gethostbyname() "only" resolves a host name to an IP address. That does not necessarily involve any network communication, as the local DNS resolver might have cached the information.

Better: Use the SCNetworkReachability framework.

From the documentation:

The SCNetworkReachability programming interface allows an application to determine the status of a system's current network configuration and the reachability of a target host. A remote host is considered reachable when a data packet, sent by an application into the network stack, can leave the local device. Reachability does not guarantee that the data packet will actually be received by the host.

Other Resources:

\$\endgroup\$
  • \$\begingroup\$ Thank you for your great answer. Ideally, I wrote this script so I wouldn't have to include the Reachability dependency in my project. Is there a way to expand on the current implementation to do a basic network check without a third-party dependency? \$\endgroup\$ – JAL Jun 3 '15 at 19:21
  • 1
    \$\begingroup\$ @JAL: The SO thread that I linked to does not use any 3rd party dependency. import SystemConfiguration is sufficient. \$\endgroup\$ – Martin R Jun 3 '15 at 19:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.