UITextField being treated as nil when it isn't - swift

I have a piece of code intended to allow a user to change which unit of weight they are using. If they've already entered a value it is automatically converted between kilograms and pounds. It is changed by tapping on a UISegmentedControl where segment 0 is kilogram and segment 1 is pound.
Changing the first time works perfectly regardless of if the user went from kilogram to pound or vice versa. However, upon attempting to make a second conversion the program immediately crashes claiming that the value in the text field containing weight is nil even though it clearly contains text.
Here is my code as of now:
#IBAction func unitChanged(_ sender: UISegmentedControl) {
if let text = weightInput.text, text.isEmpty {
//Does nothing since there is no weight to change the unit of
}
else {
let weight: Int? = Int(weightInput.text!)
var weightDouble: Double = Double(weight!)
if (weightTypeInput.selectedSegmentIndex == 0) {
//The user is trying to change from pounds to kilograms
weightDouble = weightDouble * 0.45359237
}
else {
//The user is trying to change from kilograms to pounds
weightDouble = weightDouble * 2.2046226218
}
weightInput.text = String(weightDouble)
}
}
Here is what the code looks like in practice
Finally here is the error message I get:
Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
If I put a default value for weightDouble it is always used for all subsequent conversion attempts. Has anyone here encountered a similar issue? I attempted to search online but I couldn't find anything to help.

Are you sure that the text value of your TextField is nil? Since you're simply checking it in the first if in my opinion that would not be the case.
Int can't parse numbers with a floating-point in them. It returns nil when you do this. You might want to parse it into a Double first and then convert it to Int.
I think your code should be as follows:
#IBAction func unitChanged(_ sender: UISegmentedControl) {
guard let text = weightInput.text, !text.isEmpty, var weightDouble = Double(text) else { return }
if (weightTypeInput.selectedSegmentIndex == 0) {
//The user is trying to change from pounds to kilograms
weightDouble = weightDouble * 0.45359237
}
else {
//The user is trying to change from kilograms to pounds
weightDouble = weightDouble * 2.2046226218
}
weightInput.text = String(Int(weightDouble))
}

Related

AVPlayer - Fatal error: Double value cannot be converted to Int because it is either infinite or NaN

I have this function:
func getSeconds()->Int{
return Int(Double(self.value) * (self.player.currentItem?.duration.seconds)!)
}
It sometimes crashes:
Fatal error: Double value cannot be converted to Int because it is either infinite or NaN
Of course I've seen this:
Swift 3:fatal error: Double value cannot be converted to Int because it is either infinite or NaN
But how EXACTLY do I have to use this?
What is this?:
guard !(totalSeconds.isNaN || totalSeconds.isInfinite) else {
return "illegal value"
}
Where do I return totalSeconds if everything is fine?
EDIT:
I'm doing it like this now:
func getSeconds()->Int{
let totalSeconds = self.player.currentItem?.duration.seconds
if(totalSeconds != nil){
guard !(totalSeconds!.isNaN || totalSeconds!.isInfinite) else {
return 0 // or do some error handling
}
return Int(Double(self.value) * (self.player.currentItem?.duration.seconds)!)
}else{
return 0
}
}
Does it make sense?
As mentioned in the answer to the question you linked to, the code is there to verify that the value is a valid number.
Your edit is in the correct direction but it could use some improvements to the handling of optionals. Something like the following might be better:
func getSeconds() -> Int {
guard let totalSeconds = self.player.currentItem?.duration.seconds,
!totalSeconds.isNaN && !totalSeconds.isInfinite else { return 0 }
return Int(Double(self.value) * totalSeconds)
}
The guard first gets the value of self.player.currentItem?.duration.seconds but only if there is a non-optional value. If self.player.currentItem is currently nil then there won't be a duration.
Once the guard gets a value in seconds, it makes sure the value is neither NaN nor infinite.
So the guard's else will be reached if there is no current item to get the duration for, or if the duration isn't a finite number.
If you have a good number, the code can move on the calculation and return a final value.
Note there is no use of force-unwraps in the code. You should avoid those to eliminate likely crashes. Learning proper optional handling in Swift is critical.
As stated in a comment to the answer you referenced, the whole !(totalSeconds.isNaN || totalSeconds.isInfinite) can be written as totalSeconds.isFinite. Doing that in your function would change the guard to be:
guard let totalSeconds = self.player.currentItem?.duration.seconds,
totalSeconds.isFinite else { return 0 }
This is a bit easier to read and more to the point of the needed check in this case.

how to override default region code in "PhoneNumberKit"?

