Using Realm to save a title from a textfield in Swift - ios

I'm not sure how to save a title from a textfield into Realm. CurrentNote and note are both from Note, a swift file with dynamic vars including title.
func saveNote() {
currentNote = Note()
note = currentNote
if let note = note {
let realm = Realm()
var index = tableView[]
var changingNote:Note = Note()
changingNote.title = index
realm.write{
realm.add(note)
self.notes = realm.objects(Note)
}
}
tableView.reloadData()
}

Could something like the following work?
func saveNote(noteIndex: Int) {
let note = notes[noteIndex]
note.realm.write {
note.title = TITLE
}
}

Related

Problem with deleting an entity from other ViewController in CoreData

people. Im learning how to work with CoreData and i have a question.
I have a "WordArrayEntity" which have oneToMany relationship with "WordEntity" and Nulify deletion rule.
So at first when my app start im fetching info from my CoreData to special array
var wordEntities:[[WordEntity]] = []
After it my tableView recieves an attribute that it needs.
As i understand i can delete entities by this method
context.delete(wordEntity)
everything works fine when i do this on my tableView.
But, when i move to editor ViewController and try to add new Entity or delete previous nothing happens.
This is my code to move to next view controller
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
if let controller = self.storyboard?.instantiateViewController(withIdentifier: "ShowWordVC") as? ShowWordVC {
controller.word = SingleTonForEntities.shared.wordEntities[indexPath.section][indexPath.row]
controller.wordIndex = indexPath.row
self.present(controller, animated: true)
}
}
This is my method to delet word, which perfectly works at tableView
func deleteWordFromVocabulary(word: WordEntity,indexPath: Int) {
var wordStartsFromLetter = false
if let firstCharacter:Character = word.englishWord?.first {
var index = 0
for array in wordEntities {
if SingleTon.shared.sectionName[index].lowercased() == firstCharacter.lowercased() {
var newArray = array
context.delete(word)
newArray.remove(at: indexPath)
wordEntities[index] = newArray
wordStartsFromLetter = true
}
index += 1
}
}
if wordStartsFromLetter == false {
var newArray = wordEntities[26]
context.delete(word)
newArray.remove(at: indexPath)
wordEntities[26] = newArray
}
saveContext()
}
When i try to save word after editing or a new one i have the following code
#IBAction func saveButtonIsPressed(_ sender: UIButton) {
keyboardDissapears()
guard let englishWord = self.englishWordTextField.text,
!englishWord.isEmpty,
let wordImage = addedImageView.image,
let belarusianWord = self.belarusianWordTextField.text,
!belarusianWord.isEmpty,
let englishDefinition = self.englishDefinitionTextView.text,
!englishDefinition.isEmpty,
let belarusianDefinition = self.belarusianDefinitionTextView.text,
!belarusianDefinition.isEmpty else {
createAndShowAlert()
return
}
if editModeIsActivated == true {
if let wordToDelete = word, let wordIndex = wordIndex {
SingleTonForEntities.shared.deleteWordFromVocabulary(word: wordToDelete, indexPath: wordIndex)
}
}
word!.englishDefinition = englishDefinition
word!.belarusianDefinition = belarusianDefinition
word!.englishWord = englishWord
word!.belarusianWord = belarusianWord
let data = wordImage.pngData()
word!.wordImage = data
SingleTonForEntities.shared.addNewWordToVocabulary(word: self.word)
self.createAndShowDoneProgressAlert()
}
at first im checking if my fields are empty. If they aren't empty and we are in edit mode i delete the "WordEntity" from our context and then from our array.
And then i try to save a new word and add it to context with this method
func addNewWordToVocabulary(word: WordEntity!) {
var wordStartsFromLetter = false
word.englishWord = word.englishWord!.trimmingCharacters(in: .whitespacesAndNewlines)
if let firstCharacter:Character = word.englishWord?.first {
var index = 0
for array in wordEntities {
if SingleTon.shared.sectionName[index].lowercased() == firstCharacter.lowercased() {
var newArray = array
newArray.append(word)
for element in wordEntitesArray {
if element.arrayName == SingleTon.shared.sectionName[index].lowercased() {
element.addToWordEntities(word)
}
}
wordEntities[index] = newArray
wordStartsFromLetter = true
}
index += 1
}
}
if wordStartsFromLetter == false {
var newArray = wordEntities[26]
newArray.append(word)
wordEntities[26] = newArray
}
saveContext()
}
And there is a question. What am i doing wrong?
When i try to add new word to vocabulary - my app crashes.
But when im in edit mode and after it adding a new word to vocabulary - it just returns an empty tableViewCell.
I am new to CoreData and working with for about a week, but i would be glad to here what am i doing wrong and what i should do in such situations.
P.S. Everything worked well with UserDefaults

how to update realm object based on the certain property in Realm?

