How to create and save a one to many relationship (Parent Child) using a tableView and Realm - ios

I am trying to create a one to many relationship, otherwise known as a parent child relationship, in realm. I looked at the documentation that realm offers but I am still a little stuck on how to do the actual saving to realm. I have two views, the main view is a view controller that just has a tableview with the numbers 1-7. In this view i can mass select for editing these rows in the table and save them to realm. That part is no big deal.
On the next view I have something very similar where there is a tableview with just some sample data. There is a mass select rows button, that is fine, it is the save button that I am having trouble with. The data in this tableView, which is the same on all of them just for testing purposes, is data i want to have a child relationship with the data on the first view.
For example if I have 4 saved to realm I click the row with 4 on it and it takes me to my next View. The tableview on this has two rows and other data, but i want to be able to mass select these rows and save them as a child to 4. I am a little confused as to what the save to realm function would look like.
this is my first view controller
import UIKit
import Realm
import RealmSwift
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
var realm: Realm!
fileprivate var createSaves = SaveClass.createSaves()
var testingBool = false
var values: [String] = []
var valuesTwo: [String] = []
var valuesThree: [String] = []
#IBOutlet weak var itemBtn: UIBarButtonItem!
#IBOutlet weak var saveBtn: UIBarButtonItem!
override func viewDidLoad() {
super.viewDidLoad()
print(Realm.Configuration.defaultConfiguration.fileURL!)
realm = try! Realm()
self.tableView.delegate = self
self.tableView.dataSource = self
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return createSaves.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
cell.txtLbl?.text = "\(createSaves[indexPath.row].label)"
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if testingBool == true {
values.append(createSaves[indexPath.row].label)
valuesTwo.append(createSaves[indexPath.row].romanNum)
valuesThree.append(createSaves[indexPath.row].txt)
} else if testingBool == false {
performSegue(withIdentifier: "segue", sender: indexPath)
}
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
if testingBool == true {
if let index = values.index(of: createSaves[indexPath.row].label) {
values.remove(at: index)
}
if let index = valuesTwo.index(of: createSaves[indexPath.row].romanNum) {
valuesTwo.remove(at: index)
}
if let index = valuesThree.index(of: createSaves[indexPath.row].txt) {
valuesThree.remove(at: index)
}
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let nxtVC = segue.destination as? TestingViewController
let myIndexPath = self.tableView.indexPathForSelectedRow!
let row = myIndexPath.row
nxtVC?.realmedData = createSaves[row].label
}
#IBAction func btnPressed(_ sender: Any) {
testingBool = !testingBool
if testingBool == true {
tableView.allowsMultipleSelection = true
tableView.allowsMultipleSelectionDuringEditing = true
itemBtn.title = "cancel"
} else if testingBool == false {
tableView.allowsMultipleSelection = false
tableView.allowsMultipleSelectionDuringEditing = false
itemBtn.title = "item"
}
}
#IBAction func saveBtnPressed(_ sender: Any) {
if testingBool == true {
//favorite(label: values)
realmed(label: values, romanNum: valuesTwo, txt: valuesThree)
}
}
func realmed(label: [String], romanNum: [String], txt: [String]) {
try? realm!.write {
for (stringOne, (stringTwo, stringThree)) in zip(label, zip(romanNum, txt)) {
let realmed = Realmed(label: stringOne, romanNum: stringTwo, txt: stringThree)
realm.add(realmed)
}
}
}
}
this is my second view
import UIKit
import Realm
import RealmSwift
class TestingViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var mainLbl: UILabel!
var realm: Realm!
var realmedData = ""
var testingBool = false
var values: [String] = []
var valuesTwo: [String] = []
#IBOutlet weak var testTable: UITableView!
#IBOutlet weak var selectBtn: UIButton!
#IBOutlet weak var saveBtn: UIButton!
let firstSave = OtherSave.otherArrOne()
override func viewDidLoad() {
super.viewDidLoad()
realm = try! Realm()
self.testTable.delegate = self
self.testTable.dataSource = self
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if objectExists(label: realmedData) == true {
self.mainLbl.text = "\(realmedData)"
} else {
self.mainLbl.text = "Don't Know"
}
}
#IBAction func selectBtnPressed(_ sender: Any) {
testingBool = !testingBool
if testingBool == true {
testTable.allowsMultipleSelection = true
testTable.allowsMultipleSelectionDuringEditing = true
} else if testingBool == false {
testTable.allowsMultipleSelection = false
testTable.allowsMultipleSelectionDuringEditing = false
}
}
#IBAction func saveBtnPressed(_ sender: Any) {
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return firstSave.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell2", for: indexPath) as! TestingTableViewCell
cell.nameLbl.text = "\(firstSave[indexPath.row].name)"
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if testingBool == true {
values.append(firstSave[indexPath.row].info)
valuesTwo.append(firstSave[indexPath.row].name)
}
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
if testingBool == true {
if let index = values.index(of: firstSave[indexPath.row].info) {
values.remove(at: index)
}
if let index = valuesTwo.index(of: firstSave[indexPath.row].name) {
valuesTwo.remove(at: index)
}
}
}
func fetchingLabel(label: String) -> Realmed? {
let realm = try? Realm()
return realm?.object(ofType: Realmed.self, forPrimaryKey: label)
}
func objectExists(label: String) -> Bool {
return realm.object(ofType: Realmed.self, forPrimaryKey: label) != nil
}
}
this is the parent's realm object:
import Foundation
import Realm
import RealmSwift
class Realmed: Object {
#objc dynamic var label = ""
#objc dynamic var romanNum = ""
#objc dynamic var txt = ""
let realmTwo = List<RealmTwo>()
override static func primaryKey() -> String {
return "label"
}
convenience init(label: String, romanNum: String, txt: String) {
self.init()
self.label = label
self.romanNum = romanNum
self.txt = txt
}
}
this is the child's realm object
import Foundation
import UIKit
import Realm
import RealmSwift
class RealmTwo: Object {
#objc dynamic var spanish = String()
#objc dynamic var french = String()
let realmed = LinkingObjects(fromType: Realmed.self, property: "realmTwo")
}
I have tried to do a function similar to the first view controllers:
func realmed(label: [String], romanNum: [String], txt: [String]) {
try? realm!.write {
for (stringOne, (stringTwo, stringThree)) in zip(label, zip(romanNum, txt)) {
let realmed = Realmed(label: stringOne, romanNum: stringTwo, txt: stringThree)
realm.add(realmed)
}
}
}
but I don't really understand how to create an instance of the data that i want to save as an array.
If there is anything i can help with, please ask
Thank you

