Swift 3 how to resolve NetService IP? - ios

Just trying Bonjour in swift 3
Here is my code , I can receive the delegate
func netServiceDidResolveAddress(_ sender: NetService) {
print("netServiceDidResolveAddress service name \(sender.name) of type \(sender.type)," +
"port \(sender.port), addresses \(sender.addresses)")
}
And here is my result
netServiceDidResolveAddress service name Webber's Mac mini of type
_myapp._tcp.,port 5678, addresses Optional([<1002162e c0a80205 00000000 00000000>, <1c1e162e 00000000 fe800000 00000000 00bce7ad
24b4b7e8 08000000>])
c0a80205 is the IP I looking for => 192.168.2.5
And the address is [Data] , Apple says
The addresses of the service. This is an NSArray of NSData instances,
each of which contains a single struct sockaddr suitable for use with
connect(2). In the event that no addresses are resolved for the
service or the service has not yet been resolved, an empty NSArray is
returned.
I still confuse why Data can't use .btyes ?
As Apple says "This is an NSArray of NSData instances"
But I can't use it like NSData
And how to resolve the address as readable IP string ?
I try this before , but do not get the result as I except ...
let thedata = NSData(bytes: sender.addresses, length: (sender.addresses?.count)!)
var storage = sockaddr_storage()
thedata.getBytes(&storage, length: sizeof(sockaddr_storage))
if Int32(storage.ss_family) == AF_INET {
let addr4 = withUnsafePointer(&storage) {UnsafePointer<sockaddr_in>($0).pointee }
print(inet_ntoa(addr4.sin_addr));
}
Any suggestion will be help , Thanks

Here's how I did it in Swift 3.
func netServiceDidResolveAddress(_ sender: NetService) {
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
guard let data = sender.addresses?.first else { return }
data.withUnsafeBytes { (pointer:UnsafePointer<sockaddr>) -> Void in
guard getnameinfo(pointer, socklen_t(data.count), &hostname, socklen_t(hostname.count), nil, 0, NI_NUMERICHOST) == 0 else {
return
}
}
let ipAddress = String(cString:hostname)
print(ipAddress)
}
Swift 5
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
data.withUnsafeBytes { (pointer: UnsafeRawBufferPointer) -> Void in
let sockaddrPtr = pointer.bindMemory(to: sockaddr.self)
guard let unsafePtr = sockaddrPtr.baseAddress else { return }
guard getnameinfo(unsafePtr, socklen_t(data.count), &hostname, socklen_t(hostname.count), nil, 0, NI_NUMERICHOST) == 0 else {
return
}
}
let ipAddress = String(cString:hostname)
print(ipAddress)

Edited Phil Coles answer for Swift 5.0 warning free solution:
func netServiceDidResolveAddress(_ sender: NetService) {
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
guard let data = sender.addresses?.first else { return }
data.withUnsafeBytes { ptr in
guard let sockaddr_ptr = ptr.baseAddress?.assumingMemoryBound(to: sockaddr.self) else {
// handle error
return
}
var sockaddr = sockaddr_ptr.pointee
guard getnameinfo(sockaddr_ptr, socklen_t(sockaddr.sa_len), &hostname, socklen_t(hostname.count), nil, 0, NI_NUMERICHOST) == 0 else {
return
}
}
let ipAddress = String(cString:hostname)
print(ipAddress)
}

OK ... this is not a smart answer , at least I can get the readable IP
Just use this func to get IP string
let bonjourDevices = [NetService]()
let bonjourDevice = bonjourDevices[0]
let host = self.getIPV4StringfromAddress(address:bonjourDevice.addresses!)
func getIPV4StringfromAddress(address: [Data] , port : Int ) -> String{
let data = address.first! as NSData;
var ip1 = UInt8(0)
data.getBytes(&ip1, range: NSMakeRange(4, 1))
var ip2 = UInt8(0)
data.getBytes(&ip2, range: NSMakeRange(5, 1))
var ip3 = UInt8(0)
data.getBytes(&ip3, range: NSMakeRange(6, 1))
var ip4 = UInt8(0)
data.getBytes(&ip4, range: NSMakeRange(7, 1))
let ipStr = String(format: "%d.%d.%d.%d:%d",ip1,ip2,ip3,ip4,port);
return ipStr;
}

I can't make it work with Data, but using NSData, I would use this:
extension NSData {
func castToCPointer<T>() -> T {
let mem = UnsafeMutablePointer<T>.allocate(capacity: MemoryLayout<T.Type>.size)
self.getBytes(mem, length: MemoryLayout<T.Type>.size)
return mem.move()
}
}
So we have netServiceDidResolveAddress:
func netServiceDidResolveAddress(_ sender: NetService) {
if let addresses = sender.addresses, addresses.count > 0 {
for address in addresses {
let data = address as NSData
let inetAddress: sockaddr_in = data.castToCPointer()
if inetAddress.sin_family == __uint8_t(AF_INET) {
if let ip = String(cString: inet_ntoa(inetAddress.sin_addr), encoding: .ascii) {
// IPv4
print(ip)
}
} else if inetAddress.sin_family == __uint8_t(AF_INET6) {
let inetAddress6: sockaddr_in6 = data.castToCPointer()
let ipStringBuffer = UnsafeMutablePointer<Int8>.allocate(capacity: Int(INET6_ADDRSTRLEN))
var addr = inetAddress6.sin6_addr
if let ipString = inet_ntop(Int32(inetAddress6.sin6_family), &addr, ipStringBuffer, __uint32_t(INET6_ADDRSTRLEN)) {
if let ip = String(cString: ipString, encoding: .ascii) {
// IPv6
print(ip)
}
}
ipStringBuffer.deallocate(capacity: Int(INET6_ADDRSTRLEN))
}
}
}
}
I am having the following result (storing ips in array before display):
["172.16.10.120", "172.16.8.251", "::", "::82c9:d9a5:2eed:1c87"]
Code inspired by https://gist.github.com/agrippa1994/d8c66a2ded74fb2dd801 written in Swift 2.3 and adapted for Swift 3.0

Related

Binding UDP Socket to Cellular IP

