First time using button code - swift

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"

Related

Text label not updating on screen, even though it shows up on `print()`

I'm writing an application which has an NSSplitViewController as the main View-Controller. I have it linked-up so that clicking a button in the menubar will trigger an #IBAction which then calls a function in one of the sub-View-Controllers.
if let board = storyboard {
let imageController = board.instantiateController(withIdentifier: "VC_image_ID") as! VC_image
imageController.viewDidAppear() // necessary or else I'll get an "Unexpectedly found nil" later on
DispatchQueue.global().async{imageController.processImage(path)} // path variable was set earlier in the code, but not shown here
}
Inside the sub-View-Controller (named VC_image), I'm trying to change the stringValue of a label by using the following code:
public func processImage(_ path: String) {
DispatchQueue.main.async {
self.imageText.stringValue = path
print(self.imageText.stringValue)
}
}
Although the imageText.stringValue actually seems to have changed based on the fact that it prints the updated text (through the console), the text in the window never changes. Why is this happening? The reason is probably really obvious to professionals, but I'm still an amateur and can't figure it out. Thanks.

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.

NSTextField: Converting String to Int aborts (nil) outside of Xcode

I have a MacOS app that has been working fine for around a year. An NSTextField on a screen containing 20 NSTextFields started aborting with "unexpectedly found nil while unwrapping an Optional value". So far, I've done the following:
Removed and reassigned the textfield's IBOutlet link
Deleted and rebuilt the textfield
This fixes the app when I run in Xcode in my Development account. When I generate an archive and transfer it to my Production account, it still aborts.
I'm going to assume this is still the culprit, since I don't really get much from the core dump (because I probably don't understand what I'm looking at).
Here's my code. (game_number is an Int)
game_number = Int(gameNumberTextField.stringValue)!
When I split the code and do this:
let theNumber = (gameNumberTextField.stringValue)!
game_number = Int(theNumber)
theNumber is a String and correct, but game_number is nil
NSTextField parent class NSControl has a property called integerValue which returns an Int:
game_number = gameNumberTextField.integerValue
You should unwrap the optional value like this:
guard let theNumber = gameNumberTextField.integerValue else {
// Value not found, handle this case
return
}
Hope this helps!

How to stop ´enumerateChildNodesWithName("//*") ´ enumeration?

How do I stop this enumeration? I have the following code and Xcode complaining that I cannot assign a value to let constant. Stopping is probably a simple thing but I'm quite the noobie with Swift, so please bear with me.
self.enumerateChildNodesWithName("//*") {
spaceshipNode, stop in
if (( spaceshipNode.name?.hasSuffix("ship") ) != nil) {
for enemyNode in self.children {
if (enemyNode.name == "enemy"){
if(enemyNode.containsPoint(spaceshipNode.position)){
self.gotoGameOverScene(spaceshipNode)
stop = true // Error: Cannot assign to value: 'stop' is a 'let' constant
}
}
}
}
}
Generally you'd better show your code as text. With which we can easily copy & paste, and test it, or edit it.
In your code, the type of stop should be shown as UnsafeMutablePointer<ObjCBool> in the Quick Help pane of Xcode.
You need to modify the content of the pointer, in Swift2:
stop.memory = true
In Swift 3, the property memory is renamed to pointee:
stop.pointee = true
Apple's documentation suggests that we stop enumeration via stop.initialize(to: true).

Swift NSUserDefaults first time nil

Hi my app crashes the first time I run it. This is my code:
let State = save.stringForKey("StateSave")
let City = save.stringForKey("CitySave")
let Vehicle = save.stringForKey("ModelNumberSave")
let ExtensionPeriod = save.stringForKey("ExtensionPeriodChoosed")
let Location = "Location"
if ExtensionPeriod == nil {
let name = ""
var FieldChoosed: Void = save.setObject(name, forKey: "ExtensionPeriodChoosed")
save.synchronize()
}
save.synchronize()
var DetailNames = ["\(State!)","\(City!)","\(Location)","\(Vehicle!)","\(ExtensionPeriod!)"]
I get a nil on this line:
var DetailNames =
["(State!)","(City!)","(Location)","(Vehicle!)","(ExtensionPeriod!)"]
In fact ExtensionPeriod is nil. But I don't understand why... ExtensionPeriod is nil, but with the code I write, ExtensionPeriod will be like "" so it's not nil. Please help me.
stringForKey returns nil when there has not been a value saved already.
You need to give your values a default. The easiest way to do this is with the ?? operator, that replaces nil with another value:
let State = save.stringForKey("StateSave") ?? "-"
Some general advice: you need to stop using ! so much. Usually when something returns nil, it’s for a good reason – it might be nil. When you unwrap it with !, your program will crash (with not much helpful info as to why). Similarly, it’s usually a bad sign if you’re comparing values to nil.
Instead, take a look at this list of optional handling techniques for some alternatives.
Airspeed Velocity has a good solution for the proper way to accomplish what you want to do, but he did not really explain why what you did does not work, so I will address that aspect of this question.
if ExtensionPeriod == nil {
let name = ""
var FieldChoosed: Void = save.setObject(name, forKey: "ExtensionPeriodChoosed")
save.synchronize()
}
That block of code does not set ExtensionPeriod, thus ExtensionPeriod is still nil. All it does is set the value for the key "ExtensionPeriodChoosed" in the NSUserDefaults to no longer be nil. The local variable ExtensionPeriod, however, still has nil. ExtensionPeriod doesn't just magically point to the variable stored in NSUserDefaults, such that when you update NSUserDefaults, it automatically updates the variable. Instead, it copies the variable at the time that it is created.
Here is some sample code that demonstrates this:
NSUserDefaults.standardUserDefaults().setValue("string", forKey: "s")
NSUserDefaults.standardUserDefaults().synchronize()
var s = NSUserDefaults.standardUserDefaults().valueForKey("s")
NSUserDefaults.standardUserDefaults().setValue("string2", forKey: "s")
NSUserDefaults.standardUserDefaults().synchronize()
var y = NSUserDefaults.standardUserDefaults().valueForKey("s")
println(s)
println(y)
outputs:
"string"
"string2"
For your code to work, if you were to keep it the same structure (although you really shouldn't), you would need to change ExtensionPeriod to a var, and then in your if block, after you synchronize save, you would have to reassign ExtensionPeriod to save.valueForKey("ExtensionPeriodChoosed").
One way to make sure that your app's defaults are set is to use NSUserDefault's registerDefaults(_: [NSObject : AnyObject]) function. In Objective-C, I often put in the + (void)initialize class method, but overriding the init() of the application delegate should work just as well.