Adding up entered value with an existing value in swift - ios

I have a text field where I can enter a double and it will be displayed on a label in the 2nd view controller. This value will be saved using UserDefaults. I am struggling to find what to do, to be able to then use this saved value and increase it with a new value entered in the text field.
i.e 1st time I enter 5; label displays 5. 2nd time I enter 3; label displays 8.
I tried to use the below if function, but this has not worked. When I enter a value for the 2nd time the label value goes back to 0, if then enter a value again, label is updated with the value entered.
func saveOne() {
UserDefaults.standard.set(weekOneTotal, forKey: "WEEKONE")
secondScreen.weekOneText = String(UserDefaults().double(forKey: "WEEKONE"))
}
func addCorrectSpend () {
guard let addAmount = convertAmount(input: enterField.text!) else {
print("Invalid amount")
return
}
if UserDefaults().double(forKey: "WEEKONE") == 0 {
weekOneTotal += addAmount
secondScreen.weekOneText = String(UserDefaults().double(forKey: "WEEKONE"))
saveOne()
}
else if UserDefaults().double(forKey: "WEEKONE") > 0 {
let defaultOne = UserDefaults.standard
defaultOne.set(defaultOne.double(forKey: "WEEKONE")+addAmount, forKey: "WEEKONE")
secondScreen.weekOneText = String(UserDefaults().double(forKey: "WEEKONE"))
saveOne()
}
}

To answer (quickly) why this is happening: You are setting the initial value in UserDetails.standard, which is correct, but then you are updating the value in a new UserDefaults() object each time.
You can also pare down your code a little bit as there is some unnecessary stuff in there. Ultimately you just need to add the new value to the existing value, so you don't really need to check if the existing value == 0. Here is an example of how I may refactor the above code:
func addCorrectSpend() {
guard let addAmount = convertAmount(input: enterField.text!) else {
print("Invalid amount")
return
}
//Get the existing total (will be 0 if none)
let weekOneAmount = UserDefaults.standard.double(forKey: "WEEKONE")
//Add the existing total to the new amount from your textField
let weekOneTotal = weekOneAmount + addAmount
//Update UserDefaults.standard to the new value
UserDefaults.standard.set(weekOneTotal, forKey: "WEEKONE")
//Set the text
secondScreen.weekOneText = "\(weekOneTotal)"
}

A different approach would be to utilize custom getter & setter for weekOneAmount, so that you can abstract away most of your calls and work with it as a normal variable.
var weekOneAmount: Double {
get {
return UserDefaults.standard.double(forKey: "WEEKONE")
}
set {
UserDefaults.standard.set(newValue, forKey: "WEEKONE")
}
}
Now, whenever you need to read or write, it behaves just like any other variable.

Related

Loop in different type of array to find matched one

I'm trying to add a favorite button to my application using CoreData.
I have tableView and added favorite button inside of it to save the label when I press that specific row. I saved it successfully. But I want to populate the CoreData just once for that specific row.
var checkFav = [Fav]()
I created an array with Type Fav which is name of my class for CoreData to populate items I have to check they appear just once.
let result = try context.fetch(Fav.fetchRequest())
checkFav = result as! [Fav]
if checkFav.isEmpty{
let fav = Fav(context: context)
fav.name = name
appDelegate.saveContext()
}
Above you see i populated the array.
do{
let result = try context.fetch(Fav.fetchRequest())
checkFav = result as! [Fav]
if checkFav.isEmpty{
let fav = Fav(context: context)
fav.name = name
appDelegate.saveContext()
}
else{
let checkName = array[rowSelected]
for value in checkFav{
if value.name == checkName{
print("You already have this name ")
}
else {
let fav = Fav(context: context)
fav.name = name
appDelegate.saveContext()
}
}
}
} catch{
print("Error")
}
Let's says I have two name "john","martha" in CoreData if I press to button of "martha" it shouldn't add again. But because of my loop when it sees "john" in the array it thinks this name isn't matching so it's saving "martha" (same name) to CoreData.
How can I check my checkFav array which contains the upcoming name if it contains don't save it again. But if not, add to CoreData. I'm really confused.
Your way to check for an empty entity cannot work if you want to save multiple records.
Add a predicate to the fetch request and a result limit of 1 to check for the specific name.
This method takes the name as parameter and adds a record if the name does not exist. It returns true if an entry is added otherwise false. Further it passes a potential error.
func addFavorite(for name : String) throws -> Bool {
let request : NSFetchRequest<Fav> = Fav.fetchRequest()
request.predicate = NSPredicate(format: "name = %#", name)
request.resultsLimit = 1
if let _ = try context.fetch(request).first {
return false // record exists
} else {
let fav = Fav(context: context)
fav.name = name
appDelegate.saveContext()
return true // record added
}
}

getting win - loss counter to save in user default swift

