Parse a ip in swift - ios

I'm trying to parse an for a validation purpose,
like
var ip = "192.168.1.2"
So I need to pass this ip in a api call for the response , for that I need to pass ip's from "192.168.1.[1-255]" till 255, to get a response.
My question how I achieve this, need I to spilt the string by '.' and move into an array and to search with index for 4. till 255.
Or is there another method to achieve it?

//If you have a fixed local ip like "192.168.1.x"
for i in 1...255 {
let ip = "192.168.1.\(i)"
// Call the API with ip
}
//if you get local ip from other service or from refference, then
let ip = "192.168.1.10"
let ipAry = ip.components(separatedBy: ".")
let ipStr = "\(ipAry[0]).\(ipAry[1]).\(ipAry[2])"
for i in 1...255 {
let newIP = "\(ipStr).\(i)"
// Call the API with ip
}

General idea:
Extract everything from start to last '.'
Run a for loop and generate numbers from 1 to 255.
Cast the number generated in step2 to a string and append it to the result of step1.
Hit the api with the final string and check for a response.
If you get a response then break the loop and work with the response object outside the loop.
You can perform step1 with string methods or regex. Although string methods will be better, i'll give the regex example.
Step1 with regex:
^(.*?\.)\d+$
Desired string will be captured as group 1.
Demo:
https://regex101.com/r/gQKs0w/1

No need splitting and iterations
func isValidIp(ip : String) -> Bool {
let allowedIpPrefix = "192.168.1."
if ip.hasPrefix(allowedIpPrefix) {
let suffix = String(ip.characters.dropFirst(allowedIpPrefix.characters.count))
if let suffixValue = Int(suffix){
//check for "0" prefix because 192.168.1.01 is invalid ip but Int("01") will return valid 1
if (1 ... 255).contains(suffixValue) && !suffix.hasPrefix("0") {
return true
}
}
}
return false
}

Related

Data value Overflow

I am a little confused on how Data object works in swift. I am attempting to write values using bluetooth and one of the values I need to write is 500. In order to write you need to convert the value to a data object before sending. Here's the code I am using.
if(characteristic != nil){
var byteCount = 1
if(sensitivity > 255){
byteCount = 2
}
let data = Data.init(bytes: &sensitivity, count: byteCount)
peripheral.writeValue(data, for: sensCharacteristic!, type: CBCharacteristicWriteType.withResponse)
peripheral.readValue(for: sensCharacteristic!)
}else{
print("No bluetooth Connection")
}
From what I understood if I try to send a value that takes more than a byte to represent then it will overflow, thus when I send 500 the value it writes is actually 244. Because of this I tried writing and sending 2 bytes but the value I am getting is 244101. I am unsure where that value is coming from. What is the correct way to covert 500?
This function is called whenever a characteristic is read and then placed in a label. All it does is loop through the data and adds each byte to a string.
func readCharacteristic(data: Data) -> String {
var characterString = ""
for byte in data {
let c = String(byte)
characterString.append(c)
}
return characterString
}
Based on this SO post: round trip Swift number types to/from Data (pointed to by Wei Jay)...
Try this:
func readCharacteristic(data: Data) -> Int {
return data.withUnsafeBytes { $0.pointee }
}
Note: that post provides various ways to convert the Data to different data types - Int, Float, Double, etc - so this could be used if you know you'll only be reading an Int value. Otherwise, just build on this + the other post.

Swift crashes in CFRelease when leaving scope