i'm using the library "PhoneNumberKit"
to format a UITextfield i'm using on my app that i use it to input phone number.
i am using PartialFormatter to format the input into a phone number mask style.
#IBAction func onPhoneNumberChanged(_ sender: Any) {
if (phoneNumberTextField.text?.count ?? 0 < 15) {
phoneNumberTextField.text = PartialFormatter().formatPartial(phoneNumberTextField.text ?? "")
} else {
phoneNumberTextField.deleteBackward()
}
}
my problem is that it always format the string to a LOCAL phone number, i want to force it to format it into a US phone number mask.
the library say:
The default region code is automatically computed but can be overridden if needed.
but doesn't give any examples on force region code to be to a specific country
here is the library i'm using:
https://github.com/marmelroy/PhoneNumberKit
does someone here has any exmaples on how to do it ?
thanks
fixed it by doing:
phoneNumberTextField.text = PartialFormatter(phoneNumberKit: PhoneNumberKit(), defaultRegion: "US", withPrefix: true).formatPartial(phoneNumberTextField.text ?? "")
The default region code is automatically computed but can be overridden if needed like given below
class MyTextField: PhoneNumberTextField {
override var defaultRegion: String {
get {
return "US"
}
set {} // exists for backward compatibility
}
}
Reference

Swift NSTextField stringValue not displaying set value

Hello Sultans of Swift!
I am new to Swift although I have used C, C++ and C# a lot. I have come upon a situation that is really puzzling me. I'm going to post a code snippet here:
#IBAction func myFunc(sender: AnyObject)
{
let dirPicker: NSOpenPanel = NSOpenPanel()
dirPicker.allowsMultipleSelection = false
dirPicker.canChooseFiles = true
dirPicker.canChooseDirectories = false
dirPicker.runModal()
let selection = dirPicker.URL
if(selection != nil)
{
do
{
print(selection)
let mp3File = try MP3File(path: (selection?.path)!)
let title = mp3File.getTitle()
// This prints OK
print("Title:\t\(mp3File.getTitle())")
// This prints OK too
print("Title:\t\(title)")
print("Artist:\t\(mp3File.getArtist())")
print("Album:\t\(mp3File.getAlbum())")
print("Lyrics:\n\(mp3File.getLyrics())")
fileName.stringValue = (selection?.path)!
// This sets the label songTitle to an empty space and I can't see why.
// If I initialise title to:
// let title = "STRING CONSTANT"
// ...instead of
// let title = mp3File.getTitle()
// ...then it does actually set the correct text on the label songTitle.
// In both cases, printing to the console works fine! Its just setting
// the text on the label that is eluding me!
songTitle.stringValue = title
}
catch ID3EditErrors.FileDoesNotExist
{
print("Error: File Does Not Exist")
}
catch ID3EditErrors.NotAnMP3
{
print("Error: Not an MP3")
}
catch let e
{
print(e)
}
}
}
When I try to set the text in a label by setting its stringValue property to a variable it just displays empty space, yet I can actually print the variable to the console just fine. The variable is set as the return value of a function. Now if I instead set the variable explicitly to a string constant then it works. So this will be perhaps related to the uncertainty of the return value of the function, but I know it contains the text because I can print it to the console.
Can anyone spot what on earth is happening here?
Thanks
EDIT: I just fixed the comments in the code to refer to songTitle instead of fileName - sorry for the confusion. This is about setting songTitle.stringValue = title
EDIT: This is the definition of songTitle:
#IBOutlet weak var fileName: NSTextField!
#IBOutlet weak var songTitle: NSTextField!
Note that setting the stringValue property of these does actually work so long as I am not using the a variable that is assigned the return value of mp3File.getTitle(). Note also that mp3File.getTitle() does return a value and I can print it to the console OK.
When you have a String value which is printed fine:
print(title) //->I Ran
But cannot be processed well:
songTitle.stringValue = title //->`songTitle` shows nothing!!!
(Of course, you have confirmed that songTitle is fine by assigning constant string to it.)
One possible reason may be existing some control characters in the String.
You can use debugPrint to reveal such cases:
debugPrint(title) //->"\0I Ran\0"
debugPrint uses String literal-like format to show control characters.
In this case, you have NUL characters (U+0000) at both ends.
So, one quick fix is trimming them at each time you get such strings:
Swift 2:
let title = mp3File.getTitle().stringByTrimmingCharactersInSet(NSCharacterSet(charactersInString: "\0"))
Swift 3:
let title = mp3File.getTitle().trimmingCharacters(in: CharacterSet(charactersIn: "\0"))
Or you can write an extension, if you cannot touch the original source of the library:
Swift 2:
extension MP3File {
var title: String {
return self.getTitle().stringByTrimmingCharactersInSet(NSCharacterSet(charactersInString: "\0"))
}
}
Swift 3:
extension MP3File {
var title: String {
return self.getTitle().trimmingCharacters(in: CharacterSet(charactersIn: "\0"))
}
}
(Assuming MP3File does not have a property named title.)
and use it as:
let title = mp3File.title