I assume you want to create “RealmTwo” objects from “values” and “valuesTwo” and add them to a Realmed object .
What you want to do is
Fetch a parent object (Realmed)
Create a child object (RealmTwo)
Append the child to the parent object
You can do that with a function like this.
func save() {
let realmed = fetchingLabel(label: realmedData)! // fetch a parent
do {
try realm.write {
for index in 0..<values.count {
let realmTwo = RealmTwo() // create a child
realmTwo.french = values[index]
realmTwo.spanish = valuesTwo[index]
realmed.realmTwo.append(realmTwo) // append
}
}
} catch {
}
}

Related

My label.text is nil with Reusable library in swift

I'm trying to show the label with content "aaaaaaaaaaaaaaaaA". I'm using Reusable library. Although I connected IBOutlet right way, the label and imageView of cell did not show content.
This is my cell class
import UIKit
import Reusable
import SDWebImage
protocol ChooseMemberTableViewCellDelegate: AnyObject {
func addUserToGroup(forUser user: User)
}
class ChooseMemberTableViewCell: UITableViewCell, Reusable {
#IBOutlet weak var userImageView: UIImageView!
#IBOutlet weak var userNameLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
func setupCell(data: User) {
userNameLabel?.text = "aaaaaaaaaaaaaaaaA"
// userNameLabel?.text = "\(data.userName)"
// let url = URL(string: data.image)
// userImageView.sd_setImage(with: url ?? "", completed: nil)
}
}
This is my ViewController, which i register cell
//
// MembersViewController.swift
// Message
//
// Created by Minh Tâm on 1/9/20.
// Copyright © 2020 Minh Tâm. All rights reserved.
//
import UIKit
import Firebase
import Reusable
import Then
private enum Constants {
static let numberOfSection = 1
static let heightForRows: CGFloat = 60
}
final class ChooseMembersViewController: UIViewController {
#IBOutlet private weak var searchMembers: UISearchBar!
#IBOutlet private weak var listContacts: UITableView!
var searchUser = [User]()
private let database = Firestore.firestore()
var users = [User]()
private var currentUser = Auth.auth().currentUser
private var roomRepository = RoomRepository()
private var userRepository = UserRepository()
var groupName: String?
private var selectUserArray = [String]()
override func viewDidLoad() {
super.viewDidLoad()
// userRepository.fetchUser()
configListTableView()
fetchUser()
}
func configListTableView() {
listContacts.do {
$0.register(cellType: ChooseMemberTableViewCell.self)
$0.delegate = self
$0.dataSource = self
}
}
public func fetchUser() {
database.collection("users").getDocuments(){ (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
if let snapshot = querySnapshot {
for document in snapshot.documents {
let data = document.data()
let uid = data["uid"] as? String ?? ""
if uid != self.currentUser?.uid {
let newUser = User.map(uid: uid, dictionary: data)
self.users.append(newUser)
}
}
}
self.searchUser = self.users
self.listContacts.reloadData()
}
}
}
#IBAction func handleBack(_ sender: UIButton) {
self.dismiss(animated: false)
}
#IBAction func handleDone(_ sender: UIButton) {
guard let currentUser = currentUser, let groupName = groupName else { return }
selectUserArray.append(currentUser.uid)
let time = NSNumber(value: Int(NSDate().timeIntervalSince1970))
roomRepository.updateFirebase(groupName: groupName, time: time, selectUserArray: selectUserArray)
}
}
extension ChooseMembersViewController: UISearchBarDelegate {
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if searchText.isEmpty {
searchUser = users
} else {
searchUser = users.filter { $0.userName.lowercased().contains(searchText.lowercased()) }
}
listContacts.reloadData()
}
}
extension ChooseMembersViewController: UITableViewDelegate, UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return Constants.numberOfSection
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return searchUser.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = listContacts.dequeueReusableCell(for: indexPath, cellType: ChooseMemberTableViewCell.self).then {
let user = searchUser[indexPath.row]
$0.setupCell(data: user)
}
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return Constants.heightForRows
}
}
extension ChooseMembersViewController: ChooseMemberTableViewCellDelegate {
func addUserToGroup(forUser user: User) {
let selectedUserUid = user.uid
selectUserArray.append(selectedUserUid)
}
}
Cell does not show information of label and imageView
ChooseMemberTableViewCell is correct, but I'd recommend use userNameLabel.text = "aaaaaaaaaaaaaaaaA" (not optional for userNameLabel)
If you use cell from Storyboard into your tableView - no need to register this one, the UIStoryboard already auto-register its cells. I mean you should remove this line: listContacts.register(cellType: ChooseMemberTableViewCell.self)
You should add identifier ChooseMemberTableViewCell for cell in Storyboard
This should work for cellForRowAt:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: ChooseMemberTableViewCell = tableView.dequeueReusableCell(for: indexPath)
cell.setupCell(data: user)
.then {
let user = searchUser[indexPath.row]
$0.setupCell(data: user)
}
return cell
}
I guess that's all. I've done it and I see label with text in tableView

