How can I output this function to be a simple string? - swift

I am trying to get access to the private IP in my app and I have the following code which gets the local IP and returns it. The issue is that it is returning a [String] not String and every time I try and use it I get an error. Here is the code (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)
}
print(addresses)
return addresses
}
and here is how I am trying to use it:
self.privateIp.title = getIFAddresses()
However when I do this, I get an error:
Cannot assign a value of type "[String]" to a value of type "String".
If I try and cast it like this:
self.privateIp.title = getIFAddresses() as! String
I get the following error:
Cast from '[String]' to unrelated type 'String' always fails.

[String] is an array of string so it can contain more than one item.
If you want first element from the array you can use:
if let ipAdd = getIFAddresses().first {
self.privateIp.title = ipAdd
}
It's equivalent to getIFAddresses()[0] but more safety because if the array is empty call to getIFAddresses()[0] will crash your app.
You can call last to get the last one or you can enumerate all of the items inside the array.

Related

Swift getnameinfo unreliable results for IPv6

I have the following extension on sockaddr:
extension sockaddr {
/// Indicates if this is an IPv4 address.
var isIPv4: Bool {
return sa_family == UInt8(AF_INET)
}
/// Indicates if this is an IPv6 address.
var isIPv6: Bool {
return sa_family == UInt8(AF_INET6)
}
/// Returns the address in string notation.
var address: String? {
var result: String = ""
var me = self
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
if getnameinfo(&me, socklen_t(me.sa_len), &hostname, socklen_t(hostname.count), nil, socklen_t(0), NI_NUMERICHOST) == 0 {
result = String(cString: hostname)
}
return result
}
}
In an other part of my code I'm calling getifaddrs to get the interface addresses of the current device. The code above works fine for IPv4, but is somewhat unreliable for IPv6.
I get results like: 192.168.1.10 and fe80::e0fa:1204:100:0
When I change the line var result: String = "" to var result: String? = nil. The IPv6 addresses suddenly become fe80::, the rest is cut off.
Even weirder, when I just switch the var result and the var me = self lines like this:
extension sockaddr {
/// Indicates if this is an IPv4 address.
var isIPv4: Bool {
return sa_family == UInt8(AF_INET)
}
/// Indicates if this is an IPv6 address.
var isIPv6: Bool {
return sa_family == UInt8(AF_INET6)
}
/// Returns the address in string notation.
var address: String? {
var me = self
var result: String = ""
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
if getnameinfo(&me, socklen_t(me.sa_len), &hostname, socklen_t(hostname.count), nil, socklen_t(0), NI_NUMERICHOST) == 0 {
result = String(cString: hostname)
}
return result
}
}
Then the function will only work for IPv4 addresses. The getnameinfo will return 4 (FAIL).
This is during debugging, with no optimizations that I know of. It doesn't matter if I run it on a simulator or real device.
Could someone please explain why this is happening?
The problem is that getnameinfo expects a pointer that can either be a sockaddr_in or a sockaddr_in6. The definition of the function is a bit confusing because it expects a sockaddr pointer.
Because I'm using an extension to extract the IP address a copy is being made of the memory contents. This isn't a problem for IPv4 because the size of a sockaddr_in is the same size as a sockaddr. However for IPv6, the sockaddr_in6 is larger than the sockaddr struct and some relevant information is cut off.
The order of my commands probably determined what was stored in memory at the location directly after the sockaddr address. Sometimes it would look like a proper IPv6 address, but in reality incorrect.
I've resolved this issue by moving my extension to the network interface ifaddrs:
extension ifaddrs {
/// Returns the IP address.
var ipAddress: String? {
var buffer = [CChar](repeating: 0, count: Int(NI_MAXHOST))
let address = ifa_addr.pointee
let result = getnameinfo(ifa_addr, socklen_t(address.sa_len), &buffer, socklen_t(buffer.count), nil, socklen_t(0), NI_NUMERICHOST)
return result == 0 ? String(cString: buffer) : nil
}
}
Thank you #MartinR finding the cause of the problem!

Why does force Unwrapping give me an EXC_BREAKPOINT (SIGTRAP) Error?