I've just thrown myself into iOS development and I'm current getting a runtime error in the function CFRelease just at the end of the queryServer function (I put a comment on the line that get's highlighted) and I don't have an error if I comment out the function call to extractIPFromQuery.
The code below is taking the name of a server and returning a list of ip addresses to that server.
func extractIPFromQuery(query: NSArray) -> [String] {
var addresses = [String]()
for x in 0...query.count - 1{
let adr = "\(query[x])"
let adrStart = adr.startIndex.advancedBy(10)
let adrEnd = adr.startIndex.advancedBy(18)
let address = adr.substringWithRange(Range<String.Index>(start: adrStart, end: adrEnd))
var final = ""
// Convert the hex version of the address into
// a human readable version
for seg in 0...3{
let start = address.startIndex.advancedBy(seg * 2)
let end = address.startIndex.advancedBy((seg * 2) + 2)
let hexRange = Range<String.Index>(start: start, end: end)
let hexPair = address.substringWithRange(hexRange)
final += "\(UInt8(strtoul(hexPair, nil, 16)))"
if(seg != 3){
final += "."
}
}
addresses.append(final)
}
return addresses;
}
func queryServer(hostName: String) -> [String]{
var ips = [String]()
if hostName != "\0" {
let hostRef = CFHostCreateWithName(kCFAllocatorDefault, hostName).takeRetainedValue()
while(CFHostStartInfoResolution(hostRef, CFHostInfoType.Addresses, nil) == false){}
ips += extractIPFromQuery(CFHostGetAddressing(hostRef, nil)!.takeRetainedValue() as NSArray)
} // Code breaks here
return ips
}
CFHostGetAddressing does not have "Create" or "Copy" in its name,
which means that it does not return a (+1) retained object.
Therefore you have to use takeUnretainedValue() to get the
value of the unmanaged reference.
For more information about these naming conventions, see "The Create Rule" and "The Get Rule"
in Ownership Policy
in the "Memory Management Programming Guide for Core Foundation".
You are taking ownership of the array returned by CFHostGetAddressing, which means ARC will insert a release call to balance the retain call that thinks needs to be balanced, when in fact it doesn't need to do this. You should be using takeUnretainedValue() instead of takeRetainedValue() as the name semantics of CFHostGetAddressing don't imply you are required to take ownership of the result.
At a basic level, the difference between takeRetainedValue() and takeUnretainedValue() is that the former will instruct ARC to insert a release call when the variable get's out of scope, while the latter one will not. At a semantical level, the former one tells that you want, or need to take ownership of the variable, usually meaning that there's an unbalanced retain call on that object, which ARC will balance.

Turn a string into a variable

Hello I have a for in loop where elements is the variable being changed and in this case "elements" is a string but there is a corresponding variable out side of the for in loop that has the same name as the string called elements. So what I mean is out side there is a Var time = [some,text,words] and theres a for in loop that calls a STRING named "time" and I would like to know how to convert the string in the for in loop into the variable by some how taking off the "'s (not that simple I know) without specifically saying "time"(the variable) but instead converting the "elements"(which is the string 'time') string into the variable. I hope I was clear enough if I'm not making sense I'll try again.
You cannot refer to local variables dynamically by their names in Swift. This would break a lot of compiler optimizations as well as type safety if you could.
You can refer to object properties by their names if the class conforms to key-value coding. For example:
class X : NSObject {
let time = ["some", "text", "words"]
func readWordsFromProp(name: String) -> String {
guard let list = self.valueForKey(name) as? [String] else {
return ""
}
var result = ""
for word in list {
result += word
}
return result
}
}
let x = X()
print(x.readWordsFromProp("time"))
In general, there are better ways to do things in Swift using closures that don't rely on fragile name-matching. But KVC can be a very powerful tool

Returning a tuple in Swift

sorry for such a basic question but I ve only just started working with tuples
this is my code
func test() -> (authorName:String, numberOfViews:Int) {
let author : String = ""
let numberViews = 0
return(authorName : author, numberOfView : numberViews)
}
can anyone provide the correct way to do this
thanks in advance
according to the Apple's swift book:
func test() -> (authorName:String, numberOfViews:Int) {
let author : String = ""
let numberViews = 0
return(author, numberViews)
}
you define the return object at the declaration. and in the return statement just put the values.
For create a tuple simply put it in normal brackets and separate each other with comas, you also can do it on te return function
Example :
let exampleTuple = (23, "A string", 5.583)
The article from Apple :
Tuples group multiple values into a single compound value. The values within a tuple can be of any type and do not have to be of the same type as each other.In this example, (404, "Not Found") is a tuple that describes an HTTP status code. An HTTP status code is a special value returned by a web server whenever you request a web page. A status code of 404 Not Found is returned if you request a webpage that doesn’t exist.
let http404Error = (404, "Not Found")

Check if string is already a currency string?

I would like to create a function that looks at a string, and if it's a decimal string, returns it as a currency-formatted string. The function below does that, however if I pass in a string that is already formatted, it will fail of course (it expects to see a string like '25' or '25.55' but not '$15.25'
Is there a way to modify my function below to add another if condition that says "if you've already been formatted as a currency string, or your string is not in the right format, return X" (maybe X will be 0, or maybe it will be self (the same string) i'm not sure yet).
func toCurrencyStringFromDecimalString() -> String
{
var numberFormatter = NSNumberFormatter()
numberFormatter.numberStyle = NSNumberFormatterStyle.CurrencyStyle
if (self.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet()).utf16Count == 0)
{
//If whitespace is passed in, just return 0.0 as default
return numberFormatter.stringFromNumber(NSDecimalNumber(string: "0.0"))!
}
else if (IS_NOT_A_DECIMAL_OR_ALREADY_A_CURRENCY_STRING)
{
//So obviously this would go here to see if it's not a decimal (or already contains a current placeholder etc)
}
else
{
return numberFormatter.stringFromNumber(NSDecimalNumber(string: self))!
}
}
Thank you for your help!
Sounds like you need to use NSScanner.
According to the docs, the scanDecimal function of NSScanner:
Skips past excess digits in the case of overflow, so the receiver’s
position is past the entire integer representation.
Invoke this method with NULL as value to simply scan past a decimal integer representation.
I've been mostly programming in Obj-C so my Swift is rubbish, but here's my attempt at translating the appropriate code for detecting numeric strings (as also demonstrated in this answer):
let scanner: NSScanner = NSScanner(string:self)
let isNumeric = scanner.scanDecimal(nil) && scanner.atEnd
If the string is not a decimal representation, isNumeric should return false.

Resources