How to pass data with delegate from footer cell to view controller?

Ive been stuck trying to pass data from the FoodEatenController(FEC) Footer to the TotalCaloriesController. The code that I have now it shows NOTHING in the calorieLbl of the TotalCalorieController(TCC).
The delegate that ive been using to pass the data from the FEC to the TCC does not pass the text/string data that is in the FoodFooter calorieTotallbl to the TEC calorieLbl
the data that populates the cells of the FEC is retrieved from Cloud Firestore and passed in from anotherView Controller (FoodPickerController)
import UIKit
class FoodEatenController: UIViewController{
var selectedFood: FoodList! // allows data to be passed into the VC
// allows data to be sepearted into sections
var foodItems: [FoodItem] = []
var groupedFoodItems: [String: [FoodItem]] = [:]
var dateSectionTitle: [String] = []
#IBOutlet weak var tableView: UITableView!
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let vc = segue.destination as? TotalCalorieController {
}
}
}
extension FoodEatenController: UITableViewDelegate, UITableViewDataSource{
func numberOfSections(in tableView: UITableView) -> Int {
return dateSectionTitle.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let date = dateSectionTitle[section]
return groupedFoodItems[date]!.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let foodCell = tableView.dequeueReusableCell(withIdentifier: "FoodCell") as! FoodCell
let date = dateSectionTitle[indexPath.section]
let foodItemsToDisplay = groupedFoodItems[date]![indexPath.row]
foodCell.configure(withCartItems: fooditemsToDisplay.foodList)
return foodCell
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let foodHeader = tableView.dequeueReusableCell(withIdentifier: "FoodHeader") as! FoodHeader
let headerTitle = dateSectionTitle[section]
foodHeader.dateLbl.text = "Date: \(headerTitle)"
return foodHeader
}
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let foodFooter = tableView.dequeueReusableCell(withIdentifier: "FoodFooter") as! FoodFooter
let date = dateSectionTitle[section]
let arrAllItems = groupedFoodItems[date]!
var total: Float = 0
for item in arrAllItems {
let eaten = item.productList
let selectedMeal = item.foodList.selectedOption
if selectedMeal == 1 {
total = total + (Float(eaten!.calorie))
}
}
foodFooter.calorieTotal.text = String(subtotal!)
foodFooter.delegate = self
return foodFooter
}
}
extension FoodEatenController: EatenFoodDelegate {
func onTouchCaloireInfo(info: String) {
let popUp = self.storyboard?.instantiateViewController(withIdentifier: "AdditionalCostsVC") as! TotalCalorieController
popUp.calorieLbl.text = info
}
}
import UIKit
protocol EatenFoodDelegate: class {
func onTouchCaloireInfo(info: String)
}
class FoodFooter: UITableViewCell {
weak var delegate: EatenFoodDelegate? = nil
#IBOutlet weak var calorieTotal: UILabel!
#IBOutlet weak var totalInfoBtn: UIButton!
#IBAction func totalOnClicked(_ sender: AnyObject) {
self.delegate?. onTouchCaloireInfo(info: calorieTotal.text!)
}
}
class TotalCalorieController: UIViewController, EatenFoodDelegate {
func onTouchCaloireInfo(info: String) {
calorieLbl.text = info
}
#IBOutlet weak var calorieLbl: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func returnButton(_ sender: Any) {
dismiss(animated: true, completion: nil)
print("Close Taxes and Fees")
}
}
Add the following line at the end of the func onTouchCaloireInfo(info:)
self.present(popUp, animated: true, completion: nil)
If you would like to be sure that the function onTouchCaloireInfo(info:) gets called, just add the following line:
debugPrint("onTouchCaloireInfo")
And check, if it prints the given string in the console of the Xcode
extension FoodEatenController: EatenFoodDelegate {
func onTouchCaloireInfo(info: String) {
debugPrint("onTouchCaloireInfo")
let popUp = self.storyboard?.instantiateViewController(withIdentifier: "AdditionalCostsVC") as! TotalCalorieController
self.present(popUp, animated: true) {
popUp.calorieLbl.text = info
}
}
}