My application is crashing on the declaration of my for loop for participant in event.attendees!. I'm relatively new to swift and understood that if I check that the attendees array is not nil then I'm free to force unwrap it. What have I misunderstood here?
private static func parseParticipants(event: EKEvent) -> [Attendee] {
var participants = [Attendee]()
if(event.attendees != nil && event.attendees?.count != 0) {
for participant in event.attendees! {
let participantName = parseEKParticipantName(participant)
let isRequiredParticipant = participant.participantRole == EKParticipantRole.Required
let hasAccepted = participant.participantStatus == EKParticipantStatus.Accepted
let attendee = Attendee(name: participantName, email: participant.URL.resourceSpecifier!.lowercaseString, required: isRequiredParticipant, hasAccepted: hasAccepted)
participants.append(attendee)
}
}
return participants
}
Turns out this was not about force unwrapping but was due to the EKParticipant.url property returning nil when it contained a String contained a " character.
let attendee = Attendee(name: participantName, email: participant.URL.resourceSpecifier!.lowercaseString, required: isRequiredParticipant, hasAccepted: hasAccepted)
We used this to access the participants email but any read or write operation to the url caused a crash so we took the EKParticipant.description property and parsed the email using a regular expression.
let participantEmail = participant.description.parse(pattern: emailRegex).first ?? ""
How about using optional binding?
if let attendees = event.attendees && attendees.count > 0 {
}

How to get the string representation of a nested class

In my reflection library EVReflection I have the following problem when class definitions are nested (class within a class). Below is a worked out case which can be found as a unit test here and The location in the library itself where the code needs to change is Here
I need to get the Internal Swift string representation of a nested
class for a property which is an array of that nested class.
Below you can see a unit test where I am able to get the correct type for the property company that is an other object. It will output _TtCC22EVReflection_iOS_Tests13TestIssue114b10Company114 instead of Company114
When I try the same for the friends property my goal is that it outputs something like: Swift.Array<_TtCC22EVReflection_iOS_Tests13TestIssue114b7User114>
What do I have to do to get that?
As you can see in the test I have various assignments to the value valueType. None of these assignments work. I am only able to get an Array<User114> or an Swift._EmptyArrayStorage.
As you also can see in the test is that if I set a breakpoint and do a po in the output window I am able to get the correct output. So what code will accomplish the same in my code?
class TestIssue114b: XCTestCase {
class User114: EVObject {
var company: Company114 = Company114()
var friends: [User114] = []
}
class Company114: EVObject {
var name: String = ""
var address: String?
}
func testIssueNestedObjects() {
let x = User114()
print("type 1 = \(NSStringFromClass(type(of: x.company)))") // output = type 2 = _TtCC22EVReflection_iOS_Tests13TestIssue114b10Company114
print("type 2 = \(testIssueNestedObjects(x.friends))")
}
func testIssueNestedObjects(_ theValue: Any) -> String {
var valueType = ""
let mi = Mirror(reflecting: theValue)
valueType = NSStringFromClass(type(of: (theValue as! [NSObject]).getTypeInstance() as NSObject)) // NSObject
valueType = "\(type(of: theValue))" // Array<User114>
valueType = "\(mi.subjectType)" // Array<User114>
valueType = ObjectIdentifier(mi.subjectType).debugDescription //"ObjectIdentifier(0x0000000118b4a0d8)"
valueType = (theValue as AnyObject).debugDescription // <Swift._EmptyArrayStorage 0x10d860b50>
valueType = NSStringFromClass(type(of: theValue as AnyObject)) // Swift._EmptyArrayStorage
// set breakpont en enter this in output window: (lldb) po type(of: theValue)
// Ouput will be: Swift.Array<EVReflection_iOS_Tests.TestIssue114b.User114>
return valueType
}
}
Background info:
Actually the end goal is that I have to be able to create instances of the object that I can add to the array. Since the array property is only available as a result from a Mirror command the variable will be of type Any. I do have an extension for arrays in place that will return a new array element. however I am only able to get that when the Any is casted to Array<NSObject> and because of that my extension will return an NSObject. So I would like to get a string like Swift.Array<_TtCC22EVReflection_iOS_Tests13TestIssue114b7User114> I can then get the parts between <> and then create an instance for that using NSClassFromString.
String(reflecting: type(of: theValue))
update by Edwin Vermeer:
For the required conversion to the internal string representation i now have the following function (still in draft)
public class func convertToInternalSwiftRepresentation(type: String) -> String {
if type.components(separatedBy: "<").count > 1 {
// Remove the Array or Set prefix
let prefix = type.components(separatedBy: "<") [0] + "<"
var subtype = type.substring(from: prefix.endIndex)
subtype = subtype.substring(to: subtype.characters.index(before: subtype.endIndex))
return prefix + convertToInternalSwiftRepresentation(type: subtype) + ">"
}
if type.contains(".") {
var parts = type.components(separatedBy: ".")
if parts.count == 2 {
return parts[1]
}
let c = String(repeating:"C", count: parts.count - 1)
var rv = "_Tt\(c)\(parts[0].characters.count)\(parts[0])"
parts.remove(at: 0)
for part in parts {
rv = "\(rv)\(part.characters.count)\(part)"
}
return rv
}
return type
}

Convert a Swift Array of String to a to a C string array pointer

