How can I Compact Code - swift

I have multiple text fields in my app and want all of them to not crash if the textfield is empty.
I have the code that fixes this but I am wondering if there is something i can write that shortcuts this code?
SO i could write
fixNilError()
and it would run the code:
if textField.text != nil {
print("Success")
}

textField.text returns an optional value, which means that the value is either something, or nil.
To handle this value, you need to "unwrap" it, and one of the cleaner way to make it would be this one:
if let fieldText = textField.text {
print(fieldText)
}
Not sure it helps you to make your code shorter, but optionals is a clever way to make it secure.

use method hasText() to check multiple textfield empty or not see below code and code accordingly.
if !text1 .hasText(){ // in this case text1 does not contain text
// deliver appropriate message to user here
return;
}else if !text2 .hasText(){
// deliver appropriate message to user here
return;
}

Related

How can I limit a text field to only intaking Double values

So essentially I'm creating a calculator type app, in which I only want to intake numbers, decimals, and the negative sign. I'm fairly new to Xcode and swift so I wouldn't consider my knowledge extensive when it comes to knowing how to handle these sorts of issues.
Here is my current attempt at a solution
let v = Double(inputOne.text!)!
(inputOne. is the associated text field)
This works well in my algorithm when everything is inputted as it should be, however when inputting anything other than a valid double I receive "Fatal error: Unexpectedly found nil while unwrapping an Optional value", and the app crashes.
I figured someone may have already come up with a creative solution to this problem or that there may be some command in Xcode that limits text fields in this way, however I am new to Xcode and unaware of such things if they exist. Maybe there is some way I can implement an error message that occurs whenever something other than a valid double is inputted without crashing the app? Any creative solutions would be appreciated.
You can use a UITextView as your input field (if you want a one-line input, use a textField). Then, simply set your ViewController as the textView's delegate -inside your ViewController add the following line (and don't forget to inherit UITextViewDelegate):
textView.delegate = self
Essentially, what this does is allow the ViewController to take ownership of some of the textView's functionalities. In this case, we are interested in filtering the user input and for that you can use the following function:
/// Intercept whatever character the user is typing on the keyboard
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
// If the user presses "enter", handle it the way you want
guard text.rangeOfCharacter(from: CharacterSet.newlines) == nil else {
// Handle new line
}
// If character is a digit, a dot, or a dash, allow input, else reject it.
if text == "-" || text.rangeOfCharacter(from: CharacterSet.decimalDigits) != nil || text == "." {
return true
} else {
return false
}
}

function doesn't return true unless I call it once before I need it