I have products in my Realm database like this
I want to update my realm database based on productID, so I don't need to add another product over and over again. let say I want to update quantity of product that has productID = "a" to be 5.
I have tried to write something like this.
let selectedProductID = "a"
let productsInRealmDatabase = realm.objects(Product.self)
let productIndex = productsInRealmDatabase.index(where: {$0.productID == selectedProductID})
if let productIndex = productIndex {
do {
try realm.write {
var productRealm = productsInRealmDatabase[productIndex]
productRealm.quantity = 5
productsInRealmDatabase[productIndex] = productRealm // ERROR HERE
}
} catch {
// error Handling
}
}
but I got error in : productsInRealmDatabase[productIndex] = productRealm
Error Message: Cannot assign through subscript: subscript is get-only
so how to update realm object based on the certain property in Realm?
You should use Realm's own filter method which accepts an NSPredicate and returns an auto-updating Results instance rather than Swift's filter when operating on Realm collections. Than either update the properties of the fetched prouduct or create a new one and save that to Realm.
let selectedProductID = "a"
let productsInRealmDatabase = realm.objects(Product.self)
let matchingProduct = productsInRealmDatabase.filter("productID == %#", selectedProductID).first
if let matchingProduct = matchingProduct {
do {
try realm.write {
matchingProduct.quantity = 5
}
} catch {
// error Handling
}
} else {
let newProduct = Product()
newProduct.productID = selectedProductID
newProduct.quantity = 5
do {
try realm.write {
realm.add(newProduct)
}
} catch {
// error Handling
}
}
If you want your Products to be unique based on their productID property, you use also set productID as the primaryKey of your Object subclass.
class Product:Object {
#objc dynamic var productID = ""
...
override static func primaryKey() -> String? {
return "productID"
}
}
Try this -
let selectedProductID = "a"
let productsInRealmDatabase = realm.objects(Product.self)
let filteredProducts = productsInRealmDatabase.filter("productID = \(selectedProductID)")
do {
try realm.write {
filteredProducts.forEach { product in
product.quantity = 5
}
}
} catch {
// error Handling
}
While inserting data to your database inside the insert function mark update key as true and then try updating the value. eg:
static func insertData() {
//Your insertion code//
try! realm.write {
realm.add(request, update: true)
}
}
static func updateData(productId: String, quantity: Int) {
let product = self.getProductData(prodId: productId)
let realm = try! Realm()
try! realm.write {
product?.quantity = quantity
}
}
Hope this helps you out.

Get the indexPath of the relevant element in a loop

I'm working in a project in Swift 3.0 and I have a loop that would give me the state of an object (gives a boolean value in a an array sequence). My requirement is if the element state is "true" I wants to get the relevant index path of that, so using that indexPath I can get the whole object from my original array. So inside a loop how can i get that as well? My partially done code is below. In a comment I have stated where I wants to pass this indexPath.
func tapFunction(sender: UIButton) {
// This gives me the selected indexPath
print("ID :",sender.accessibilityIdentifier!)
if let rowIndexString = sender.accessibilityIdentifier, let rowIndex = Int(rowIndexString) {
self.sateOfNewSongArray[rowIndex] = !self.sateOfNewSongArray[rowIndex]
}
sender.isSelected = !sender.isSelected
for (element) in self.sateOfNewSongArray {
//This is where I wants to pass the rowIndex that became true
if element == true {
selectedSongList.addingObjects(from: [songList[rowIndex]])
}
}
}
Note: Make sure your array 'sateOfNewSongArray' has boolean types
of elements
Try following solutions:
Sample Code 1: According to your current code in your question.
func tapFunction(sender: UIButton) {
// This gives me the selected indexPath
print("ID :",sender.accessibilityIdentifier!)
if let rowIndexString = sender.accessibilityIdentifier, let rowIndex = Int(rowIndexString) {
self.sateOfNewSongArray[rowIndex] = !self.sateOfNewSongArray[rowIndex]
}
sender.isSelected = !sender.isSelected
for (index, element) in self.sateOfNewSongArray.enumerated() {
if element {
if let songName:String = songList[index] {
selectedSongList.append(songName)
}
}
}
}
Sample Code 2: More easier and concise.
func tapFunction(sender: UIButton) {
// This gives me the selected indexPath
print("ID :",sender.accessibilityIdentifier!)
if let rowIndexString = sender.accessibilityIdentifier, let rowIndex = Int(rowIndexString) {
if let songName:String = songList[rowIndex] {
selectedSongList.append(songName)
}
}
}
enumerated() is a function, returns sequence of pairs enumerating with index and element of array (by iterating elements of array)
Here is basic sample code, for your understanding. Copy and execute this code:
let songList =  [“cat”, “dog”, “elephant”]
let sateOfNewSongArray = [false, true, false]
let selectedSongList = [String]()
for (index, element) in self.sateOfNewSongArray.enumerated() {
if element {
if let animalName:String = songList[index] {
selectedSongList.append(animalName)
}
}
}
print(“selectedSongList - \(selectedSongList)”)