I'm on Swift 3, and I need to interact with an C API, which accepts a NULL-terminated list of strings, for example
const char *cmd[] = {"name1", "value1", NULL};
command(cmd);
In Swift, the API was imported like
func command(_ args: UnsafeMutablePointer<UnsafePointer<Int8>?>!)
After trying hundreds of times using type casting or unsafeAddress(of:) I still cannot get this work. Even though I pass a valid pointer that passed compilation, it crashes at runtime saying invalid memory access (at strlen function). Or maybe it's something about ARC?
let array = ["name1", "value1", nil]
// ???
// args: UnsafeMutablePointer<UnsafePointer<Int8>?>
command(args)
You can proceed similarly as in How to pass an array of Swift strings to a C function taking a char ** parameter. It is a bit different because of the different
const-ness of the argument array, and because there is a terminating
nil (which must not be passed to strdup()).
This is how it should work:
let array: [String?] = ["name1", "name2", nil]
// Create [UnsafePointer<Int8>]:
var cargs = array.map { $0.flatMap { UnsafePointer<Int8>(strdup($0)) } }
// Call C function:
let result = command(&cargs)
// Free the duplicated strings:
for ptr in cargs { free(UnsafeMutablePointer(mutating: ptr)) }
This class provides a pointer that works with char** and automatically deallocates the memory, even though it's a struct (using a little trick with a mapped data with deallocator).
public struct CStringArray {
public let pointer: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>
public let count: Int
private var data: Data
public init(_ array: [String]) {
let count = array.count
// Allocate memory to hold the CStrings and a terminating nil
let pointer = UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>.allocate(capacity: count + 1)
pointer.initialize(repeating: nil, count: count + 1) // Implicit terminating nil at the end of the array
// Populate the allocated memory with pointers to CStrings
var e = 0
array.forEach {
pointer[e] = strdup($0)
e += 1
}
// This uses the deallocator available on the data structure as a solution to the fact that structs do not have `deinit`
self.data = Data(bytesNoCopy: pointer, count: MemoryLayout<UnsafeMutablePointer<CChar>>.size * count, deallocator: .custom({_,_ in
for i in 0...count - 1 {
free(pointer[i])
}
pointer.deallocate()
}))
self.pointer = pointer
self.count = array.count
}
public subscript(index: Data.Index) -> UnsafeMutablePointer<CChar>? {
get {
precondition(index >= 0 && index < count, "Index out of range")
return pointer[index]
}
}
public subscript(index: Data.Index) -> String? {
get {
precondition(index >= 0 && index < count, "Index out of range")
if let pointee = pointer[index] {
return String(cString: pointee)
}
return nil
}
}
}

Nested Swift Dictionaries

I want to initialize a dictionary with a dictionary nested inside like this:
var a = [Int:[Int:Float]]()
a[1][2] = 12
But I get an error:
(Int:[Int:Float]) does not have a member named 'subscript'
I've hacked at a variety of other approaches, all of them running into some kind of issue.
Any idea why this doesn't work?
You can create your own 2D dictionary like this:
struct Dict2D<X:Hashable,Y:Hashable,V> {
var values = [X:[Y:V]]()
subscript (x:X, y:Y)->V? {
get { return values[x]?[y] }
set {
if values[x] == nil {
values[x] = [Y:V]()
}
values[x]![y] = newValue
}
}
}
var a = Dict2D<Int,Int,Float>()
a[1,2] = 12
println(a[1,2]) // Optional(12.0)
println(a[0,2]) // nil
The point is you access the element via a[x,y] instead of a[x][y] or a[x]?[y].
It's giving you that error because your first subscript returns an optional so it may return a dictionary or nil. In the case that it returns nil the second subscript would be invalid. You can force it to unwrap the optional value by using an exlamation point.
var a = [1 : [ 2: 3.14]]
a[1]
a[1]![2]
If you aren't positive that a[1] is non-nil you may want to safely unwrap with a question mark instead.
var a = [1 : [ 2: 3.14]]
a[1]
a[1]?[2]
You can also assign using this method. (As of Beta 5)
var a = [Int:[Int:Float]]()
a[1] = [Int: Float]()
a[1]?[2] = 12.0
a[1]?[2] //12.0
Another way to do it is with an extension to the standard dictionary:
extension Dictionary {
mutating func updateValueForKey(key: Key, updater: ((previousValue: Value?) -> Value)) {
let previousValue = self[key]
self[key] = updater(previousValue: previousValue)
}
}
Example:
var a = [Int:[Int:Float]]()
a.updateValueForKey(1) { nestedDict in
var nestedDict = nestedDict ?? [Int:Float]()
nestedDict[2] = 12
return nestedDict
}