Search code examples
iosobjective-cswiftobjective-c-swift-bridge

Init a `sockaddr` struct with a given IP address in Swift


I playing around with Apple's Reachabilitty class in Swift.

The web server in my private Wi-Fi runs on http://10.0.0.1. To check its reachability I want to use the following function from Reachability.h (not the one with hostname):

/*!
 * Use to check the reachability of a given IP address.
 */
+ (instancetype)reachabilityWithAddress:(const struct sockaddr *)hostAddress;

I sockaddr looks accordingly to socket.h like this:

/*
 * [XSI] Structure used by kernel to store most addresses.
 */
struct sockaddr {
    __uint8_t   sa_len;     /* total length */
    sa_family_t sa_family;  /* [XSI] address family */
    char        sa_data[14];    /* [XSI] addr value (actually larger) */
};

How can I create a sockaddr struct with th ip address 10.0.0.1 in Swift?

I already read [here] something about UnsafePointer and UnsafeMutablePointer and that C arrays (or is it in this case an Objective-C array?) are represented as tuples in Swift, but I still don't know for sure how to work with them.


Solution

  • You create a sockaddr_in first:

    var addr = sockaddr_in()
    addr.sin_len = UInt8(MemoryLayout.size(ofValue: addr))
    addr.sin_family = sa_family_t(AF_INET)
    
    addr.sin_addr.s_addr = inet_addr("10.0.0.1")
    // Alternatively:
    inet_pton(AF_INET, "10.0.0.1", &addr.sin_addr)
    

    and then "rebind" it to a sockaddr pointer, e.g.

    let reachability = withUnsafePointer(to: &addr, {
        $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
            SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, $0)
        }
    })
    

    Some explanation:

    There are different address types for different address families (such as struct sockaddr_in for IPv4, struct sockaddr_in6 for IPv6). struct sockaddr is the “common part”, and many socket functions which take a pointer to a sockaddr as parameter.

    In C you can simply cast between the pointers:

    struct sockaddr sin;
    // ...
    struct sockaddr *addrPtr = (struct sockaddr *)&sin;
    

    In Swift that is done more explicitly as “rebinding”. For more information, see