Why in swift are variables option in a function but not in playground

I am puzzled. I need to compare product date codes. they look like 12-34-56. I wrote some code to break the parts up and compare them. this code works fin in the play ground. But when i make it a function in a view controller values come up NIL and i get a lot of "Optional("12-34-56")" values when printed to the log or viewed in a break. I tried unwrapping in many locations but nothing takes.? don't be confused by the variables date and month because they are not product codes can have 90 days and 90 months depending on the production machine used.
func compaireSerial(oldNumIn: NSString, newNumIn: String) -> Bool {
// take the parts of the number and compare the pics on at a time.
// Set up the old Num in chunks
let oldNum = NSString(string: oldNumIn)
let oldMonth = Int(oldNum.substringToIndex(2))
let oldDay = Int(oldNum.substringWithRange(NSRange(location: 3, length: 2)))
let oldYear = Int(oldNum.substringFromIndex(6))
print(oldMonth,oldDay, oldYear)
// Set up the new Num in chunks
let newNum = NSString(string: newNumIn)
let newMonth = Int(newNum.substringToIndex(2))
let newDay = Int(newNum.substringWithRange(NSRange(location: 3, length: 2)))
let newYear = Int(newNum.substringFromIndex(6))
print(newMonth, newDay, newYear)
// LETS Do the IF comparison steps.
if oldYear < newYear {
return true
} else if oldMonth < newMonth {
return true
} else if oldDay < newDay {
return true
} else {
return false
}
}
May thanks to any one. Im totally stumped
All Int() initializers with String parameters return always an optional Int.
The realtime result column in a Playground doesn't indicate the optional but printing it does.
let twentyTwo = Int("22") | 22
print(twentyTwo) | "Optional(22)\n"
I don't see how i can delete my question so ill post this to let others know it is fixed. Turns out the auction works okay but the NSUserDefaults value coming in was optional. So i was feeding the optional in. After unwrapping the NSUser value all works.

"If" statement not working with optional value

My problem is that I have some text fields that the user enters in numbers, the entered numbers then get saved to the corresponding variable.
However if the user doesn't enter a number and leaves it blank, the text field has a value of 'nil' and so would crash if unwrapped.
So I used an if statement to only unwrap if the contents of the test field are NOT nil, however this doesn't work. My program still unwraps it and crashes because the value is nil...
I don't understand how my if statement is not catching this.
On another note, how do I change my if statement to only allow Int values to be unwrapped and stored, strings or anything else would be ignored.
#IBAction func UpdateSettings() {
if CriticalRaindays.text != nil {
crit_raindays = CriticalRaindays.text.toInt()!
}
if EvapLess.text != nil {
et_raindays_lessthan_11 = EvapLess.text.toInt()!
}
if EvapMore.text != nil {
et_raindays_morethan_11 = EvapMore.text.toInt()!
}
if MaxWaterStorage.text != nil {
max_h2Ostore = MaxWaterStorage.text.toInt()!
}
if CarryForward.text != nil {
carry_forward = CarryForward.text.toInt()!
}
}
Your issue is that while the text exists, it doesn't mean toInt() will return a value.
Say the text was abc, CriticalRaindays.text != nil would be true but CriticalRaindays.text.toInt()! can still be nil, because abc cannot be converted to an Int.
The exact cause of your crash is likely that .text is equal to "", the empty string. It's not nil, but definitely not an Int either.
The better solution is to use optional binding to check the integer conversion and see if that passes, instead of merely the string existing:
if let rainDays = CriticalRaindays.text.toInt() {
crit_raindays = rainDays
}
If that doesn't compile, you possibly need to do Optional chaining:
if let rainDays = CriticalRaindays.text?.toInt()
Not on a Mac atm so can't test it for you but hope this makes sense!
Why not use an if let to unwrap on if the the text field's text is non-nil?
if let textString = self.textField.text as String! {
// do something with textString, we know it contains a value
}
In your ViewDidLoad, set CarryForward.text = "" This way it will never be nil
Edit:
To check if a textfield is empty, you can use this:
if (CarryForward.text.isEmpty) {
value = 10
}
else {
value = CarryForward.text
}