Unwrapping Optional Int in Swift - ios

The API response I am working with returns a total amount. It supports multiple different currencies, so sometimes the currency symbol will be at the front (ex. $20.00) or at the end (ex. 20.00€). I am doing a check to see if the first char of the value is an int. In this specific case, the value "20.00€" is being returned. firstChar is "2" :
DOES NOT WORK:
let firstNumOpt: Int? = String(firstChar).toInt()
if let num = firstNumOpt { //20.00€
NSLog("Total: \(total)")
}
WORKS:
if let num = String(firstChar).toInt() { //20.00€
NSLog("Total: \(total)")
}
Can someone explain why the first code block does not work? Both ways seem identical to me.
Some debug info:
(lldb) po firstNumOpt
2
{
value = 2
}
(lldb) po num
411432864

Note: This isn't really an answer, but I needed to post code to describe what was working.
I'm not sure that your error is in the code that you've posted. Here is some code that I just ran in an empty playground:
func test(str: String) {
let firstNumOpt: Int? = String(str[str.startIndex]).toInt()
if let anum = firstNumOpt {
print("First worked on \(str)")
}
if let bnum = String(str[str.startIndex]).toInt() {
print("Second worked on \(str)")
}
}
test("20.00€") // prints "First worked on 20.00€" and "Second worked on 20.00€"
test("$20.00") // doesn't print anything

Related

How can I get String from Any in swift3