Can't save data to MVC (Swift)

I can't save my data from textfield to MVC.
When I enter text and click on the button, the data is not added.
how can i fix it?
I delete viewDidLoad() with table.delegate = self, table.dataSource = self from this Question.
This is the ViewController:
class ViewController: UIViewController, UITextFieldDelegate, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var table: UITableView!
#IBOutlet weak var Text: UITextField!
var model = ViewModel()
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return model.persons?.count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! TableViewCell
let person = model.persons?[indexPath.row]
cell.Name?.text = person?.name
return cell
}
func textFieldEditing(_ sender: UITextField) {
model.input = sender.text
}
#IBAction func Add(_ sender: UIButton) {
if let name = model.input, name.count > 0 {
let person = Person()
person.name = name
model.addPerson(person)
model.input = nil
}
table.reloadData()
}
}
Model
class ViewModel {
var persons: [Person]?
var input: String?
func addPerson(_ person: Person) {
if persons == nil { persons = [] }
persons?.append(person)
}
}
class Person {
var name: String?
}
try this
// func textFieldEditing(_ sender: UITextField) {
// model.input = sender.text
// }
func textFieldDidEndEditing(_ textField: UITextField) {
model.input = textField.text
}
#IBAction func Add(_ sender: UIButton) {
self.view.endEditing(true)
if let name = model.input, name.count > 0 {
let person = Person()
person.name = name
model.addPerson(person)
model.input = nil
}
table.reloadData()
}

how to insert data from string to Sqlite in Swift3?

