why realm database property value do not change? - swift

As above pic, the index is increased one with sequence. when I delete one of index (except for 0), like 3, I hope all index will still in the sequence through 0~5, which means 1 & 2 keep unchanged, 4 decrease to 3, 5->4, 6->5. below is my code:
let defaultRealm = try! Realm()
let currentRealm = self.defaultRealm.objects(CurrentRealmObject.self)
let remainedItems = currentRealm.filter("index > \(indexPath.row)")
for item in remainedItems {
var realmIndex = item.index
print("before \(realmIndex)")
try! self.defaultRealm.write {
realmIndex -= 1
print("update \(realmIndex)")
}
}
After I deleted index 3, the realm database become following:
and print in console is:
before 6
update 5
before 4
update 3
before 5
update 4
see? values are actually updated, but realm database remains the index unchanged, and its sequence become confused(I'd also like to know why --! and how to keep them in the same sequence).
Thanks for your help!

var realmIndex = item.index means that item.index was copied to realmIndex. No matter how many copied values ​​you change, it does not affect the original object. To update the value of Realm, assign it again or directly manipulate the property rather than the copied value.
Assign it again
try! self.defaultRealm.write {
realmIndex -= 1
item.index = realmIndex
}
directly manipulate the property
try! self.defaultRealm.write {
item.index -= 1
}
For the sequence, As same as other databases, Realm does not keep order. If you would like to get results in sequence, you need to explicitly sort using the sorted() method. Or, use Realm's List<T> instead. List<T> keeps the orders.

Related

Updating CoreData value with UIStepper effect per cell

Hopefully someone can help me... I am passing an item from the web into coredata, I am passing the values with a click of a button.. as soon as the button gets pressed then number of items and cost are immediately passed on to Coredata attributes. e.i price: 30.00, itemQuantity 1 (all well and good here per cell)
these items are then displayed on a cellForRowAt along side with a UIStepper to change the values of the item per cell. take a look:
var itemsArray: [ItemModel] = []
let currentPrice = itemsArray[indexPath.row].price
let currentQty = itemsArray[indexPath.row].itemQuantity
let totalQty = currentQty + Int16(sender.value)
let totalPrice = Double(currentPrice) * sender.value
itemsArray[indexPath.row].setValue(totalPrice, forKey: "price")
itemsArray[indexPath.row].setValue(totalQty, forKey: "itemQuantity")
let context = CoreDataManager.shared.persistentContainer.viewContext
do {
try context.save()
} catch {
print("Failed to save main context: \(error)")
}
Theoretically this would work just find.. and it does! if I don't use Coredata and just plain UI...
But since I am using CoreData... here is what's actually happening (I bet you can already tell): I am re-using the itemsArray which initial value per item is already 30.00, so when I multiply the price with a the new sender.value now equals to 2 i'll get 60.00 which is great but NOW the price in coredata is 60 and new itemQuantity is 2 so when i click the + on the Stepper it all gets multiplied now three times the updated value, so it all gets messed up... because Im updating the values with the same previously-updated-values.. so.. yea.. I'm stuck : / How can I make this right?

How to fetch objects from results one by one without do .count

I need to fetch from Realm Results 20 objects or less. A database can be heavy, so Results.count is a long time for calling.
So, what I need is to fetch objects from Results one by one until I get 20 or until last object.
But, when I'm trying to fetch index after the last object it's throwing Realm exception 'Index x is out of bounds (must be less than x)'.
So, this one isn't working:
let searchResult = Ticket().get(filter: "base == nil && deleted == 0 AND orderPaidAt > 0 AND (\(query))").sorted(byKeyPath: "orderPaidAt")
for i in 0..<20 {
if let ticket = searchResult[i] as? Ticket {
...
} else {
break
}
}
If I'm trying to use searchResult.count or searchResult.endIndex it increases a time a lot, especially on old devices. That's why I want to avoid it.
The results are lazily loaded, so you could loop through the results one by one, until the end, or until you hit a self-set count:
let searchResult = Ticket().get(filter: "base == nil && deleted == 0 AND orderPaidAt > 0 AND (\(query))").sorted(byKeyPath: "orderPaidAt")
var count = 0
for thisTicket in searchResult {
// do something
count += 1
if count > 20 { break }
}
This way you are only loading the values that you need, and never calling count or accessing the results out of bounds.
You can use prefix(maxLenght: Int) method to get a subCollection with specified maxLenght.
Example:
realm.objects(ObjectModel.self).prefix(20).count

Swift: Remove only the first object from the NSUserDefaults

I create the Bus Ticket Booking App. so I have Dictionary with the Array in it. In which the recent Route search result is save into the NSUserDefaults. Now I want to print that result into the one TableView.
Now in that TableView I only want to show the last 10 That's all I can do perfectly but the problem is every time the result is save in to the UserDefaults so the size of the UserDefaults is increase so I just want to remove the Defaults So every time my array element remove Repeat While loop is call the number of the element in the UserDefaults (If I i have 30 element into it it run 20 times so like). This is what i'm doing for Dictionary.
var arrSearch = [[String:Any]]()
var searchBusVC:SearchBusViewController?
override func viewDidLoad() {
super.viewDidLoad()
if let arr = UserDefault["Search"] {
arrSearch.append(contentsOf: (arr as! [[String:Any]]))
repeat{
arrSearch.removeFirst()
}while (arrSearch.count > 10)
arrSearch.reverse()
}
self.tblSearch.reloadData()
}
So I want to remove the same from the UserDefaults Is it possible? This is my Table View Image.
If you are saving [[String: Any]] in UserDefaults means its like a normal way to remove objects from array. Once you done removing objects from array then save it to UserDefaults.
var recentRecords:[[String: Any]]? {
get {
return UserDefaults.standard.array(forKey:"recentRecord")
}
set {
var trimmed: [[String: Any]] = newValue
// Trim last 10 objects from array
if newValue.count >= 10 {
trimmed = newValue.suffix(10)
}
UserDefaults.standard.set(trimmed, forKey: "recentRecord")
}
}

Accessing Firebase Data inside unique AutoID

This is my first question and I'm still learning Swift/Xcode/Firebase, so I appreciate your patience. I've been stalking StackOverflow and have found a lot of answers to help with various things, but nothing that makes sense for the problem I've been struggling with for 2 days.
I am writing a program that will save a date picked on a previous viewcontroller and a set of user-entered floats from text fields to a Firebase database, and append each data set as a separate entry instead of overwriting the previous data. Using the first block of code below, I've got this problem solved except I can't find a way to do it without using AutoID. This leaves me with a setup like this in Firebase, but with multiple categories and "optionSelected" sections in each category:
program-name
Category 1
optionSelected
L1cggMnqFqaJf1a7UOv
Date: "21-12-2017"
Variable 1 Float: "12345"
Variable 2 Float: "26.51"
L1ciVpLq1yXm5khimQC
Date: "30-12-2017"
Variable 1 Float: "23456"
Variable 2 Float: "35.88"
Code used to save:
func newWithNewVars() {
let myDatabase = Database.database().reference().child("Category 1").child(optionSelected)
let variable1 = textField1.text
let variable2 = textField2.text
let variable1Float = (textField1.text! as NSString).floatValue
let variable2Float = (textField2.text! as NSString).floatValue
let writeArray = ["Date": textPassedOverDate, "Variable 1 Float": variable1Float, "Variable 2 Float": variable2Float]
myDatabase.childByAutoId().setValue(gasArray) {
(error, reference) in
if error != nil {
print(error!)
}
else {
print("Message saved successfully!")
}
}
}
The problem comes with recalling data. Since the AutoID is unique, I can't figure out how to access the data deeper inside for calculations. Specifically, I want to be able to make a new entry, press the save data button, and have it find the most recent entry in the "optionSelected" section so it can do calculations like subtract the older variable 1 from the new variable 1 and such.
Given the above description, layout, and code used above, what code structure would allow me to find the most recent date and access the data inside the AutoID sections for a specific category and "optionSelected"?
Thank you for your help.
The issue you're having is that you're trying to dig deeper but can't as you don't have a hold of that id. You'll want to use the .childAdded in your reference observation when you want to get inside of a list in your JSON tree when you don't have a hold of that id to get inside - this will be called as many times as there are values inside of Category 1 tree:
let reference = Database.database().reference()
reference.child("Category 1").child("optionSelected").observe(.childAdded, with: { (snapshot) in
let uniqueKey = snapshot.key // IF YOU WANT ACCESS TO THAT UNIQUE ID
print(uniqueKey)
guard let dictionary = snapshot.value as? [String: AnyObject] else { return }
let date = dictionary["date"] as? String
let variableOne = dictionary["Variable 1 Float"] as? Float
let variableOne = dictionary["Variable 2 Float"] as? Float
}, withCancel: nil)
You may also want to avoid using spaces in your database keys to avoid any problems in the near future. I'd stick with the common lowercased underscore practice e.g. "category_1" or "variable_2_float"

Swift: fatal error: Array index out of range on deleting item from a tableview

I've setup a table to pull data from a database. The user can manually delete items from the table (and thus the database) via checkbox (table.editing = true, iirc) and a delete button. This can be done one at a time, or all at a time.
Unfortunately, whenever I check everything for deletion, the app crashes with the following error:
fatal error: Array index out of range
This does not happen if I select and delete only one or any number of the table rows, as long as I don't select everything.
Here's my code for the delete button:
func deleteButtonPressed(sender: AnyObject) {
if (self.pureSteamFormView.tableCalibration.editing == true) {
if (self.pureSteamFormView.tableCalibration.indexPathsForSelectedRows!.count >= 1) {
for indexPath in self.pureSteamFormView.tableCalibration.indexPathsForSelectedRows!.sort({ $0.row < $1.row}) {
let calibTable : FormSteamPurityCalibration = self.steamPurityCalibrationTableList[indexPath.row] /* <--- ERROR HERE */
DatabaseManager.getInstance().deleteData("FormSteamPurityCalibration", "ID = \(calibTable.ID)")
self.steamPurityCalibrationTableList.removeAtIndex(indexPath.row)
}
self.pureSteamFormView?.tableCalibration.reloadData()
}
}
}
Near as I can figure, it is attempting to remove the row at an index, an index that may no longer exist (?) due to the previous row also being deleted, but I'm not sure about that.
I tried putting the following code:
self.steamPurityCalibrationTableList.removeAtIndex(indexPath.row)
In its own for-loop block, and the error promptly move there.
I also tried removing the removeAtIndex part completely, relying on the reloadData() to perhaps update the table automatically, but it doesn't work - the data is deleted from the database, but remains on the table (although moving away from that view and going back there updates the table).
Any suggestions please? Thanks.
Your problem here is that you are deleting the lowest indexes before the bigger ones. Let me explain with an example:
Image you have 4 elements in your array:
let array = ["Element1", "Element2", "Element3", "Element4"]
You are trying to remove the elements at index 1 et 3:
for index in [1, 3] {
array.removeAtIndex(index)
}
Your program will first remove element at index 1, leaving you with the following array:
["Element1", "Element3", "Element4"]
On the second pass of the loop it will try to remove the element at index 3. Which does not exist anymore because it has moved to index 2.
One solution to this is to start removing element with the greater index before, so in your code you could change
for indexPath in self.pureSteamFormView.tableCalibration.indexPathsForSelectedRows!.sort({ $0.row < $1.row}) {
to
for indexPath in self.pureSteamFormView.tableCalibration.indexPathsForSelectedRows!.sort({ $0.row > $1.row}) {
A better solution would be to filter your data array to include only the elements you whish to keep, so instead of:
for indexPath in self.pureSteamFormView.tableCalibration.indexPathsForSelectedRows!.sort({ $0.row < $1.row}) {
let calibTable : FormSteamPurityCalibration = self.steamPurityCalibrationTableList[indexPath.row]
DatabaseManager.getInstance().deleteData("FormSteamPurityCalibration", "ID = \(calibTable.ID)")
self.steamPurityCalibrationTableList.removeAtIndex(indexPath.row)
}
you could do:
self.steamPurityCalibrationTableList.filter {
if let index = self.steamPurityCalibrationTableList.indexOf ({ $0 })
{
for indexPath in self.pureSteamFormView.tableCalibration.indexPathsForSelectedRows! {
if indexPath.row == index { return false }
}
return true
}
}