I am trying to create an iOS client that sends data to a server on a UDP socket over the device's cellular communication.
Following Does IOS support simultaneous wifi and 3g/4g connections?
link to iOS Multipath BSD Sockets Test, I've tried implementing the solution in Swift 3, that is enumerate network interfaces in the device, identifying the Cellular interface (as suggested in Swift - Get device's IP Address), create a UDP socket and bind it to the sockaddr retrieved from the interface.
Implementation of socket programming in Swift was done by following examples from Socket Programming in Swift: Part 1 - getaddrinfo and following posts.
Unfortunately I received Operation not permitted when trying to send data on the socket, so instead I've tried creating the socket and binding it to the data from getaddrinfo called on a designated port (5555).
That too didn't do the trick.
The interesting thing is that while trying to understand what's wrong, I created a test application for both methods, and when tested for 1000 consecutive create->bind->send->close, about 3-5 of the attempts actually did send the data without the error on either method.
Needless to say this was tested on an actual iPhone.
Quite at a loss, I'd appreciate any advice regarding this.
Code implemented in a static "SocketManager" class (edit: fixed sockaddr allocation size)
// Return IP address String, port String & sockaddr of WWAN interface (pdp_ip0), or `nil`
public static func getInterface() -> (String?, String?, UnsafeMutablePointer<sockaddr>?) {
var host : String?
var service : String?
// Get list of all interfaces on the local machine:
var ifaddr : UnsafeMutablePointer<ifaddrs>?
var clt : UnsafeMutablePointer<sockaddr>?
guard getifaddrs(&ifaddr) == 0 else {
return (nil, nil, clt)
}
guard let firstAddr = ifaddr else {
return (nil, nil, clt)
}
// For each interface ...
for ifptr in sequence(first: firstAddr, next: { $0.pointee.ifa_next }) {
let interface = ifptr.pointee
let flags = Int32(ifptr.pointee.ifa_flags)
/// Check for running IPv4 interfaces. Skip the loopback interface.
if (flags & (IFF_UP|IFF_RUNNING|IFF_LOOPBACK)) == (IFF_UP|IFF_RUNNING) {
let addrFamily = interface.ifa_addr.pointee.sa_family
if addrFamily == UInt8(AF_INET) { //Interested in IPv4 for in particular case
// Check interface name:
let name = String(cString: interface.ifa_name)
print("interface name: \(name)")
if name.hasPrefix("pdp_ip") { //cellular interface
// Convert interface address to a human readable string:
let ifa_addr_Value = interface.ifa_addr.pointee
clt = UnsafeMutablePointer<sockaddr>.allocate(capacity: 1)
clt?.initialize(to: ifa_addr_Value, count: 1)
var hostnameBuffer = [CChar](repeating: 0, count: Int(NI_MAXHOST))
var serviceBuffer = [CChar](repeating: 0, count: Int(NI_MAXSERV))
getnameinfo(interface.ifa_addr, socklen_t(ifa_addr_Value.sa_len),
&hostnameBuffer, socklen_t(hostnameBuffer.count),
&serviceBuffer,
socklen_t(serviceBuffer.count),
NI_NUMERICHOST | NI_NUMERICSERV)
host = String(cString: hostnameBuffer)
if let host = host {
print("found host \(String(describing: host))")
}
service = String(cString: serviceBuffer)
if let service = service {
print("found service \(String(describing: service))")
}
break;
}
}
}
}
freeifaddrs(ifaddr)
return (host, service, clt)
}
public static func bindSocket(ip: String, port : String, clt : UnsafeMutablePointer<sockaddr>, useCltAddr : Bool = false) -> Int32 {
print("binding socket for IP: \(ip):\(port) withCltAddr=\(useCltAddr)")
var hints = addrinfo(ai_flags: 0,
ai_family: AF_INET,
ai_socktype: SOCK_DGRAM,
ai_protocol: IPPROTO_UDP,
ai_addrlen: 0,
ai_canonname: nil,
ai_addr: nil,
ai_next: nil)
var connectionInfo : UnsafeMutablePointer<addrinfo>? = nil
let status = getaddrinfo(
ip,
port,
&hints,
&connectionInfo)
if status != 0 {
var strError: String
if status == EAI_SYSTEM {
strError = String(validatingUTF8: strerror(errno)) ?? "Unknown error code"
} else {
strError = String(validatingUTF8: gai_strerror(status)) ?? "Unknown error code"
}
print(strError)
return -1
}
let socketDescriptor = useCltAddr ? socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) : socket(connectionInfo!.pointee.ai_family, connectionInfo!.pointee.ai_socktype, connectionInfo!.pointee.ai_protocol)
if socketDescriptor == -1 {
let strError = String(utf8String: strerror(errno)) ?? "Unknown error code"
let message = "Socket creation error \(errno) (\(strError))"
freeaddrinfo(connectionInfo)
print(message)
return -1
}
let res = useCltAddr ? bind(socketDescriptor, clt, socklen_t(clt.pointee.sa_len)) : bind(socketDescriptor, connectionInfo?.pointee.ai_addr, socklen_t((connectionInfo?.pointee.ai_addrlen)!))
if res != 0 {
let strError = String(utf8String: strerror(errno)) ?? "Unknown error code"
let message = "Socket bind error \(errno) (\(strError))"
freeaddrinfo(connectionInfo)
close(socketDescriptor)
print(message)
return -1
}
freeaddrinfo(connectionInfo)
print("returned socket descriptor \(socketDescriptor)")
return socketDescriptor
}
//returns 0 for failure, 1 for success
public static func sendData(toIP: String, onPort : String, withSocketDescriptor : Int32, data : Data) -> Int{
print("sendData called for targetIP: \(toIP):\(onPort) with socket descriptor: \(withSocketDescriptor)")
var target = UnsafeMutablePointer<sockaddr_in>.allocate(capacity: MemoryLayout<sockaddr_in>.size)
target.pointee.sin_family = sa_family_t(AF_INET)
target.pointee.sin_addr.s_addr = inet_addr(toIP)
target.pointee.sin_port = in_port_t(onPort)!
var res = 0
data.withUnsafeBytes { (u8Ptr: UnsafePointer<UInt8>) in
let rawPtr = UnsafeRawPointer(u8Ptr)
withUnsafeMutablePointer(to: &target) {
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
let bytesSent = sendto(withSocketDescriptor, rawPtr, data.count, 0, $0, socklen_t(MemoryLayout.size(ofValue: target)))
if bytesSent > 0 {
print("😄😄😄 Sent \(bytesSent) bytes 😄😄😄")
res = 1
}
if bytesSent == -1 {
let strError = String(utf8String: strerror(errno)) ?? "Unknown error code"
let message = "Socket sendto error \(errno) (\(strError))"
print(message)
}
}
}
}
return res
}
public static func closeSocket(socketDescriptor : Int32, clt : UnsafeMutablePointer<sockaddr>) {
print("closing socket descriptor \(socketDescriptor)")
close(socketDescriptor)
clt.deinitialize()
clt.deallocate(capacity: 1)
}
On ViewController:
override func viewDidLoad() {
super.viewDidLoad()
var i = 0
for _ in 0..<1000 {
i += connectSendClose(withDescriptor: false) // change withDescriptor to switch socket create/bind method
}
print("Sent \(i) packets")
}
private func connectSendClose(withDescriptor : Bool) -> Int {
let interface = SocketManager.getInterface()
guard let ip = interface.0 else {
print("no relevant interface")
return 0
}
guard let clt = interface.2 else {
print("no addr")
return 0
}
let socketDescriptor = SocketManager.bindSocket(ip: ip, port: "5555", clt: clt, useCltAddr: withDescriptor)
if socketDescriptor == -1 {
print("faild to configure socket")
return 0
}
let serverIP = "59.122.442.9" //dummy IP, test was preformed on actual server
let serverPort = "10025" //dummy port, test was preformed on actual server
let input = 42.13
var value = input
let data = withUnsafePointer(to: &value) {
Data(bytes: UnsafePointer($0), count: MemoryLayout.size(ofValue: input))
}
let res = SocketManager.sendData(toIP: serverIP, onPort: serverPort, withSocketDescriptor: socketDescriptor, data: data)
SocketManager.closeSocket(socketDescriptor: socketDescriptor, clt: clt)
return res
}
Edit: Fixed Network byte order bug in creation of target sockadd_in.
Alright, found the problem:
First, as Martin noted, I miss used UnsafeMutablePointer allocation as I took capacity/count parameters as bytes.
This was done also when I allocated sockaddr_in for server details in sendData function (var target = UnsafeMutablePointer<sockaddr_in>.allocate(capacity: MemoryLayout<sockaddr_in>.size as opposed to var target = UnsafeMutablePointer<sockaddr_in>.allocate(capacity: 1).
After fixing this to, I started to get better results (about 16 out of 1000 sends passed), but obviously it was not enough.
I Found Send a message using UDP in Swift 3, and decided to change the use of sockaddr_in to var target = sockaddr_in(sin_len: __uint8_t(MemoryLayout<sockaddr_in>.size), sin_family: sa_family_t(AF_INET), sin_port: in_port_t(onPort)!, sin_addr: in_addr(s_addr: inet_addr(toIP)), sin_zero: (0,0,0,0, 0,0,0,0)), everything works.
I'm still puzzled as to why using Unsafe Memory with this struct didn't work though.
Another thing: I moved this code back to my actual App, trying to bind the socket to my own addrinfo via getaddrinfo constantly fails with Can't assign requested address, using the one I get from enumerated interfaces works, but I receive lots of No buffer space available errors (something for another research :).
In the test code, both binding methods (enumerated & getaddrinfo) work fine.
Fixed sendData function:
public static func sendData(toIP: String, onPort : String, withSocketDescriptor : Int32, data : Data) -> Int{
print("sendData called for targetIP: \(toIP):\(onPort) with socket descriptor: \(withSocketDescriptor)")
var target = sockaddr_in(sin_len: __uint8_t(MemoryLayout<sockaddr_in>.size), sin_family: sa_family_t(AF_INET), sin_port: in_port_t(bigEndian: onPort)!, sin_addr: in_addr(s_addr: inet_addr(toIP)), sin_zero: (0,0,0,0, 0,0,0,0))
var res = 0
data.withUnsafeBytes { (u8Ptr: UnsafePointer<UInt8>) in
let rawPtr = UnsafeRawPointer(u8Ptr)
withUnsafeMutablePointer(to: &target) {
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
let bytesSent = sendto(withSocketDescriptor, rawPtr, data.count, 0, $0, socklen_t(MemoryLayout.size(ofValue: target)))
if bytesSent > 0 {
print("😄😄😄 Sent \(bytesSent) bytes 😄😄😄")
res = 1
}
if bytesSent == -1 {
let strError = String(utf8String: strerror(errno)) ?? "Unknown error code"
let message = "Socket sendto error \(errno) (\(strError))"
print(message)
}
}
}
}
return res
}

Argument labels '(count:, repeatedValue:)' do not match any available overloads

I'm migrating my Swift code from version 2 to 3 and there is this issue:
Argument labels '(count:, repeatedValue:)' do not match any available overloads
My code
static func getWiFiAddress() -> String? {
var address: String?
var ifaddr: UnsafeMutablePointer<ifaddrs>? = nil
if getifaddrs(&ifaddr) == 0 {
var ptr = ifaddr
while ptr != nil {
defer { ptr = ptr?.pointee.ifa_next }
let interface = ptr?.pointee
let addrFamily = interface?.ifa_addr.pointee.sa_family
if addrFamily == UInt8(AF_INET) || addrFamily == UInt8(AF_INET6) {
let name = String(cString: (interface?.ifa_name)!)
var addr = interface?.ifa_addr.pointee
// issue while assigning to hostname variable
var hostname = [CChar](count: Int(NI_MAXHOST), repeatedValue: 0)
getnameinfo(&addr, socklen_t(interface.ifa_addr.memory.sa_len),
&hostname, socklen_t(hostname.count),
nil, socklen_t(0), NI_NUMERICHOST)
address = String.fromCString(hostname)
}
}
freeifaddrs(ifaddr)
}
if address == nil {
address = ""
}
return address
}
In Swift 3 they changed the function for no good reason. You should have
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
Note that, not only are the arguments reversed but repeatedValue: -> repeating:

Swift 2 check if port is busy

can someone tell me how to find out in Swift2 if Port is busy?
Because I write a mac app that has a self written Tcp server, but sometimes it wont start up because it "cant bind to port". So how can I check if the port is not used, to block the start button of the Tcp server until the port is free, again?
And I do not want a new framework.
Thanks
mainly code taken from Swifter: https://github.com/glock45/swifter
func checkTcpPortForListen(port: in_port_t) -> (Bool, descr: String){
let socketFileDescriptor = socket(AF_INET, SOCK_STREAM, 0)
if socketFileDescriptor == -1 {
return (false, "SocketCreationFailed, \(descriptionOfLastError())")
}
var addr = sockaddr_in()
addr.sin_len = __uint8_t(sizeof(sockaddr_in))
addr.sin_family = sa_family_t(AF_INET)
addr.sin_port = Int(OSHostByteOrder()) == OSLittleEndian ? _OSSwapInt16(port) : port
addr.sin_addr = in_addr(s_addr: inet_addr("0.0.0.0"))
addr.sin_zero = (0, 0, 0, 0, 0, 0, 0, 0)
var bind_addr = sockaddr()
memcpy(&bind_addr, &addr, Int(sizeof(sockaddr_in)))
if bind(socketFileDescriptor, &bind_addr, socklen_t(sizeof(sockaddr_in))) == -1 {
let details = descriptionOfLastError()
release(socketFileDescriptor)
return (false, "\(port), BindFailed, \(details)")
}
if listen(socketFileDescriptor, SOMAXCONN ) == -1 {
let details = descriptionOfLastError()
release(socketFileDescriptor)
return (false, "\(port), ListenFailed, \(details)")
}
release(socketFileDescriptor)
return (true, "\(port) is free for use")
}
func release(socket: Int32) {
Darwin.shutdown(socket, SHUT_RDWR)
close(socket)
}
func descriptionOfLastError() -> String {
return String.fromCString(UnsafePointer(strerror(errno))) ?? "Error: \(errno)"
}
Update of the correct answer for Swift 4:
func checkTcpPortForListen(port: in_port_t) -> (Bool, descr: String) {
let socketFileDescriptor = socket(AF_INET, SOCK_STREAM, 0)
if socketFileDescriptor == -1 {
return (false, "SocketCreationFailed, \(descriptionOfLastError())")
}
var addr = sockaddr_in()
let sizeOfSockkAddr = MemoryLayout<sockaddr_in>.size
addr.sin_len = __uint8_t(sizeOfSockkAddr)
addr.sin_family = sa_family_t(AF_INET)
addr.sin_port = Int(OSHostByteOrder()) == OSLittleEndian ? _OSSwapInt16(port) : port
addr.sin_addr = in_addr(s_addr: inet_addr("0.0.0.0"))
addr.sin_zero = (0, 0, 0, 0, 0, 0, 0, 0)
var bind_addr = sockaddr()
memcpy(&bind_addr, &addr, Int(sizeOfSockkAddr))
if Darwin.bind(socketFileDescriptor, &bind_addr, socklen_t(sizeOfSockkAddr)) == -1 {
let details = descriptionOfLastError()
release(socket: socketFileDescriptor)
return (false, "\(port), BindFailed, \(details)")
}
if listen(socketFileDescriptor, SOMAXCONN ) == -1 {
let details = descriptionOfLastError()
release(socket: socketFileDescriptor)
return (false, "\(port), ListenFailed, \(details)")
}
release(socket: socketFileDescriptor)
return (true, "\(port) is free for use")
}
func release(socket: Int32) {
Darwin.shutdown(socket, SHUT_RDWR)
close(socket)
}
func descriptionOfLastError() -> String {
return String.init(cString: (UnsafePointer(strerror(errno))))
}
EDIT:
example for calling this function:
var portNum: UInt16 = 0
for i in 50000..<65000 {
let (isFree, _) = checkTcpPortForListen(port: UInt16(i))
if isFree == true {
portNum = UInt16(i)
break;
}
}

Epub Font Mangling is not working

I am creating an EPUB 3 reader for iOS using Swift 2.
The problem I'm currently facing is with font obfuscation / font mangling. I've read a tutorial that goes over how to do that in Swift, and integrated it into my project with some adaptations.
When I load an obfuscated epub into my app, the fonts are not loaded correctly and fall back to other system fonts. When I load an epub with the same fonts but not obfuscated, everything looks fine. Obviously, that means there's something wrong with my obfuscation code, but I can't for the life of me find the error.
Here's my code:
public struct Crypto {
public func obfuscateFontIDPF(data:NSData, key:String) -> NSData {
let source = data
var destination = [UInt8]()
let shaKey = key.sha1()
let keyData = shaKey.utf8Array
var arr = [UInt8](count: source.length, repeatedValue: 0)
source.getBytes(&arr, length:source.length)
var outer = 0
while outer < 52 && arr.isEmpty == false {
var inner = 0
while inner < 20 && arr.isEmpty == false {
let byte = arr.removeAtIndex(0) //Assumes read advances file position
let sourceByte = byte
let keyByte = keyData[inner]
let obfuscatedByte = sourceByte ^ keyByte
destination.append(obfuscatedByte)
inner++
}
outer++
}
if arr.isEmpty == false {
while arr.isEmpty == false {
let byte = arr.removeAtIndex(0)
destination.append(byte)
}
}
let newData = NSData(bytes: &destination, length: destination.count*sizeof(UInt8))
return newData
}
}
extension String {
func sha1() -> String {
var selfAsSha1 = ""
if let data = self.dataUsingEncoding(NSUTF8StringEncoding)
{
var digest = [UInt8](count: Int(CC_SHA1_DIGEST_LENGTH), repeatedValue: 0)
CC_SHA1(data.bytes, CC_LONG(data.length), &digest)
for index in 0..<CC_SHA1_DIGEST_LENGTH
{
selfAsSha1 += String(format: "%02x", digest[Int(index)])
}
}
return selfAsSha1
}
var utf8Array: [UInt8] {
return Array(utf8)
}
}
And here I call the obfuscation method:
func parserDidEndDocument(parser: NSXMLParser) {
if encryptedFilePaths!.count != 0 {
for file in encryptedFilePaths! {
let epubMainDirectoryPath = NSString(string: epubBook!.epubMainFolderPath!).stringByDeletingLastPathComponent
let fullFilePath = epubMainDirectoryPath.stringByAppendingString("/" + file)
let url = NSURL(fileURLWithPath: fullFilePath)
if let source = NSData(contentsOfURL: url) {
let decryptedFont = Crypto().obfuscateFontIDPF(source, key: self.epubBook!.encryptionKey!)
do {
try decryptedFont.writeToFile(fullFilePath, options: .DataWritingAtomic)
} catch {
print(error)
}
}
}
}
}
If you see where the error might be, please let me know.
I figured it out, here is the working code:
private func obfuscateData(data: NSData, key: String) -> NSData {
var destinationBytes = [UInt8]()
// Key needs to be SHA1 hash with length of exactly 20 chars
let hashedKeyBytes = generateHashedBytesFromString(key)
var sourceBytes = [UInt8](count: data.length, repeatedValue: 0)
data.getBytes(&sourceBytes, length: data.length)
var outerCount = 0
while outerCount < 52 && sourceBytes.isEmpty == false {
var innerCount = 0
while innerCount < 20 && sourceBytes.isEmpty == false {
let sourceByte = sourceBytes.removeAtIndex(0)
let keyByte = hashedKeyBytes[innerCount]
let obfuscatedByte = (sourceByte ^ keyByte)
destinationBytes.append(obfuscatedByte)
innerCount += 1
}
outerCount += 1
}
destinationBytes.appendContentsOf(sourceBytes)
let destinationData = NSData(bytes: &destinationBytes, length: destinationBytes.count*sizeof(UInt8))
sourceBytes.removeAll(keepCapacity: false)
destinationBytes.removeAll(keepCapacity: false)
return destinationData
}
/// Convert the key string to a SHA1 hashed Byte Array
private func generateHashedBytesFromString(string: String) -> [UInt8] {
var resultBytes = [UInt8]()
var hashedString = string.sha1()
for _ in 0.stride(to: hashedString.characters.count, by: 2) {
let character = "0x\(hashedString.returnTwoCharacters())"
resultBytes.append(UInt8(strtod(character, nil)))
}
return resultBytes
}
extension String {
func sha1() -> String {
var selfAsSha1 = ""
if let data = self.dataUsingEncoding(NSUTF8StringEncoding) {
var digest = [UInt8](count: Int(CC_SHA1_DIGEST_LENGTH), repeatedValue: 0)
CC_SHA1(data.bytes, CC_LONG(data.length), &digest)
for index in 0..<CC_SHA1_DIGEST_LENGTH {
selfAsSha1 += String(format: "%02x", digest[Int(index)])
}
}
return selfAsSha1
}
mutating func returnTwoCharacters() -> String {
var characters: String = ""
characters.append(self.removeAtIndex(startIndex))
characters.append(self.removeAtIndex(startIndex))
return characters
}
}

Swift - Get device's WIFI IP Address

I need to get IP Address of iOS device in Swift. This is not a duplicate of other questions about this! I need to get only WiFi IP address, if there is no wifi ip address - I need to handle it. There are a few questions about it on Stack Overflow, but there are only functions that return ip addresses. For example (from How to get Ip address in swift):
func getIFAddresses() -> [String] {
var addresses = [String]()
// Get list of all interfaces on the local machine:
var ifaddr : UnsafeMutablePointer<ifaddrs> = nil
if getifaddrs(&ifaddr) == 0 {
// For each interface ...
for (var ptr = ifaddr; ptr != nil; ptr = ptr.memory.ifa_next) {
let flags = Int32(ptr.memory.ifa_flags)
var addr = ptr.memory.ifa_addr.memory
// Check for running IPv4, IPv6 interfaces. Skip the loopback interface.
if (flags & (IFF_UP|IFF_RUNNING|IFF_LOOPBACK)) == (IFF_UP|IFF_RUNNING) {
if addr.sa_family == UInt8(AF_INET) || addr.sa_family == UInt8(AF_INET6) {
// Convert interface address to a human readable string:
var hostname = [CChar](count: Int(NI_MAXHOST), repeatedValue: 0)
if (getnameinfo(&addr, socklen_t(addr.sa_len), &hostname, socklen_t(hostname.count),
nil, socklen_t(0), NI_NUMERICHOST) == 0) {
if let address = String.fromCString(hostname) {
addresses.append(address)
}
}
}
}
}
freeifaddrs(ifaddr)
}
return addresses
}
Here I get 2 values - address from mobile internet(I think) and WiFi address I need. Is there any other way to get ONLY WiFi IP Address?
According to several SO threads (e.g. What exactly means iOS networking interface name? what's pdp_ip ? what's ap?), the WiFi interface on an iOS device always has then name "en0".
Your code (which seems to be what I answered at How to get Ip address in swift :) retrieves a list of the IP addresses of all running network interfaces. It can easily be modified to return only the IP address
of the "en0" interface, and actually that is what I originally had
answered at that thread (and this is just a Swift translation of the
answer to how to get ip address of iphone programmatically):
// Return IP address of WiFi interface (en0) as a String, or `nil`
func getWiFiAddress() -> String? {
var address : String?
// Get list of all interfaces on the local machine:
var ifaddr : UnsafeMutablePointer<ifaddrs> = nil
if getifaddrs(&ifaddr) == 0 {
// For each interface ...
var ptr = ifaddr
while ptr != nil {
defer { ptr = ptr.memory.ifa_next }
let interface = ptr.memory
// Check for IPv4 or IPv6 interface:
let addrFamily = interface.ifa_addr.memory.sa_family
if addrFamily == UInt8(AF_INET) || addrFamily == UInt8(AF_INET6) {
// Check interface name:
if let name = String.fromCString(interface.ifa_name) where name == "en0" {
// Convert interface address to a human readable string:
var hostname = [CChar](count: Int(NI_MAXHOST), repeatedValue: 0)
getnameinfo(interface.ifa_addr, socklen_t(interface.ifa_addr.memory.sa_len),
&hostname, socklen_t(hostname.count),
nil, socklen_t(0), NI_NUMERICHOST)
address = String.fromCString(hostname)
}
}
}
freeifaddrs(ifaddr)
}
return address
}
Usage:
if let addr = getWiFiAddress() {
print(addr)
} else {
print("No WiFi address")
}
Update for Swift 3: In addition to adopting the code to the
many changes in Swift 3,
iterating over all interfaces can now use the new generalized
sequence() function:
Do NOT forget to add #include <ifaddrs.h> in your bridging header
// Return IP address of WiFi interface (en0) as a String, or `nil`
func getWiFiAddress() -> String? {
var address : String?
// Get list of all interfaces on the local machine:
var ifaddr : UnsafeMutablePointer<ifaddrs>?
guard getifaddrs(&ifaddr) == 0 else { return nil }
guard let firstAddr = ifaddr else { return nil }
// For each interface ...
for ifptr in sequence(first: firstAddr, next: { $0.pointee.ifa_next }) {
let interface = ifptr.pointee
// Check for IPv4 or IPv6 interface:
let addrFamily = interface.ifa_addr.pointee.sa_family
if addrFamily == UInt8(AF_INET) || addrFamily == UInt8(AF_INET6) {
// Check interface name:
let name = String(cString: interface.ifa_name)
if name == "en0" {
// Convert interface address to a human readable string:
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
getnameinfo(interface.ifa_addr, socklen_t(interface.ifa_addr.pointee.sa_len),
&hostname, socklen_t(hostname.count),
nil, socklen_t(0), NI_NUMERICHOST)
address = String(cString: hostname)
}
}
}
freeifaddrs(ifaddr)
return address
}
For those of you who came looking for more than the WIFI IP you could modify this code a little
func getAddress(for network: Network) -> String? {
var address: String?
// Get list of all interfaces on the local machine:
var ifaddr: UnsafeMutablePointer<ifaddrs>?
guard getifaddrs(&ifaddr) == 0 else { return nil }
guard let firstAddr = ifaddr else { return nil }
// For each interface ...
for ifptr in sequence(first: firstAddr, next: { $0.pointee.ifa_next }) {
let interface = ifptr.pointee
// Check for IPv4 or IPv6 interface:
let addrFamily = interface.ifa_addr.pointee.sa_family
if addrFamily == UInt8(AF_INET) || addrFamily == UInt8(AF_INET6) {
// Check interface name:
let name = String(cString: interface.ifa_name)
if name == network.rawValue {
// Convert interface address to a human readable string:
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
getnameinfo(interface.ifa_addr, socklen_t(interface.ifa_addr.pointee.sa_len),
&hostname, socklen_t(hostname.count),
nil, socklen_t(0), NI_NUMERICHOST)
address = String(cString: hostname)
}
}
}
freeifaddrs(ifaddr)
return address
}
enum Network: String {
case wifi = "en0"
case cellular = "pdp_ip0"
//... case ipv4 = "ipv4"
//... case ipv6 = "ipv6"
}
Then we have access to the cellular IP as well.
guard let wifiIp = getAddress(for: .wifi) else { return }
&
guard let cellularIp = getAddress(for: .cellular) else { return }
To get IPAddress for wifi , wired, and cellular - swift 5
func getIPAddress() -> String {
var address: String?
var ifaddr: UnsafeMutablePointer<ifaddrs>? = nil
if getifaddrs(&ifaddr) == 0 {
var ptr = ifaddr
while ptr != nil {
defer { ptr = ptr?.pointee.ifa_next }
guard let interface = ptr?.pointee else { return "" }
let addrFamily = interface.ifa_addr.pointee.sa_family
if addrFamily == UInt8(AF_INET) || addrFamily == UInt8(AF_INET6) {
// wifi = ["en0"]
// wired = ["en2", "en3", "en4"]
// cellular = ["pdp_ip0","pdp_ip1","pdp_ip2","pdp_ip3"]
let name: String = String(cString: (interface.ifa_name))
if name == "en0" || name == "en2" || name == "en3" || name == "en4" || name == "pdp_ip0" || name == "pdp_ip1" || name == "pdp_ip2" || name == "pdp_ip3" {
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
getnameinfo(interface.ifa_addr, socklen_t((interface.ifa_addr.pointee.sa_len)), &hostname, socklen_t(hostname.count), nil, socklen_t(0), NI_NUMERICHOST)
address = String(cString: hostname)
}
}
}
freeifaddrs(ifaddr)
}
return address ?? ""
}
How to use
let strIPAddress : String = self.getIPAddress()
print("IPAddress :: \(strIPAddress)")
Note : Add this below in your project's Bridging-Header file
#include<ifaddrs.h>
Create bridging header and Include #include <ifaddrs.h> in it.
then write this method
func getIFAddresses() -> [String] {
var addresses = [String]()
// Get list of all interfaces on the local machine:
var ifaddr : UnsafeMutablePointer<ifaddrs> = nil
if getifaddrs(&ifaddr) == 0 {
// For each interface ...
for (var ptr = ifaddr; ptr != nil; ptr = ptr.memory.ifa_next) {
let flags = Int32(ptr.memory.ifa_flags)
var addr = ptr.memory.ifa_addr.memory
// Check for running IPv4, IPv6 interfaces. Skip the loopback interface.
if (flags & (IFF_UP|IFF_RUNNING|IFF_LOOPBACK)) == (IFF_UP|IFF_RUNNING) {
if addr.sa_family == UInt8(AF_INET) || addr.sa_family == UInt8(AF_INET6) {
// Convert interface address to a human readable string:
var hostname = [CChar](count: Int(NI_MAXHOST), repeatedValue: 0)
if (getnameinfo(&addr, socklen_t(addr.sa_len), &hostname, socklen_t(hostname.count),
nil, socklen_t(0), NI_NUMERICHOST) == 0) {
if let address = String.fromCString(hostname) {
addresses.append(address)
}
}
}
}
}
freeifaddrs(ifaddr)
}
return addresses
}
when I call this method in my viewController like var arr : NSArray = self.getIFAddresses() I get perfect response in my console like
IP :(
"10.0.0.94"
)
from this array you can access it where ever u want.
Hope it helps
Swift 4.2 UIDevice extension that avoids force unwraps and supports cellular and wired ip addresses:
import UIKit
extension UIDevice {
private struct InterfaceNames {
static let wifi = ["en0"]
static let wired = ["en2", "en3", "en4"]
static let cellular = ["pdp_ip0","pdp_ip1","pdp_ip2","pdp_ip3"]
static let supported = wifi + wired + cellular
}
func ipAddress() -> String? {
var ipAddress: String?
var ifaddr: UnsafeMutablePointer<ifaddrs>?
if getifaddrs(&ifaddr) == 0 {
var pointer = ifaddr
while pointer != nil {
defer { pointer = pointer?.pointee.ifa_next }
guard
let interface = pointer?.pointee,
interface.ifa_addr.pointee.sa_family == UInt8(AF_INET) || interface.ifa_addr.pointee.sa_family == UInt8(AF_INET6),
let interfaceName = interface.ifa_name,
let interfaceNameFormatted = String(cString: interfaceName, encoding: .utf8),
InterfaceNames.supported.contains(interfaceNameFormatted)
else { continue }
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
getnameinfo(interface.ifa_addr,
socklen_t(interface.ifa_addr.pointee.sa_len),
&hostname,
socklen_t(hostname.count),
nil,
socklen_t(0),
NI_NUMERICHOST)
guard
let formattedIpAddress = String(cString: hostname, encoding: .utf8),
!formattedIpAddress.isEmpty
else { continue }
ipAddress = formattedIpAddress
break
}
freeifaddrs(ifaddr)
}
return ipAddress
}
}
Usage:
UIDevice.current.ipAddress()
Swift 4 - Get device's IP Address:
Add #include<ifaddrs.h> in your bridging header.
This is the framework needed to get IP address.
class func getIPAddress() -> String? {
var address: String?
var ifaddr: UnsafeMutablePointer<ifaddrs>? = nil
if getifaddrs(&ifaddr) == 0 {
var ptr = ifaddr
while ptr != nil {
defer { ptr = ptr?.pointee.ifa_next }
let interface = ptr?.pointee
let addrFamily = interface?.ifa_addr.pointee.sa_family
if addrFamily == UInt8(AF_INET) || addrFamily == UInt8(AF_INET6) {
if let name: String = String(cString: (interface?.ifa_name)!), name == "en0" {
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
getnameinfo(interface?.ifa_addr, socklen_t((interface?.ifa_addr.pointee.sa_len)!), &hostname, socklen_t(hostname.count), nil, socklen_t(0), NI_NUMERICHOST)
address = String(cString: hostname)
}
}
}
freeifaddrs(ifaddr)
}
return address
}
Another solution thats a bit of a different approach is using Ipify, which is a free lightweight service that you can ping to get your IP
Heres their Swift example:
let url = URL(string: "https://api.ipify.org")
do {
if let url = url {
let ipAddress = try String(contentsOf: url)
print("My public IP address is: " + ipAddress)
}
} catch let error {
print(error)
}
You can fetch IP address using the code below:
Note : I've used reachability so that it captures new IP address in case WiFi is changed to another.
In Podfile file
pod 'ReachabilitySwift' and then install pod
In AppDelegate.swift file import ReachabilitySwift
Note : If it prompts an error that Could not find ReachabilitySwift module then simply copy and paste this. It works!
didFinishLaunchingOptions function
NotificationCenter.default.addObserver(self, selector: #selector(self.reachabilityChanged), name: ReachabilityChangedNotification, object: reachability)
do{
try reachability.startNotifier()
}
catch {
print("could not start reachability notifier")
}
Then copy paste below code in AppDelegate file
func reachabilityChanged(note: NSNotification) {
let reachability = note.object as! Reachability
if reachability.isReachable {
if reachability.isReachableViaWiFi {
print("Reachable via WiFi")
} else {
print("Reachable via Cellular")
}
setIPAddress()
} else {
ipAddress = "" // No IP captures
print("Network not reachable")
}
}
func setIPAddress() {
if let addr = self.getWiFiAddress() {
print("ipAddress : \(addr)")
ipAddress = addr
} else {
ipAddress = "" // No IP captures
print("No WiFi address")
}
}
// Return IP address of WiFi interface (en0) as a String, or `nil`
func getWiFiAddress() -> String? {
var address : String?
// Get list of all interfaces on the local machine:
var ifaddr : UnsafeMutablePointer<ifaddrs>?
guard getifaddrs(&ifaddr) == 0 else { return nil }
guard let firstAddr = ifaddr else { return nil }
// For each interface ...
for ifptr in sequence(first: firstAddr, next: { $0.pointee.ifa_next }) {
let interface = ifptr.pointee
// Check for IPv4 or IPv6 interface:
let addrFamily = interface.ifa_addr.pointee.sa_family
if addrFamily == UInt8(AF_INET) || addrFamily == UInt8(AF_INET6) {
// Check interface name:
let name = String(cString: interface.ifa_name)
if name == "en0" {
// Convert interface address to a human readable string:
var addr = interface.ifa_addr.pointee
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
getnameinfo(&addr, socklen_t(interface.ifa_addr.pointee.sa_len),
&hostname, socklen_t(hostname.count),
nil, socklen_t(0), NI_NUMERICHOST)
address = String(cString: hostname)
}
}
}
freeifaddrs(ifaddr)
return address
}
Add this in Bridging-Header file #include<ifaddrs.h>
In case you don't have this file then you can create it Check this link
6.
func applicationWillEnterForeground(_ application: UIApplication) {
// Post notification
NotificationCenter.default.post(name: ReachabilityChangedNotification, object: reachability)
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}
If you want to remove observer, then:
reachability.stopNotifier()
NSNotificationCenter.defaultCenter().removeObserver(self,name: ReachabilityChangedNotification,object: reachability)
Swift 5 Cleanup
I updated an answer above to eliminate any force unwrapping and some SwiftLint cleanup.
class func getIPAddress() -> String? {
var address: String?
var ifaddr: UnsafeMutablePointer<ifaddrs>?
if getifaddrs(&ifaddr) == 0 {
var ptr = ifaddr
while ptr != nil {
defer { ptr = ptr?.pointee.ifa_next }
let interface = ptr?.pointee
let addrFamily = interface?.ifa_addr.pointee.sa_family
if addrFamily == UInt8(AF_INET) || addrFamily == UInt8(AF_INET6),
let cString = interface?.ifa_name,
String(cString: cString) == "en0",
let saLen = (interface?.ifa_addr.pointee.sa_len) {
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
let ifaAddr = interface?.ifa_addr
getnameinfo(ifaAddr,
socklen_t(saLen),
&hostname,
socklen_t(hostname.count),
nil,
socklen_t(0),
NI_NUMERICHOST)
address = String(cString: hostname)
}
}
freeifaddrs(ifaddr)
}
return address
}
An extension for NWInterface.InterfaceType based on some of the above answers:
import Network
extension NWInterface.InterfaceType {
var names : [String]? {
switch self {
case .wifi: return ["en0"]
case .wiredEthernet: return ["en2", "en3", "en4"]
case .cellular: return ["pdp_ip0","pdp_ip1","pdp_ip2","pdp_ip3"]
default: return nil
}
}
func address(family: Int32) -> String?
{
guard let names = names else { return nil }
var address : String?
for name in names {
guard let nameAddress = self.address(family: family, name: name) else { continue }
address = nameAddress
break
}
return address
}
func address(family: Int32, name: String) -> String? {
var address: String?
// Get list of all interfaces on the local machine:
var ifaddr: UnsafeMutablePointer<ifaddrs>?
guard getifaddrs(&ifaddr) == 0, let firstAddr = ifaddr else { return nil }
// For each interface ...
for ifptr in sequence(first: firstAddr, next: { $0.pointee.ifa_next }) {
let interface = ifptr.pointee
let addrFamily = interface.ifa_addr.pointee.sa_family
if addrFamily == UInt8(family)
{
// Check interface name:
if name == String(cString: interface.ifa_name) {
// Convert interface address to a human readable string:
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
getnameinfo(interface.ifa_addr, socklen_t(interface.ifa_addr.pointee.sa_len),
&hostname, socklen_t(hostname.count),
nil, socklen_t(0), NI_NUMERICHOST)
address = String(cString: hostname)
}
}
}
freeifaddrs(ifaddr)
return address
}
var ipv4 : String? { self.address(family: AF_INET) }
var ipv6 : String? { self.address(family: AF_INET6) }
}
Usage:
For ipv4 of cellular: NWInterface.InterfaceType.cellular.ipv4
func getIPAddress() -> String {
var address: String = "error"
var interfaces: ifaddrs? = nil
var temp_addr: ifaddrs? = nil
var success: Int = 0
// retrieve the current interfaces - returns 0 on success
success = getifaddrs(interfaces)
if success == 0 {
// Loop through linked list of interfaces
temp_addr = interfaces
while temp_addr != nil {
if temp_addr?.ifa_addr?.sa_family == AF_INET {
// Check if interface is en0 which is the wifi connection on the iPhone
if (String(utf8String: temp_addr?.ifa_name) == "en0") {
// Get NSString from C String
address = String(utf8String: inet_ntoa((temp_addr?.ifa_addr as? sockaddr_in)?.sin_addr))
}
}
temp_addr = temp_addr?.ifa_next
}
}
// Free memory
freeifaddrs(interfaces)
return address
}
All the answers here gives only ip address for wifi and not wired or cellular. Following snippet can be used for wifi/wired/cellular case:
func getIPAddressForCellOrWireless()-> String? {
let WIFI_IF : [String] = ["en0"]
let KNOWN_WIRED_IFS : [String] = ["en2", "en3", "en4"]
let KNOWN_CELL_IFS : [String] = ["pdp_ip0","pdp_ip1","pdp_ip2","pdp_ip3"]
var addresses : [String : String] = ["wireless":"",
"wired":"",
"cell":""]
var address: String?
var ifaddr: UnsafeMutablePointer<ifaddrs>? = nil
if getifaddrs(&ifaddr) == 0 {
var ptr = ifaddr
while ptr != nil {
defer { ptr = ptr?.pointee.ifa_next } // memory has been renamed to pointee in swift 3 so changed memory to pointee
let interface = ptr?.pointee
let addrFamily = interface?.ifa_addr.pointee.sa_family
if addrFamily == UInt8(AF_INET) || addrFamily == UInt8(AF_INET6) {
if let name: String = String(cString: (interface?.ifa_name)!), (WIFI_IF.contains(name) || KNOWN_WIRED_IFS.contains(name) || KNOWN_CELL_IFS.contains(name)) {
// String.fromCString() is deprecated in Swift 3. So use the following code inorder to get the exact IP Address.
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
getnameinfo(interface?.ifa_addr, socklen_t((interface?.ifa_addr.pointee.sa_len)!), &hostname, socklen_t(hostname.count), nil, socklen_t(0), NI_NUMERICHOST)
address = String(cString: hostname)
if WIFI_IF.contains(name){
addresses["wireless"] = address
}else if KNOWN_WIRED_IFS.contains(name){
addresses["wired"] = address
}else if KNOWN_CELL_IFS.contains(name){
addresses["cell"] = address
}
}
}
}
}
freeifaddrs(ifaddr)
var ipAddressString : String?
let wirelessString = addresses["wireless"]
let wiredString = addresses["wired"]
let cellString = addresses["cell"]
if let wirelessString = wirelessString, wirelessString.count > 0{
ipAddressString = wirelessString
}else if let wiredString = wiredString, wiredString.count > 0{
ipAddressString = wiredString
}else if let cellString = cellString, cellString.count > 0{
ipAddressString = cellString
}
return ipAddressString
}
This class monitors the device interfaces with NWPathMonitor. You can set an updatehandler that informs you about interface state changes. Choose if you want to track ipv4 or ipv6 and check for status.interfaceType == .wifi to see, if your active interface is wifi
Usage:
let monitor = IPMonitor(ipType: .ipv4)
monitor.pathUpdateHandler = { status in
print("\(status.debugDescription)")
}
Class:
import Foundation
import Network
class IPMonitor {
enum InterfaceType: String {
case cellular = "cellular"
case wifi = "wifi"
case wired = "wired"
case loopback = "loopback"
case other = "other"
case notFound = "not found"
}
enum IPType: String {
case ipv4 = "IPv4"
case ipv6 = "ipV6"
case unknown = "unknown"
}
struct Status {
var name = "unknown"
var interfaceType: InterfaceType = InterfaceType.notFound
var ip: [String] = []
var ipType: IPType = IPType.unknown
var debugDescription: String {
let result = "Interface: \(name)/\(interfaceType.rawValue), \(ipType.rawValue)\(ip.debugDescription)"
return result
}
}
private let monitor = NWPathMonitor()
private let queue = DispatchQueue(label: "ip_monitor_queue")
final var pathUpdateHandler: ((Status) -> Void)?
init(ipType: IPType) {
monitor.pathUpdateHandler = { path in
let name = self.getInterfaceName(path: path)
let type = self.getInterfaceType(path: path)
let ip = self.getIPAddresses(interfaceName: name, ipType: ipType)
let status = Status(name: name, interfaceType: type, ip: ip, ipType: ipType)
//print("\(status)")
self.pathUpdateHandler?(status)
}
monitor.start(queue: queue)
}
private func getInterfaceName(path: NWPath) -> String {
if let name = path.availableInterfaces.first?.name {
return name
}
return "unknown"
}
private func getInterfaceType(path: NWPath) -> InterfaceType {
if let type = path.availableInterfaces.first?.type {
switch type {
case NWInterface.InterfaceType.cellular:
return InterfaceType.cellular
case NWInterface.InterfaceType.wifi:
return InterfaceType.wifi
case NWInterface.InterfaceType.wiredEthernet:
return InterfaceType.wired
case NWInterface.InterfaceType.loopback:
return InterfaceType.loopback
default:
return InterfaceType.other
}
}
return InterfaceType.notFound
}
private func getIPAddresses(interfaceName: String, ipType: IPType)-> [String]{
var addresses: [String] = []
var ifaddr: UnsafeMutablePointer<ifaddrs>? = nil
if getifaddrs(&ifaddr) == 0 {
var ptr = ifaddr
while ptr != nil {
defer { ptr = ptr?.pointee.ifa_next }
let interface = ptr?.pointee
let addrFamily = interface?.ifa_addr.pointee.sa_family
if (addrFamily == UInt8(AF_INET) && ipType == .ipv4)
|| (addrFamily == UInt8(AF_INET6) && ipType == .ipv6) {
let name = String(cString: (interface?.ifa_name)!)
if name == interfaceName {
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
getnameinfo(interface?.ifa_addr, socklen_t((interface?.ifa_addr.pointee.sa_len)!), &hostname, socklen_t(hostname.count), nil, socklen_t(0), NI_NUMERICHOST)
addresses.append(String(cString: hostname))
}
}
}
}
freeifaddrs(ifaddr)
return addresses
}
}
If you want only IPv4 response as output just modify the solution by Martin R.
func getWiFiAddress() -> String? {
var address : String?
// Get list of all interfaces on the local machine:
var ifaddr : UnsafeMutablePointer<ifaddrs>?
guard getifaddrs(&ifaddr) == 0 else { return nil }
guard let firstAddr = ifaddr else { return nil }
// For each interface ...
for ifptr in sequence(first: firstAddr, next: { $0.pointee.ifa_next }) {
let interface = ifptr.pointee
// Check for IPv4 or IPv6 interface:
let addrFamily = interface.ifa_addr.pointee.sa_family
//if addrFamily == UInt8(AF_INET) || addrFamily == UInt8(AF_INET6) { // **ipv6 committed
if addrFamily == UInt8(AF_INET){
// Check interface name:
let name = String(cString: interface.ifa_name)
if name == "en0" {
// Convert interface address to a human readable string:
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
getnameinfo(interface.ifa_addr, socklen_t(interface.ifa_addr.pointee.sa_len),
&hostname, socklen_t(hostname.count),
nil, socklen_t(0), NI_NUMERICHOST)
address = String(cString: hostname)
}
}
}
freeifaddrs(ifaddr)
return address
}
Usage:
if let addr = getWiFiAddress() {
print(addr)
} else {
print("No WiFi address")
}
SUPPORT FOR IPV4 & IPV6
Make it an extension of UIDevice and call using UIDevice.current.getIPAdress()
private struct Interfaces {
// INTERFACCIE SUPPORT
static let wifi = ["en0"]
static let cellular = ["pdp_ip0","pdp_ip1","pdp_ip2","pdp_ip3"]
static let supported = wifi + cellular
}
func getIPAdress() -> (String?,String?)? {
var ip4Adress: String?
var ip6Adress: String?
var hasAdress: UnsafeMutablePointer<ifaddrs>?
if getifaddrs(&hasAdress) == 0 {
var pointer = hasAdress
while pointer != nil {
defer { pointer = pointer?.pointee.ifa_next}
guard let interface = pointer?.pointee else {continue}
// SEARCH FOR IPV4 OR IPV6 IN THE INTERFACE OF THE NODE
// HERE I'M ALREADY LOOSING MY MIND
// PRIORITY FOR IPV4 THAN IPV6
if interface.ifa_addr.pointee.sa_family == UInt8(AF_INET) {
guard let ip4 = processInterface(interface: interface) else {
continue
}
ip4Adress = ip4
}
if interface.ifa_addr.pointee.sa_family == UInt8(AF_INET6) {
guard let ip6 = processInterface(interface: interface) else {
continue
}
ip6Adress = ip6
}
}
freeifaddrs(hasAdress)
}
return (ip4Adress, ip6Adress)
}
func processInterface(interface: ifaddrs) -> String? {
var ipAdress: String = ""
guard
let interfaceName = interface.ifa_name else {return nil}
guard
let interfaceNameFormatted = String(cString: interfaceName, encoding: .utf8) else {return nil}
guard Interfaces.supported.contains(interfaceNameFormatted) else {return nil}
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
print(interfaceNameFormatted)
// CONVERT THE SOCKET ADRESS TO A CORRESPONDING HOST AND SERVICE
getnameinfo(interface.ifa_addr,
socklen_t(interface.ifa_addr.pointee.sa_len),
&hostname, socklen_t(hostname.count),
nil,
socklen_t(0),
NI_NUMERICHOST)
guard let formattedIpAdress = String(cString: hostname, encoding: .utf8) else {return nil}
if !formattedIpAdress.isEmpty {
ipAdress = formattedIpAdress
}
return ipAdress
}
enum Network: String {
case wifi = "en0"
case cellular = "pdp_ip0"
// case en1 = "en1"
// case lo = "lo0"
}
// get ipv4 or ipv6 address
extension UIDevice {
func address(family: Int32, for network: Network) -> String? {
var address: String?
// Get list of all interfaces on the local machine:
var ifaddr: UnsafeMutablePointer<ifaddrs>?
guard getifaddrs(&ifaddr) == 0, let firstAddr = ifaddr else { return nil }
// For each interface ...
for ifptr in sequence(first: firstAddr, next: { $0.pointee.ifa_next }) {
let interface = ifptr.pointee
let addrFamily = interface.ifa_addr.pointee.sa_family
if addrFamily == UInt8(family) {
// Check interface name:
let name = String(cString: interface.ifa_name)
if name == network.rawValue {
// Convert interface address to a human readable string:
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
getnameinfo(interface.ifa_addr, socklen_t(interface.ifa_addr.pointee.sa_len),
&hostname, socklen_t(hostname.count),
nil, socklen_t(0), NI_NUMERICHOST)
address = String(cString: hostname)
}
}
}
freeifaddrs(ifaddr)
return address
}
func ipv4(for network: Network) -> String? {
self.address(family: AF_INET, for: network)
}
func ipv6(for network: Network) -> String? {
self.address(family: AF_INET6, for: network)
}
// get all addresses
func getIFAddresses() -> [String] {
var addresses = [String]()
// Get list of all interfaces on the local machine:
var ifaddr : UnsafeMutablePointer<ifaddrs>?
guard getifaddrs(&ifaddr) == 0 else { return [] }
guard let firstAddr = ifaddr else { return [] }
// For each interface ...
for ptr in sequence(first: firstAddr, next: { $0.pointee.ifa_next }) {
let flags = Int32(ptr.pointee.ifa_flags)
let addr = ptr.pointee.ifa_addr.pointee
// Check for running IPv4, IPv6 interfaces. Skip the loopback interface.
if (flags & (IFF_UP|IFF_RUNNING|IFF_LOOPBACK)) == (IFF_UP|IFF_RUNNING) {
if addr.sa_family == UInt8(AF_INET) || addr.sa_family == UInt8(AF_INET6) {
// Convert interface address to a human readable string:
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
if (getnameinfo(ptr.pointee.ifa_addr, socklen_t(addr.sa_len), &hostname, socklen_t(hostname.count),
nil, socklen_t(0), NI_NUMERICHOST) == 0) {
let address = String(cString: hostname)
addresses.append(address)
}
}
}
}
freeifaddrs(ifaddr)
return addresses
}
}
How to use
// for wifi
let wifi = UIDevice.current.ipv4(for: .wifi) # ipv4
let wifi6 = UIDevice.current.ipv6(for: .wifi) # ipv6
// for cellular
let cellular = UIDevice.current.ipv4(for: .cellular) # ipv4
let cellular6 = UIDevice.current.ipv6(for: .cellular) # ipv6
For Swift on Mac - Swift 4 :
This way you can also find out the ip from the Wifi (not only Ethernet)
func getWiFiAddress() -> String? {
var address : String?
// Get list of all interfaces on the local machine:
var ifaddr : UnsafeMutablePointer<ifaddrs>?
guard getifaddrs(&ifaddr) == 0 else { return nil }
guard let firstAddr = ifaddr else { return nil }
// For each interface ...
for ifptr in sequence(first: firstAddr, next: { $0.pointee.ifa_next }) {
let interface = ifptr.pointee
// Check for IPv4 or IPv6 interface:
let addrFamily = interface.ifa_addr.pointee.sa_family
if addrFamily == UInt8(AF_INET) || addrFamily == UInt8(AF_INET6) {
// Check interface name:
let name = String(cString: interface.ifa_name)
if name == "en0" {
// Convert interface address to a human readable string:
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
getnameinfo(interface.ifa_addr, socklen_t(interface.ifa_addr.pointee.sa_len),
&hostname, socklen_t(hostname.count),
nil, socklen_t(0), NI_NUMERICHOST)
address = String(cString: hostname)
} else if name == "en1" {
// Convert interface address to a human readable string:
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
getnameinfo(interface.ifa_addr, socklen_t(interface.ifa_addr.pointee.sa_len),
&hostname, socklen_t(hostname.count),
nil, socklen_t(1), NI_NUMERICHOST)
address = String(cString: hostname)
}
}
}
freeifaddrs(ifaddr)
return address
}
Given you already have the socket's file descriptor this is a solution for Swift 5.2.
// Depending on your case get the socket's file descriptor
// the way you want
let socketFd = foo()
// Get the remote address for that file descriptor
var addr: sockaddr_storage = sockaddr_storage()
var addr_len: socklen_t = socklen_t(MemoryLayout.size(ofValue: addr))
var hostBuffer = [CChar](repeating: 0, count: Int(NI_MAXHOST))
// Make local copy to avoid: "Overlapping accesses to 'addr',
// but modification requires exclusive access; consider copying
// to a local variable"
let addrLen = addr.ss_len
withUnsafeMutablePointer(to: &addr) {
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
if getpeername(socketFd, $0, &addr_len) != 0 { return }
getnameinfo($0, socklen_t(addrLen), &hostBuffer, socklen_t(hostBuffer.count), nil, 0, NI_NUMERICHOST)
}
}
let connectedHost = String(cString: hostBuffer, encoding: .utf8)
enum Network: String {
case wifi = "en0"
case cellular = "pdp_ip0"
case hotspot = "bridge100"
}
if use hotspot get ip

Resources