I am comparing whether a user should get a win or loss in their score column. I can't get the code to store the win/loss and then add 1 on top of it every time the if statement runs. Here is my if statement, followed by where i call it in the view did load. Cant seem to figure where I'm going wrong. thanks for the help!
viewDidLoad {
let dailyWinsDefault = UserDefaults.standard.integer(forKey: "dailyWinsDefault")
winsLabel.text = "\(dailyWinsDefault)"
print(dailyWinsDefault)
let dailyLossDefault = UserDefaults.standard.integer(forKey: "dailyLossDefault")
lossLabel.text = "\(dailyLossDefault)"
print(dailyLossDefault)
}
showWinLossVC.callbackForWinLoss = { result in
if result > 0.0 {
self.dailyWins += 1
UserDefaults.standard.set(self.dailyWins, forKey: "dailyWinsDefault")
self.winsLabel.text = String(self.dailyWins)
print(self.dailyWins)
}
else if result < 0.0 {
self.dailyLosses += 1
UserDefaults.standard.set(self.dailyLosses, forKey: "dailyLossDefault")
self.lossLabel.text = "\(self.dailyLosses)"
Couple of mistakes
Mistake 1:
Seems like you are setting integer in UserDefault for key dailyWinsDefault using statement
UserDefaults.standard.set(self.dailyWins, forKey: "dailyWinsDefault")
And you expect it to return you String when you retrieve it with
if let dailyWinsDefault = UserDefaults.standard.string(forKey: dailyWinsDefault)
Why will it not return nil ?
use
let dailyWinsDefault = UserDefaults.standard.integer(forKey: "dailyWinsDefault")
Mistake 2:
In your statement
if let dailyWinsDefault = UserDefaults.standard.string(forKey: dailyWinsDefault)
dont you think dailyWinsDefault should be in double quote as its a key and supposed to be string ?
try
let dailyWinsDefault = UserDefaults.standard.integer(forKey: "dailyWinsDefault")
EDIT:
As OP has updated code and now facing a issue with his updated code and requested for help updating my answer.
Couple of issues again
Mistake 1:
In entire ViewDidLoad method u never assigned the value retried from user defaults to property dailyWins and dailyLoses
viewDidLoad {
let dailyWinsDefault = UserDefaults.standard.integer(forKey: "dailyWinsDefault")
self.dailyWins = dailyWinsDefault //this is missing
winsLabel.text = "(dailyWinsDefault)"
print(dailyWinsDefault)
let dailyLossDefault = UserDefaults.standard.integer(forKey: "dailyLossDefault")
self.dailyLosses = dailyLossDefault //this is missing
lossLabel.text = "\(dailyLossDefault)"
print(dailyLossDefault)
}
Mistake 2:
Though not specific to your problem, you should always call super.viewDidLoad in your viewDidLoad unless u have a very firm reason for not doing so
That should help :)

Pagination in firebase with identical child values

My data structure is as follows:
users:
user1:
-carModel: evo x
-username: importguy
-region: north east
user2:
-carModel: evo x
-username: evoguy
-region: north east
user3:
-carModel: mustang gt
-username: muscleguy
-region: south east
I want the user to be able to search for a car, say evo, and display results of users who own those particular cars. I need to paginate these results for my app. The problem is I can't figure out how to properly query this. Here is what i have so far.
func fetchUsersBy(car: String) {
if self.carCurrentKey == nil {
let ref = USER_REF.queryOrdered(byChild: "carModel").queryStarting(atValue: car).queryLimited(toFirst: 3)
ref.observeSingleEvent(of: .value, with: { (snapshot) in
guard let snap = snapshot.children.allObjects as? [FIRDataSnapshot] else { return }
guard let last = snapshot.children.allObjects.last as? FIRDataSnapshot else { return }
snap.forEach({ (snapshot) in
guard let userDict = snapshot.value as? Dictionary<String, AnyObject> else { return }
guard let carModel = userDict["carModel"] as? String else { return }
if carModel.contains(car) {
print(snapshot)
}
})
self.carCurrentKey = last.key
self.carCurrentValue = last.childSnapshot(forPath: "carModel").value as? String
})
} else {
// where to start next query?
let ref = USER_REF.queryOrder(byChild: "carModel").queryStarting(atValue: self.carCurrentValue)
}
}
I have to order the query by carModel, in order to group all of the users with that particular car type together in a snapshot. Since all the car models are the same value, I cannot figure out where to start or end the next query for the pagination. Using the reference i have in the else block starts the query at the same place as the block above. Any help or advice would be much appreciated.
I considered doing a fan out, and making a separate structure for car types. This would be difficult though.
For both startAt() and endAt(), you can pass a second value, childKey as shown here.
So your query will look something like this:
let ref = USER_REF.queryOrdered(byChild: "carModel").queryStarting(atValue: self.carCurrentValue, childKey: self.carCurrentKey).queryLimited(toFirst: 3+1)
Note that I used toFirst: 3+1. That's because, annoyingly, startAt() is inclusive and there's no way to skip the first record. So, since we started from the last record retrieved on the previous page, you will want to query for one extra record and discard the first result.
Here's a more complete example in JavaScript. Not familiar enough to translate this to Swift, but it should give you the algorithm in completion.
class Cursor {
constructor(baseRef, pageSize) {
this.baseRef = baseRef;
this.lastKey = null;
this.lastValue = null;
this.pageSize = pageSize;
}
next() {
let ref = this.baseRef;
if( this.lastValue !== null ) {
// a previous page has been loaded so get the next one using the previous value/key
// we have to start from the current cursor so add one to page size
ref = ref.startAt(this.lastValue, this.lastKey).limitToFirst(this.pageSize+1);
}
else {
// this is the first page
ref = ref.limitToFirst(this.pageSize);
}
return ref.once('value').then(snap => {
const keys = [];
const data = []; // store data in array so it's ordered
snap.forEach(ss => {
data.push(ss.val());
keys.push(ss.key);
});
if( this.lastValue !== null ) {
// skip the first value, which is actually the cursor
keys.shift();
data.shift();
}
// store the last loaded record
if( data.length ) {
const last = data.length - 1;
this.lastKey = keys[last];
this.lastValue = data[last].author;
}
return data;
});
}
}
And here's a working fiddle.
Keep in mind that this is a realtime data stream. So pagination is tricky. It's generally easier to just do infinite scroll than to try and maintain a realistic cursor on a moving data set (records can reorder when data changes, get deleted, added in the middle, etc).

iOS Xcode Swift Combining Input FirstName and LastName into a FullName Issue

I am having trouble combining a first and last name for this code I am running to update it to a tableviewcell. It seems as if it is not saving either first or last name and merely replacing it as I type new text in. I am not sure what is going on and I am certain the row numbers that I am using in my .plist match with the code. Any help with this or other alternatives are appreciated.
func textfieldTextWasChanged(newText: String, parentCell: CustomCell) {
let parentCellIndexPath = tblExpandable.indexPathForCell(parentCell)
let currentFullname = cellDescriptors[0][12]["primaryTitle"] as! String
let fullnameParts = currentFullname.componentsSeparatedByString(" ")
var newFullname = ""
if parentCellIndexPath?.row == 13 {
if fullnameParts.count == 2 {
newFullname = "\(newText) \(fullnameParts[1])"
}
else {
newFullname = newText
}
}
else {
newFullname = "\(fullnameParts[0]) \(newText)"
}
cellDescriptors[0][12].setValue(newFullname, forKey: "primaryTitle")
tblExpandable.reloadData()
}
What most likely is happening is that when you're calling tblExpandable.reloadData(), cellForRowAtIndexPath is getting called, and it's overwriting your changes that you're making here. You either need to do some cell caching or possibly consider using a UIScrollView instead of a UITableView.

how to make retrieve button for previous Value

can someone help me
i make button to retrieve previous Values
but i have two TextFiled
i must write in two TextFiled for work retrieve button
i do not want this happens
i want when I write in the first TextFiled, retrieve button work without any problem
but if i write in first TextFiled and second TextFiled at the same time i want retrieve button work without any problem
var previousValues: [String] = [String]();
var previousValues1: [String] = [String]();
previousValues.append(TextFailde.text ?? "error");
previousValues1.append(TextFilde1.text ?? "error");
if self.previousValues.count > 0 {
let previousValue = self.previousValues.removeLast()
let subtracted = (Int(self.lblZeroUs.text!)!) - (Int(previousValue)!)
self.lblZeroUs.text = "\(subtracted)"
}
else if self.previousValues1.count > 0 {
let previousValue = self.previousValues1.removeLast()
let subtracted2 = (Int(self.lblZeroThey.text!)!) - (Int(previousValue)!)
self.lblZeroThey.text = "\(subtracted2)"
}
and here the error
There are many errors, first of all you dont declare your properties with the first letter in uppercase, it's considered a bad practice.
Then, when you involve your properties in mathematical operations what do you need is to assign them a start value, especially if your code try to convert strings.
In Swift, you don’t need to write semicolons at the end of every statement.
I dont know the rest of your code, but your lines crash because you attempt to run mathematical operations using properties have nil as value.
This below it's just an example to avoid the first crashing for nil:
textFailde.text = "0"
textFilde1.text = "0"
previousValues.append(textFailde.text ?? String(0))
previousValues1.append(textFilde1.text ?? String(0))
self.lblZeroUs.text = String(0)
self.lblZeroThey.text = String(0)
if self.previousValues.count > 0 {
let subtracted = (Int(self.lblZeroUs.text!)!) - (Int(self.previousValues.last!))!
self.previousValues.removeLast()
self.lblZeroUs.text = "\(subtracted)"
}
else if self.previousValues1.count > 0 {
let subtracted2 = (Int(self.lblZeroThey.text!)!) - (Int(self.previousValues1.last!))!
self.previousValues1.removeLast()
self.lblZeroThey.text = "\(subtracted2)"
}
previousValues.append(TextFailde.text ?? "error");
previousValues.append(TextFilde1.text ?? "error");
You added to the same array twice, try changing the code to
previousValues.append(TextFailde.text ?? "error");
previousValues1.append(TextFilde1.text ?? "error");

Resources