I have a problem that I have tried to insert string data to Sqlite. I want to do is in my Details ViewController if user press Like button, the word and meaning of string pass into Sqlite file that show in Favourite ViewController. Below is my DetailsVC code.
import UIKit
import AVFoundation
class DetailsVC: UIViewController, UITextViewDelegate {
#IBOutlet weak var wordLbl: UILabel!
#IBOutlet weak var meaningLbl: UITextView!
#IBOutlet weak var sysWordLbl: UILabel!
#IBOutlet weak var sysWordsLbl: UILabel!
#IBOutlet weak var anyWordsLbl: UILabel!
#IBOutlet weak var anyWordLbl: UILabel!
#IBOutlet weak var wordImg: UIImageView!
#IBOutlet weak var buSound: UIButton!
#IBOutlet weak var buLike: UIButton!
#IBOutlet weak var menuBtn: UIButton!
//Data from HomeVC
var data:Data?
var fdatas = [FData]()
var favouriteVC = FavouriteVC()
override func viewDidLoad() {
super.viewDidLoad()
meaningLbl.delegate = self
wordLbl.text = "\((data?._word.capitalized)!) \((data?._phonetic)!)"
self.meaningLbl.text = data?._meaning
self.sysWordsLbl.text = data?._meaning
self.anyWordsLbl.text = data?._meaning
self.sysWordLbl.text = "Synonym"
self.anyWordLbl.text = "Antonym"
meaningLbl.font = UIFont(name: FONT_REGULAR, size: 24.0)
}
#IBAction func buMenu(_ sender: Any) {
//menu
dismiss(animated: true, completion: nil)
}
#IBAction func buSound(_ sender: Any) {
//Todo: speak the word
if buSound.isEnabled == true{
buSound.setImage(UIImage(named: "volume-2.png"), for: .normal)
}else{
buSound.setImage(UIImage(named: "volume-un-2.png"), for: .normal)
}
let utTerance = AVSpeechUtterance(string: "\((data?._word)!)")
let synthesize = AVSpeechSynthesizer()
utTerance.voice = AVSpeechSynthesisVoice(language: "en-gb")
synthesize.speak(utTerance)
}
#IBAction func buLike(_ sender: Any) {
//Todo: Click Like
if buLike.isEnabled == true{
buLike.setImage(UIImage(named: "heart.png"), for: .normal)
//Save data to Database
let word = data?._word ?? ""
let meaning = data?._meaning ?? ""
do{
let favData = FData(word: word, meaning: meaning)
self.fdatas.append(favData)
print("\(word), \(meaning)")
}catch{
print(error)
}
}else{
buSound.setImage(UIImage(named: "heart-un.png"), for: .normal)
}
//FavouriteVC.insertRowsAtIndexPaths([NSIndexPath(forRow: fdatas.count-1, inSection: 0)], withRowAnimation: .Fade)
}
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
self.view.endEditing(true);
return false;
}
}
My FavouriteVC
import UIKit
import SwipeCellKit
import SQLite
class FavouriteVC: UIViewController,UITableViewDelegate,UITableViewDataSource, SwipeTableViewCellDelegate{
//TabelView
#IBOutlet weak var fTableView: UITableView!
//Variables
var fdata = [FData]()
override func viewDidLoad() {
super.viewDidLoad()
// FtableView
fTableView.delegate = self
fTableView.dataSource = self
//reload Data
fTableView.reloadData()
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return fdata.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: FAVOURITE_CELL, for: indexPath) as? FavouriteCell{
cell.configureCell(fdata: fdata[indexPath.row])
//cell.delegate = self
return cell
}
return FavouriteCell()
}
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath, for orientation: SwipeActionsOrientation) -> [SwipeAction]? {
guard orientation == .right else { return nil }
let deleteAction = SwipeAction(style: .destructive, title: "Delete") { action, indexPath in
// handle action by updating model with deletion
}
// customize the action appearance
deleteAction.image = UIImage(named: "delete")
return [deleteAction]
}
}
FData.swif
import Foundation
class FData {
//let id: Int64?
var word: String
var meaning: String
//var address: String
init(id: Int64) {
// self.id = id
word = ""
meaning = ""
//address = ""
}
init(word: String, meaning: String) {
// self.id = id
self.word = word
self.meaning = meaning
//self.address = address
}
}
FavouriteData Instance
import UIKit
import SQLite
class FavouriteData{
static let instance = FavouriteData()
private let db: Connection?
private let words = Table("words")
private let id = Expression<Int64>("id")
private let wordL = Expression<String?>("word")
private let meaningL = Expression<String?>("shanword_uni")
//private let address = Expression<String>("address")
private init() {
let path = NSSearchPathForDirectoriesInDomains(
.documentDirectory, .userDomainMask, true
).first!
do {
db = try Connection("\(path)/favourite.sqlite")
} catch {
db = nil
print ("Unable to open database")
}
createTable()
}
func createTable() {
do {
try db!.run(words.create(ifNotExists: true) { table in
table.column(id, primaryKey: true)
table.column(wordL)
//table.column(phone, unique: true)
table.column(meaningL)
})
} catch {
print("Unable to create table")
}
}
func addData(word: String, meaning: String) -> Int64? {
do {
let insert = words.insert(wordL <- word, meaningL <- meaning)
let id = try db!.run(insert)
print("Save: \(id)")
return id
} catch {
print("Insert failed")
return -1
}
}
func getData() -> [FData] {
var contacts = [FData]()
do {
for contact in try db!.prepare(self.words) {
contacts.append(FData(
// id: contact[id],
word: contact[wordL]!,
meaning: contact[meaningL]!))
}
} catch {
print("Select failed")
}
return contacts
}
func deleteData(index: Int64) -> Bool {
do {
let contact = words.filter(id == index)
try db!.run(contact.delete())
return true
} catch {
print("Delete failed")
}
return false
}
}
But, there is not cell of row in the FavouriteVC. How to fix this? Please help me.
Best,
Sai Tawng Pha
I have solved my problem by add one line of code. In my FavouriteVC class, just add it under ViewdidLoad function " fdata = FavouriteData.instance.getData() ".
Here is my complete code.
import UIKit
import SwipeCellKit
import SQLite
class FavouriteVC: UIViewController,UITableViewDelegate,UITableViewDataSource, SwipeTableViewCellDelegate{
//TabelView
#IBOutlet weak var fTableView: UITableView!
//Variables
var fdata = [FData]()
override func viewDidLoad() {
super.viewDidLoad()
// FtableView
fTableView.delegate = self
fTableView.dataSource = self
//reload Data
fTableView.reloadData()
//Get Data
fdata = FavouriteData.instance.getData()
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return fdata.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: FAVOURITE_CELL, for: indexPath) as? FavouriteCell{
cell.configureCell(fdata: fdata[indexPath.row])
//cell.delegate = self
return cell
}
return FavouriteCell()
}
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath, for orientation: SwipeActionsOrientation) -> [SwipeAction]? {
guard orientation == .right else { return nil }
let deleteAction = SwipeAction(style: .destructive, title: "Delete") { action, indexPath in
// handle action by updating model with deletion
}
// customize the action appearance
deleteAction.image = UIImage(named: "delete")
return [deleteAction]
}
}