Firebase restoring initial values in textfields after changing text value

I am practicing iOS (Swift) with Firebase. the first viewcontroller retrieves all the records from firebase db and populate the tableView from an array. when the user selects an item from that tableview a new viewcontroller pops up segueing the object from the listView viewcontroller to the detail viewcontroller. data is populated to the fields successfully!
however when i try to update any of the textfield, the moment i switch to another textfield the initial value is restored in the edited textfield.
I have tried to removeAllObservers... but nothing worked. i even removed "import Firebase" and all associated objects and still it restores the initial value.
am i missing any concept here?
this is the code from the ListViewController:
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated);
self.activityIndicator.startAnimating();
self.observeIngrdUpdates = ref.observeEventType(.Value, withBlock: { (snapshot) in
self.ingredients.removeAll();
for child in snapshot.children {
var nutrition:String = "";
var type:Int = 0;
var desc:String = "";
var img:String = "";
var name:String = "";
var price:Double = 0.0;
if let _name = child.value["IngredientName"] as? String {
name = _name;
}
if let _nutrition = child.value["NutritionFacts"] as? String {
nutrition = _nutrition;
}
if let _type = child.value["IngredientType"] as? Int {
type = _type;
}
if let _desc = child.value["UnitDescription"] as? String {
desc = _desc;
}
if let _img = child.value["IngredientImage"] as? String {
img = _img;
}
if let _price = child.value["IngredientPrice"] as? Double {
price = _price;
}
let ingredient = Ingredient(name: name, type: type, image: img, unitDesc: desc, nutritionFacts: nutrition, price: price);
ingredient.key = child.key;
self.ingredients.append(ingredient);
}
self.tableView.reloadData();
})
}
and the PrepareForSegue:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let destinationVC = segue.destinationViewController as! EditIngredientVC;
if segue.identifier?.compare(SEGUE_EDIT_INGREDIENTS) == .OrderedSame {
destinationVC.ingredient = ingredients[tableView.indexPathForSelectedRow!.row];
destinationVC.controllerTitle = "Edit";
} else if segue.identifier?.compare(SEGUE_ADD_INGREDIENT) == .OrderedSame {
destinationVC.controllerTitle = "New";
}
}
this is the code for populating the fields in DetailViewController:
override func viewDidLayoutSubviews() {
lblControllerTitle.text = controllerTitle;
if controllerTitle?.localizedCaseInsensitiveCompare("NEW") == .OrderedSame {
self.segVegFru.selectedSegmentIndex = 0;
} else {
if ingredient != nil {
self.txtIngredientName.text = ingredient!.name;
self.txtUnitDesc.text = ingredient!.unitDesc;
self.segVegFru.selectedSegmentIndex = ingredient!.typeInt;
self.txtNutritionFacts.text = ingredient!.nutritionFacts;
self.txtPrice.text = "\(ingredient!.price)";
}
}
}
Thank you all for your help.
It's probably because you are putting your textField populating code, in viewDidLayoutSubviews() method.
This method will be triggered every time the layout of a views changes in viewcontroller.
Move it to viewDidLoad() and it should be fine.
The reason that it's being reverted to the previous value is because. You are populating the textField.text from the "ingredient" object. The ingredient will have the same value retained that you passed from previous view controller. Until you mutate it at some point.
And by the way Firebase is very cool I like it too :)

Loop through Realm Object fields in Swift

I have a Realm Object
class CoursesModel: Object {
dynamic var courseName = ""
dynamic var par3Field = 0
dynamic var par4Field = 0
dynamic var par5Field = 0
}
When somebody enters the course name I want to check whether it already exists before writing it to Realm.
Can you please tell me what I'm doing wrong because it doesn't seem to loop through.
class func compareCourse(name : String) -> Bool {
let c = name
do
{
let realm = try Realm()
let course = realm.objects(CoursesModel)
for course in course {
if course == c {
print("course = \(course)")
print("c = \(c)")
return true
}
else {
return false
}
}
}
catch
{
// return nil
}
return false
}
Any help will be greatly appreciated.
EDIT - WORKING CODE HERE
class func compareCourse(name : String) -> Bool {
let c = name
do
{
let realm = try Realm()
let course = realm.objects(CoursesModel)
for course in course {
let a = course.courseName
print("Model Course = \(a)")
print("Passed Course = \(c)")
if a == c {
return true
}
}
}
catch
{
// return nil
}
return false
}
You are returning in both branches of the loop, which immediately exits out of the function. You do not want to return false on the first failure, but only after all have failed (I think).

Resources