Such as I get a dic from fetchData:
(lldb) po dic
▿ 3 elements
▿ 0 : 2 elements
- .0 : "total"
- .1 : 0.00
▿ 1 : 2 elements
- .0 : "year"
- .1 : 2016
▿ 2 : 2 elements
- .0 : "month"
- .1 : 12
(lldb) po dic["year"]
▿ Optional<Any>
(lldb) po dic["year"]!
2016
Is there a function to get String form Any?
The function's usage is like below:
let total = UtilSwift.getStrFromAny(dic["total"] as Any )
In objective-c, I written a method:
+ (NSString*)getStringWithoutNull:(id)value
{
NSString *strValue = #"";
if(value != nil){
strValue = [NSString stringWithFormat:#"%#", value];
}
if([strValue isEqualToString:#"null"])
{
return #"";
}
if ([strValue isEqual:[NSNull null]]) {
return #"";
}
return strValue;
}
Is in swift could write a method like this to get String form Any?
The Any maybe Int, String, "", Double, or other type.
EDIT - 1
After the tried in Playground. :
import UIKit
let dict:[String:Any] = ["aa": 123, "year":1994, "month":"12"]
let string = String(describing:dict["year"]) ?? "" // Try to turn result into a String, o
print(string) // If I print(string!), and there will report error.
The warning:
EDIT 2
I know the edit -2 maybe paint the lily, but if when use the func below, when I deliver a Opitional value to the func, the return String will be Opitinal too, how to avoid that?
This below is my test in Playground, dic["b"] as Any convert the parameter to Opitional:
let dic:[String:Any] = [ // ["b": 12, "A": "A"]
"A":"A",
"b":12
]
func stringFromAny(_ value:Any?) -> String {
if let nonNil = value, !(nonNil is NSNull) {
return String(describing: nonNil) // "Optional(12)"
}
return ""
}
let str = stringFromAny(dic["b"] as Any) // "Optional(12)"
Try this one:
func stringFromAny(_ value:Any?) -> String {
if let nonNil = value, !(nonNil is NSNull) {
return String(describing: nonNil)
}
return ""
}
Update:
If the calling code invokes the above function with an Any? parameter that is explicitly cast to Any (a strange scenario which the Swift 3 compiler allows), then it will consider the final type of the parameter to be a non-optional optional, i.e. an Any value where the type Any represents is Any?. Or, in other terms, the value would be considered to be Optional<Any>.some(value:Any?).
In this case, the if let to unwrap the "some" case returns an optional value as the result in the function implementation. Which means that the final string description will include the "Optional" designation.
Because of the various oddities around the fact that the Swift 3 compiler will happily cast between Any and Any? and consider a value of type Any to be a value of type Any? and vice versa, it's actually pretty complicated to detect if an Any really contains an `Any?' or not, and to unwrap accordingly.
A version of this same function, along with some necessary additional extensions is provided below. This version will recursively flatten an Any value containing any number of nested Any? cases inside to retrieve the innermost non-optional value.
While this is what you seem to be looking for, I am of the opinion that it's a lot of hassle to work around something a programmer should not be doing anyway, namely miscasting a known Any? value to be Any because the compiler has a weird exception for that, even when it is not actually true.
Here's the "developer-proof" version of the code:
protocol OptionalType {
var unsafelyUnwrapped: Any { get }
var unsafelyFlattened: Any { get }
}
extension Optional: OptionalType {
var unsafelyUnwrapped: Any { return self.unsafelyUnwrapped }
var unsafelyFlattened: Any { return (self.unsafelyUnwrapped as? OptionalType)?.unsafelyFlattened ?? self.unsafelyUnwrapped }
}
func stringFromAny(_ value:Any?) -> String {
switch value {
case .some(let wrapped):
if let notNil = wrapped as? OptionalType, !(notNil.unsafelyFlattened is NSNull) {
return String(describing: notNil.unsafelyFlattened)
} else if !(wrapped is OptionalType) {
return String(describing: wrapped)
}
return ""
case .none :
return ""
}
}
Use ! if the value is an optional
String(describing: nonNil) // "Optional(12)"
String(describing: nonNil!) // "12"

NSNetService dictionaryFromTXTRecord fails an assertion on invalid input

The input to dictionary(fromTXTRecord:) comes from the network, potentially from outside the app, or even the device. However, Apple's docs say:
... Fails an assertion if txtData cannot be represented as an NSDictionary object.
Failing an assertion leaves the programmer (me) with no way of handling the error, which seems illogic for a method that processes external data.
If I run this in Terminal on a Mac:
dns-sd -R 'My Service Name' _myservice._tcp local 4567 asdf asdf
my app, running in an iPhone, crashes.
dictionary(fromTXTRecord:) expects the TXT record data (asdf asdf) to be in key=val form. If, like above, a word doesn't contain any = the method won't be able to parse it and fail the assertion.
I see no way of solving this problem other than not using that method at all and implementing my own parsing, which feels wrong.
Am I missing something?
Here's a solution in Swift 4.2, assuming the TXT record has only strings:
/// Decode the TXT record as a string dictionary, or [:] if the data is malformed
public func dictionary(fromTXTRecord txtData: Data) -> [String: String] {
var result = [String: String]()
var data = txtData
while !data.isEmpty {
// The first byte of each record is its length, so prefix that much data
let recordLength = Int(data.removeFirst())
guard data.count >= recordLength else { return [:] }
let recordData = data[..<(data.startIndex + recordLength)]
data = data.dropFirst(recordLength)
guard let record = String(bytes: recordData, encoding: .utf8) else { return [:] }
// The format of the entry is "key=value"
// (According to the reference implementation, = is optional if there is no value,
// and any equals signs after the first are part of the value.)
// `ommittingEmptySubsequences` is necessary otherwise an empty string will crash the next line
let keyValue = record.split(separator: "=", maxSplits: 1, omittingEmptySubsequences: false)
let key = String(keyValue[0])
// If there's no value, make the value the empty string
switch keyValue.count {
case 1:
result[key] = ""
case 2:
result[key] = String(keyValue[1])
default:
fatalError()
}
}
return result
}
I'm still hoping there's something I'm missing here, but in the mean time, I ended up checking the data for correctness and only then calling Apple's own method.
Here's my workaround:
func dictionaryFromTXTRecordData(data: NSData) -> [String:NSData] {
let buffer = UnsafeBufferPointer<UInt8>(start: UnsafePointer(data.bytes), count: data.length)
var pos = 0
while pos < buffer.count {
let len = Int(buffer[pos])
if len > (buffer.count - pos + 1) {
return [:]
}
let subdata = data.subdataWithRange(NSRange(location: pos + 1, length: len))
guard let substring = String(data: subdata, encoding: NSUTF8StringEncoding) else {
return [:]
}
if !substring.containsString("=") {
return [:]
}
pos = pos + len + 1
}
return NSNetService.dictionaryFromTXTRecordData(data)
}
I'm using Swift 2 here. All contributions are welcome. Swift 3 versions, Objective-C versions, improvements, corrections.
I just ran into this one using Swift 3. In my case the problem only occurred when I used NetService.dictionary(fromTXTRecord:) but did not occur when I switched to Objective-C and called NSNetService dictionaryFromTXTRecord:. When the Objective-C call encounters an entry without an equal sign it creates a key containing the data and shoves it into the dictionary with an NSNull value. From what I can tell the Swift version then enumerates that dictionary and throws a fit when it sees the NSNull. My solution was to add an Objective-C file and a utility function that calls dictionaryFromTXTRecord: and cleans up the results before handing them back to my Swift code.

NSObject does not have a member named startIndex

#IBAction func logEmButn(sender: AnyObject) {
let clonCountrLableItem = clonCountLable.text!
let baclCounterLableItem = baclCountLable.text!
let ironCountLableItem = ironCountLable.text!
let gymCountLableItem = gymCountLable.text!
let asp2CountLableItem = asp2CountLable.text!
let fiveHtpLableItem = fiveHtpLable.text!
let selectedLables = [clonCountLable, baclCountLable, ironCountLable, gymCountLable, asp2CountLable, fiveHtpLable]
for selectedLable in selectedLables {
if selectedLable != "0 s" {
println("You took \(selectedLable[selectedLable.startIndex])")
was working earlier today in a "similar" project, now it gives error
'NSObject' does not have a member named 'startIndex'
so, I tried:
println("You took \(selectedLable[selectedLable.description]) \(keyOfselectedLables)")
which gives a similar error, but ends with ... named 'subscript'
also have tried:
let lable = "rick"
for index in indices(lable) {
print("\(lable[index])")
}
which give error at runtime of : Could not cast value of type 'UILable' (0x199943e30) to 'NSString' (0x198fb2768).
See the below code, selectedLables is an array of UILabel's
let selectedLables = [clonCountLable, baclCountLable, ironCountLable, gymCountLable, asp2CountLable, fiveHtpLable]
but in the if statement you are equating selectedLable, of type UILabel, with a string constant.
for selectedLable in selectedLables {
if selectedLable != "0 s"
Since selectedLable is not a string type, it does not have a startIndex property or index.
So i think the array should be
let selectedLables = [baclCounterLableItem, ironCountLableItem, gymCountLableItem, asp2CountLableItem, fiveHtpLableItem]
Nitheesh George nailed it. (the last answer I see here.) Sorry if I messed up the flow here, total nube.

Converting Character in an array to an Integer

I can't seem to figure out how to do this even though I've searched through documentation.
I'm trying to figure out how to convert a character at an index in an array to an integer.
For example, say I have a character array named "container", I can't figure out how to do:
var number:Integer = container[3]
Thanks for the help!
Swift doesn't make it easy to convert between primitive and typed representations of things. Here's an extension that should help in the meantime:
extension Character {
func utf8Value() -> UInt8 {
for s in String(self).utf8 {
return s
}
return 0
}
func utf16Value() -> UInt16 {
for s in String(self).utf16 {
return s
}
return 0
}
func unicodeValue() -> UInt32 {
for s in String(self).unicodeScalars {
return s.value
}
return 0
}
}
This allows you to get pretty close to what you want:
let container : Array<Character> = [ "a", "b", "c", "d" ]
/// can't call anything here, subscripting's also broken
let number = container[2]
number.unicodeValue() /// Prints "100"
For any engineers that come across this question, see rdar://17494834
I am not sure that it is effective or not but at least it worked. I converted Character to String then to Int.
String(yourCharacterInArray).toInt()
You may try this:
var container = "$0123456789"
var number:Int = Array(container.utf8).map { Int($0) }[3]
It's totally ugly, but it does the job. Also it is a bit computational expensive (O(n) each time one access a character in a string). Still this can be a trick to get back a way to build the CStrings:
typealias CString = Array<CChar>
func toCString(string: String) -> CString {
return Array(string.utf8).map { CChar($0) } + [0]
}
var cString = toCString("$ 0123456789")
println("The 2nd character in cString has value \(cString[1])") // It outputs 32
or without implementing a function:
var container = "$ 0123456789"
var containerAsCString = Array(container.utf8).map { CChar($0) } + [0]
println("The 2nd character in container has value \(containerAsCString[1])") // It outputs 32
Why not just for loop the array and convert everything to Int?
https://developer.apple.com/Library/mac/documentation/General/Reference/SwiftStandardLibraryReference/index.html
Why not just convert the character to String, get the unicodeScalars for it and extract the .value on the scalar?
something like:
var chr: [Character] = ["C", "B", "A"]
for a in String(chr[1]).unicodeScalars {
println(a.value)}
For me worked something like:
"\(container[3])".toInt()

Detect a Null value in NSDictionary

I have an NSDictionary that's populated from a JSON response from an API server. Sometimes the values for a key in this dictionary are Null
I am trying to take the given value and drop it into the detail text of a table cell for display.
The problem is that when I try to coerce the value into an NSString I get a crash, which I think is because I'm trying to coerce Null into a string.
What's the right way to do this?
What I want to do is something like this:
cell.detailTextLabel.text = sensor.objectForKey( "latestValue" ) as NSString
Here's an example of the Dictionary:
Printing description of sensor:
{
"created_at" = "2012-10-10T22:19:50.501-07:00";
desc = "<null>";
id = 2;
"latest_value" = "<null>";
name = "AC Vent Temp";
"sensor_type" = temp;
slug = "ac-vent-temp";
"updated_at" = "2013-11-17T15:34:27.495-07:00";
}
If I just need to wrap all of this in a conditional, that's fine. I just haven't been able to figure out what that conditional is. Back in the Objective-C world I would compare against [NSNull null] but that doesn't seem to be working in Swift.
You can use the as? operator, which returns an optional value (nil if the downcast fails)
if let latestValue = sensor["latestValue"] as? String {
cell.detailTextLabel.text = latestValue
}
I tested this example in a swift application
let x: AnyObject = NSNull()
if let y = x as? String {
println("I should never be printed: \(y)")
} else {
println("Yay")
}
and it correctly prints "Yay", whereas
let x: AnyObject = "hello!"
if let y = x as? String {
println(y)
} else {
println("I should never be printed")
}
prints "hello!" as expected.
You could also use is to check for the presence of a null:
if sensor["latestValue"] is NSNull {
// do something with null JSON value here
}
I'm using this combination and it also checks if object is not "null".
func isNotNull(object: AnyObject?) -> Bool {
guard let object = object else { return false }
return isNotNSNull(object) && isNotStringNull(object)
}
func isNotNSNull(object: AnyObject) -> Bool {
object.classForCoder != NSNull.classForCoder()
}
func isNotStringNull(object: AnyObject) -> Bool {
guard let object = object as? String where object.uppercaseString == "NULL" else {
return true
}
return false
}
It's not that pretty as extension but work as charm :)
NSNull is a class like any other. Thus you can use is or as to test an AnyObject reference against it.
Thus, here in one of my apps I have an NSArray where every entry is either a Card or NSNull (because you can't put nil in an NSArray). I fetch the NSArray as an Array and cycle through it, switching on which kind of object I get:
for card:AnyObject in arr {
switch card { // how to test for different possible types
case let card as NSNull:
// do one thing
case let card as Card:
// do a different thing
default:
fatalError("unexpected object in card array") // should never happen!
}
}
That is not identical to your scenario, but it is from a working app converted to Swift, and illustrates the full general technique.
my solution for now:
func isNull(someObject: AnyObject?) -> Bool {
guard let someObject = someObject else {
return true
}
return (someObject is NSNull)
}
tests look good so far...
I had a very similar problem and solved it with casting to the correct type of the original NSDictionary value. If your service returns a mixed type JSON object like this
{"id":2, "name":"AC Vent Temp", ...}
you'll have to fetch it's values like that.
var id:int = sensor.valueForKey("id") as Int;
var name:String? = sensor.valueForKey("name") as String;
This did solve my problem. See BAD_INSTRUCTION within swift closure

Resources