I have a function called iCloudIsOn() which checks whether a user has opted to use iCloud with the app by comparing the value of a UserDefaults key with a predefined one, and then I am using this on my initial View Controller to sync iCloud and implement pull to refresh, but only if the user is using iCloud, otherwise this code is not needed.
What's strange however is that the function only returns true if I call it right before I need to use it, even with just a print(iCloudIsOn())
The function itself looks like this:
func iCloudIsOn() -> Bool {
if UserDefaults.standard.url(forKey: "UDDocumentsPath") == iCloudPath { return true }
else { return false }
}
Then under viewDidLoad of my initial vc:
override func viewDidLoad() {
super.viewDidLoad()
if iCloudIsOn() {
// sync iCloud
}
}
This will not work however and iCloudIsOn() will return false at that time unless I add a print(iCloudIsOn()) before if iCloudIsOn() { // sync iCloud }
I tried asking iCloudIsOn to print both the value of the UserDefaults key and iCloudPath every time it is called and they are always identical: file:///private/var/mobile/Library/Mobile%20Documents/iCloud~cristian~thrive-storage/Documents/
So there isn't something in my code changing the value for one of them at some point (the value is printed before the return).
Any idea on why this is happening? Is it something to do with how UD works or am I missing something else? I find it a little strange, but I'm sure I'm just making a mistake somewhere.
Thanks in advance.
The problem ended up being that UserDefaults stored a value without a "/" the first time yet when being compared to the original URL, iCloudPath, iCloudPath had the original "/".
UserDefaults.standard.url(forKey: "UDDocumentsPath") = someURL
iCloudPath = someURL/
This would only happen on the first go around. Workaround is to cover both bases as you don't know which one you are on, especially if you are doing multiple checks in one session of the application.
if UserDefaults.standard.url(forKey: "UDDocumentsPath") == iCloudPath || UserDefaults.standard.url(forKey: "UDDocumentsPath").appendPathComponent("/") == iCloudPath
Technically, though, this is odd and unexpected behavior.

Creating a calculator app

So I'm very new to Swift and have been following this tutorial to make this app https://www.youtube.com/watch?v=NJHsdjH2HdY
This was the first problem: currentNumber = currentNumber * 10 + Float(sender.titleLabel!.text!.toInt()!)
In the comments section the guy said to change that line to:
currentNumber = currentNumber * 10 + Float(Int(sender.titleLabel!.text!)!)
I did this and I get the error: Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
As said in the comments, you should always avoid using the 'crash operator' (!) – and learn to safely deal with optional values instead.
Your program is crashing because either the titleLabel, text or Int(...) are nil – and you are then trying to force unwrap them. This could mean that your button doesn't have a titleLabel or that titleLabel's text isn't convertible to an Int.
The solution is to safely deal with the optionals as you encounter them. There are many ways of doing this, but I usually like to use one or multiple guard statements. This allows you to safely unwrap an optional if it has a value, otherwise it will execute the code within the brackets. This is useful for when future code depends on the optional not being nil. For example:
guard let buttonText = sender.titleLabel?.text else {
print("Sender didn't have either a titleLabel or text!")
return
}
guard let textAsInt = Int(buttonText) else {
print("Text wasn't convertible to an Int!")
return
}
currentNumber = currentNumber*10 + Float(textAsInt)
Now you get helpful print messages instead of crashes! Therefore you know exactly what went wrong, and what you can do to fix it (if it needs fixing).
You could also consolidate both of these checks into a single guard if you want more concise code, but less precise errors:
guard let buttonText = sender.titleLabel?.text, textAsInt = Int(buttonText) else {
print("Something went wrong when converting the button title to an Int!")
return
}
currentNumber = currentNumber*10 + Float(textAsInt)
Or you can use flatMap if you like closures:
guard let i = sender.titleLabel?.text.flatMap({Int($0)}) else {
print("Something went wrong when converting the button title to an Int!")
return
}
currentNumber = currentNumber*10 + Float(i)
The flatMap option can look a bit weird at first, but all it's doing is attempting to convert the button's titleLabel's text to an Int. If it fails it will return nil (which the guard will pick up), else it will return the numerical value of the text.
As #vacawama said in the comments, you could also use the nil coalescing operator in order to use 0 in the event that the titleLabel, text or Int(...) are nil:
currentNumber = currentNumber * 10 + Float(Int(sender.titleLabel?.text ?? "") ?? 0)
However bear in mind that this could lead to unexpected behaviour. I suspect that your program is crashing because your logic is getting run for non-numerical buttons, for example the "+" button. If this is the case, you'll be multiplying your number by 10 every time you press a non-numerical button. You'd have to first ensure that your logic only gets called on numerical buttons.
Although without seeing your full code, it's hard to say for sure.
For more info about how to safely deal with optionals, see this extensive Q&A on the subject.

Swift basic expression

I'm very new to swift, but proficient in other languages like Java, JavaScript, C, ... I'm lost with Swift syntax when it comes to create expressions. Look at this basic example where I just try to find out if one string is contained into another by calling String.rangeOfString that returns an Optional Range (Range?)
This works as expected:
let LEXEMA:String="http://"
let longUrl:String="http://badgirls.many/picture.png"
let range=longUrl.rangeOfString(LEXEMA);
if (range? != nil) {
// blah
}
Now I'm trying to combine the expression inside the if, something like:
if (longUrl.rangeOfString(LEXEMA)? !=nil) {
// blah
}
But I always get syntax errors, the above yields a "Expected Separator" and can't understand why. Done some more tests:
if (absolutePath.rangeOfString(URL_LEXEMA) !=nil) { }
Expected Separator before "!"
if absolutePath.rangeOfString(URL_LEXEMA) !=nil { }
Braced block of statements is an unused closure
What am I doing wrong?
If you’re coming from other like Java, you might be thinking of optionals like pointers/references, and so used to equating them to nil and if non-nil, using them. But this is probably going to lead to more confusion. Instead, think of them like a container for a possible result, that you need to unwrap to use. if let combines the test and unwrapping operation.
With this in mind, here’s how you could adapt your code:
let LEXEMA: String="http://"
let longUrl: String="http://badgirls.many/picture.png"
if let range = longUrl.rangeOfString(LEXEMA) {
// use range, which will be the unwrapped non-optional range
}
else {
// no such range, perhaps log an error if this shouldn’t happen
}
Note, that ? suffixing behaviour you were using changes in Swift 1.2 so even the code in your question that compiles in 1.1 won’t in 1.2.
It’s possible that sometimes you are whether there was a value returned, but you don’t actually need that value, just to know it wasn’t nil. In that case, you can compare the value to nil without the let:
if longUrl.rangeOfString(LEXEMA) != nil {
// there was a value, but you don't care what that value was
}
That said, the above is probably better expressed as:
if longUrl.hasPrefix(LEXEMA) { }
For starters:
You don't need parenthesis with if statements unless you have nested parenthetical subexpressions that require it.
You don't need to specify the type on the left side of the = of a let or var declaration if Swift can figure it out from the right side of the =. Very often Swift can figure it out, and you can tell that Swift can figure it out, so you can avoid that redundant clutter.
You do need to specify the type if Swift cannot figure out the type from
the right side. Example:
For example, consider the following lines:
let LEXEMA = "http://"
let longUrl = "http://badgirls.many/picture.png"
Swift can figure out that they're strings.
Similarly for this function or class that returns a UIView:
var myView = ViewReturningClassOrFunc()
Consider this:
#IBOutlet var myView : UIView!
In the above line, Swift cannot figure out ahead of time it will be assigned a UIView, so you have to provide the type. By providing a ! at the end you've made it an implicitly unwrapped optional. That means, like ?, you're indicating that it can be nil, but that you are confident it will never be nil at the time you access it, so Swift won't require you to put a ! after it when you reference it. That trick is a time saver and big convenience.
You should NOT add the ? to the line:
if (longUrl.rangeOfString(URL_LEXEMA) !=nil) {
As another answer pointed out, you're missing the let.
if let longUrl.rangeOfString(URL_LEXEMA) {
println("What do I win? :-)")
}
swift is case sensitive language. you need to check about whitespaces as well
if longUrl.rangeOfString(LEXEMA) != nil {
//your condition
}
there should be space between statement != nil
Just add a space between != and nil like:
if longUrl.rangeOfString(LEXEMA) != nil {
// blah
}
I tested your code in playground, an error of Expected ',' separator reported.
And do not forget the rules that 1s and 0s and Airspeed Velocity said.

First time using button code

I have this variable
var taxableTotalText = String(28)
it is populated from a second view controller, it works well except when you use the app for the first time, if the string is blank it crashes the app. I have tried
if taxableTotalText.isEmpty {
NSUserDefaults().setObject("0.00", forKey: String(28))
}
but it didn't work.
I would like to use some code like this
if TaxableAllowancesBtn "Has never been pushed" {
NSUserDefaults().setObject("0.00", forKey: String(28))
}
else {
//do nothing
}
TaxableTotal.text = NSUserDefaults().stringForKey(taxableTotalText)
but "Has never been pushed" needs to be some real swift coding.
At the moment i am just using
NSUserDefaults().setObject("0.00", forKey: String(28))
it stops my app from crashing but also forces my label to be "0.00" every time I reopen the app. A simple segue and back fixes the issue but its bugging me.
Can someone please swap my english for swift, or suggest a better way to solve this issue.
Thank you
You can use the "??" nil coalescing operator to return a default value instead of nil.
TaxableTotal.text = NSUserDefaults().stringForKey("yourKey") ?? "0.00"