NSManagedObject Array becomes nil when UITableView is scrolled

I have a ViewController in my app where I have to show Settings to the user and user can turn the Settings on or off using UISwitch. I have to store the settings in the local db and based on that display data to user in app.
I am using SugarRecord for Core Data Management. Initially all the settings are turned on.
SugarRecordManager.swift
import Foundation
import SugarRecord
import CoreData
class SugarRecordManager
{
static let sharedInstance = SugarRecordManager()
private init(){
}
// Initializing CoreDataDefaultStorage
func coreDataStorage() -> CoreDataDefaultStorage {
let store = CoreDataStore.named("db")
let bundle = Bundle(for: type(of: self))
let model = CoreDataObjectModel.merged([bundle])
let defaultStorage = try! CoreDataDefaultStorage(store: store, model: model)
return defaultStorage
}
//MARK:- User Settings methods
//update local settings
func updateSettingsModel(userSettings: [UserSetting]){
let db = self.coreDataStorage()
for localSetting in userSettings{
try! db.operation { (context, save) -> Void in
if let settingObjectToUpdate = try! context.request(UserSetting.self).filtered(with: "groupName", equalTo: localSetting.groupName!).fetch().first{
settingObjectToUpdate.groupId = localSetting.groupId! as String
settingObjectToUpdate.groupName = localSetting.groupName! as String
settingObjectToUpdate.isGroupActive = localSetting.isGroupActive
try! context.insert(settingObjectToUpdate)
save()
}
}
}
}
//retrieve settings from storage
func getAllSettings() -> [UserSetting] {
let db = self.coreDataStorage()
var userSettings : [UserSetting]
do {
userSettings = try db.fetch(FetchRequest<UserSetting>())
} catch {
userSettings = []
}
return userSettings
}
//initialise settings for the first time
func initialiseUserSettings(){
let db = self.coreDataStorage()
var groupNameArray = UserDefaults.standard.value(forKey: "groupNamesArrayKey") as? [String]
var groupIdArray = UserDefaults.standard.value(forKey: "groupIdsArrayKey") as? [String]
for i in 0 ..< groupIdArray!.count {
try! db.operation { (context, save) -> Void in
let settingObject: UserSetting = try! context.new()
settingObject.groupId = groupIdArray?[i];
settingObject.groupName = groupNameArray?[i];
settingObject.isGroupActive = true;
try! context.insert(settingObject)
save()
}
}
}
}
SettingsViewController.swift
class SettingsViewController: BaseViewController, UITableViewDataSource, UITableViewDelegate, SettingsCellDelegate {
#IBOutlet weak var btnSideNav: UIBarButtonItem!
#IBOutlet weak var settingsTable: UITableView!
var userSetting = [UserSetting]() //array to hold settings from storage
override func viewDidLoad() {
super.viewDidLoad()
self.automaticallyAdjustsScrollViewInsets = false;
btnSideNav.target = revealViewController();
btnSideNav.action = #selector(SWRevealViewController.revealToggle(_:));
userSetting = SugarRecordManager.sharedInstance.getAllSettings() //here userSetting contains data and I have checked it
self.settingsTable.reloadData()
self.settingsTable.dataSource = self;
self.settingsTable.delegate = self;
// Do any additional setup after loading the view.
}
//MARK:- Table View Methods
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("Count of cells = \(self.userSetting.count)") //prints 18 which is good
return self.userSetting.count
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 60;
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let settingsCell : SettingsCell? = tableView.dequeueReusableCell(withIdentifier: "SettingsCell") as? SettingsCell;
settingsCell?.setUpWithModel(model: self.userSetting[indexPath.row], cell: settingsCell!)
settingsCell?.delegate = self as SettingsCellDelegate;
return settingsCell!
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}
func didTappedSwitch(cell: SettingsCell) {
let indexPath = settingsTable.indexPath(for: cell);
userSetting[(indexPath?.row)!].isGroupActive? = cell.isGroupActive.isOn as NSNumber
}
#IBAction func btnSaveTapped(_ sender: UIButton) {
// code to save settings
}
}
SettingsCell.swift
protocol SettingsCellDelegate {
func didTappedSwitch(cell: SettingsCell)
}
class SettingsCell: UITableViewCell {
#IBOutlet weak var groupName: UILabel!
#IBOutlet weak var lblGroupId: UILabel!
#IBOutlet weak var isGroupActive: UISwitch!
var delegate: SettingsCellDelegate!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func setUpWithModel(model: UserSetting, cell: SettingsCell)
{
cell.groupName.text = model.groupName;
cell.lblGroupId.text = model.groupId;
isGroupActive.setOn((model.isGroupActive?.boolValue)!, animated: false)
}
#IBAction func isGroupActiveValueChanged(_ sender: UISwitch) {
delegate.didTappedSwitch(cell: self)
}
}
Now, initally the TableView is populated and all arrays are working fine but as soon as I scroll the TableView all data is gone. Even the userSetting array is nill. I know it's something to do with context but can't figure out what. Any help would be greatly appreciated.
Change your func coreDataStorage() -> CoreDataDefaultStorage like this
// Initializing CoreDataDefaultStorage
lazy var coreDataStorage: CoreDataDefaultStorage = {
let store = CoreDataStore.named("db")
let bundle = Bundle(for: type(of: self))
let model = CoreDataObjectModel.merged([bundle])
let defaultStorage = try! CoreDataDefaultStorage(store: store, model: model)
return defaultStorage
}()
you have this problem because you re-init CoreDataDefaultStorage each time when you do any request.
After you made it lazy - you will have only one CoreDataDefaultStorage for all app life
Basically, it will be good to make coreDataStorage as singleton

Resources