Present list of items in tableview from Realm database - ios

I am successfully downloading and printing data from Realm database. Here is my log:
Item(id: Optional(0), name: Optional("Item (0)"), descr: Optional("Description of item (0)"),
icon: Optional("http://192.168.1.101:8080/api/items/0/icon.png"),
url: Optional("http://192.168.1.101:8080/api/items/0")))
Now I have to assign those values on actual list and I am getting a clean sheet tableview. How to do it properly? I am using .xib as tablewViewCell. I am thankful for any tips.
class ItemRealm : Object {
dynamic var id = 0
dynamic var name = ""
dynamic var desc = ""
dynamic var icon = ""
override class func primaryKey() -> String? {
return "id"
}
}
class ViewController: UIViewController, UITableViewDataSource, UISearchBarDelegate {
#IBOutlet weak var searchBar: UISearchBar!
#IBOutlet weak var tableView: UITableView!
let realm = try! Realm()
let results = try! Realm().objects(ItemRealm.self).sorted(byKeyPath: "id")
let SERVER_URL = "http://192.168.1.101:8080/api/items"
override func viewDidLoad() {
super.viewDidLoad()
Alamofire.request(SERVER_URL).responseJSON { response in
let items = [Item].from(jsonArray: response.result.value as! [Gloss.JSON])
print(items?[0] as Any)
try! self.realm.write {
for item in items! {
let itemRealm = ItemRealm()
itemRealm.id = item.id!
itemRealm.name = item.name!
itemRealm.desc = item.descr!
itemRealm.icon = item.icon!
self.realm.add(itemRealm)
}
}
_ = self.realm.objects(ItemRealm.self)
// print(items?[0] as Any)
}
// Do any additional setup after loading the view.
tableView.register(UINib(nibName: "TableViewCell", bundle: nil), forCellReuseIdentifier: "TableViewCell")
}
// MARK: - UITableView data source
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return results.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell") as! TableViewCell
var object: ItemRealm
object = self.results[indexPath.row] as ItemRealm
cell.item = object
return cell
}
}

I think you are missing self.tableView.reloadData() after getting data from the response. Consider also assigning fetched data to your results variable.

Related

How to get the name list on Realm database and display on my stimulator?

Here I have a Realm Database which is have some data in it and I want to display it on my Stimulator but it turn out display some other thing. What's wrong in my code?
This is the data of my Realm Database and I also marked the data which I want to display it.
The stimulator which display something like this.
And here is my ViewController.swift code's.
import UIKit
import RealmSwift
class ViewController: UIViewController,UITableViewDataSource { //UITableViewDataSource
#IBOutlet weak var mytableview: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let realm = try! Realm()
let theItem = realm.objects(Item.self).filter("itemid >= 1")
return theItem.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let realm = try! Realm()
let theItem = realm.objects(Item.self).filter("itemid >= 1")
print(theItem)
let cell = tableView.dequeueReusableCell(withIdentifier: "cell1")
//I suspect the problem is at here...
cell?.textLabel?.text = "\(theItem)"
return cell!
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
}
class Category: Object {
#objc dynamic var name: String?
#objc dynamic var caid: Int = 0
}
class Item: Object {
#objc dynamic var name: String?
#objc dynamic var itemid: Int = 0
#objc dynamic var cateid: Int = 0
}
Your problem is that you need to get the string from the Item object. try something like
"\(theItem.name)".
func getNames() -> [String]{
let items = realm.objects(Item.self).filter("itemid >= 1").toArray(ofType: Item.self ) as [Item]
return items.map { $0.name }
}
extension Results {
func toArray<T>(ofType: T.Type) -> [T] {
var array = [T]()
for i in 0 ..< count {
if let result = self[i] as? T {
array.append(result)
}
}
return array
}
}
I found a way to display the data already. I just need to add indexPath.row in my code and it can handle the data already.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let realm = try! Realm()
let theItem = realm.objects(Item.self).filter("itemid >= 1")
//I only add below this indexpath
let cellData = theItem[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "cell1")
//and change this part and it's done.
cell?.textLabel?.text = cellData.name
print(theItem)
return cell!
}

How to reload TableVIew Using Model NSObject Class with MVVM

I want to reload TableView without tableview.reloadData() method for that i have used MVVM structure so i have attach model class to storyboard and the issue is that my tableview is reload first and then i get all data how should i solved this issue please help me if any one have a solution !!
This is storyboard model attach
Model Code :-
class MovieModel: Decodable{
var artistName: String = ""
var trackName: String = ""
init(artistName: String, trackName: String){
self.artistName = artistName
self.trackName = trackName
}
}
class ResultModel: Decodable{
var results = [MovieModel]()
init(results: [MovieModel]) {
self.results = results
}
}
My ViewModel File code :-
class MovieViewModel: NSObject {
var artistName: String = ""
var trackName: String = ""
var movieModel: MovieModel?
var movieData = [MovieViewModel]()
override init() {
}
init(movie: MovieModel) {
self.artistName = movie.artistName
self.trackName = movie.trackName
}
func getData(){
Service.shareInstance.getAllMovieData { (movie, error) in
if error == nil{
self.movieData = movie?.map({return MovieViewModel(movie: $0)}) ?? []
print(self.movieData)
}else{
print("\(String(describing: error))")
}
}
}
func numberOfRow(section:Int) -> Int{
return movieData.count
}
func cellForRow(indexPath: IndexPath) -> MovieViewModel{
return self.movieData[indexPath.row]
}
}
My ViewController Code :-
class ViewController: UIViewController {
#IBOutlet var movieVM: MovieViewModel?
#IBOutlet var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
self.movieVM?.getData()
}
}
extension ViewController: UITableViewDelegate, UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return movieVM?.numberOfRow(section: section) ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell")
let movie = movieVM?.cellForRow(indexPath: indexPath)
cell?.textLabel?.text = movie?.artistName
cell?.detailTextLabel?.text = movie?.trackName
return cell!
}
}
In my case i am not reload tableview all this is done using ModelClass ! Thank You !!

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

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 {
}
}

can we pass data from table cell to table view where both are xib files?

I want to pass table cell data (xib file) to table view (also a xib file). I have tried passing the data using the following piece of code but did not get an appropriate result.
PropertyCell.swift
import UIKit
class PropertyCell: UITableViewCell {
#IBOutlet weak var propertyCodeLbl: UILabel!
#IBOutlet weak var addressLbl: UILabel!
}
I have attached the screenshot for PropertyCell below -
PropertyCell.xib
PropertyCell.xib file
PropertyTableVC.swift
import UIKit
import Alamofire
class PropertyTableVC: UITableViewController {
#IBOutlet var propertyTabel: UITableView!
let URL_Landlord_Property_List = "http://127.0.0.1/source/api/LandlordPropertyList.php"
var count: Int = 0
var landlordPropertyArray: [PropertyList]? = []
override func viewDidLoad() {
super.viewDidLoad()
fetchData()
propertyTabel.dataSource = self
propertyTabel.delegate = self
let nibName = UINib(nibName: "PropertyCell", bundle:nil)
self.propertyTabel.register(nibName, forCellReuseIdentifier: "Cell")
}
func fetchData(){
let urlRequest = URLRequest(url: URL(string: URL_Landlord_Property_List)!)
let task = URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
if error != nil{
print(error!)
return
}
print(data!)
self.landlordPropertyArray = [PropertyList]()
self.count = (self.landlordPropertyArray?.count)!
do{
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [String: AnyObject]
if let datafromjson = json["landlords_property_list"] as? [[String: AnyObject]] {
print(datafromjson)
for data in datafromjson{
var property = PropertyList()
if let id = data["ID"] as? Int,let code = data["Code"] as? String, let address1 = data["Address"] as? String
{
property.id = id
property.code = code
property.address1 = address1
}
self.landlordPropertyArray?.append(property)
}
print(self.landlordPropertyArray)
}
DispatchQueue.main.async {
self.propertyTabel.reloadData()
}
}catch let error {
print(error)
}
}
task.resume()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return (landlordPropertyArray?.count)!
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Configure the cell...
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! PropertyCell
cell.propertyCodeLbl.text = self.landlordPropertyArray?[indexPath.item].code
cell.addressLbl.text = self.landlordPropertyArray?[indexPath.item].address1
return cell
}
}
Attached the screenshot for Property Table below -
PropertyTableVC.xib
PropertyTableVC.xib file
Your TableViewCell :
import UIKit
protocol yourProtocolName { //add protocol here
func getDataFromTableViewCellToViewController (sender : self) //* as you need to pass the table view cell, so pass it as self
}
class PropertyCell: UITableViewCell {
#IBOutlet weak var propertyCodeLbl: UILabel!
#IBOutlet weak var addressLbl: UILabel!
var delegate : yourProtocolName? //set a delegate
override func awakeFromNib() {
super.awakeFromNib()
if delegate != nil {
delegate.getDataFromTableViewCellToViewController(sender :self) *
}
}
}
And your ViewController :
import UIKit
import Alamofire
class PropertyTableVC: UITableViewController,yourProtocolName { //conform to the protocol you created in tableViewCell
#IBOutlet var propertyTabel: UITableView!
let URL_Landlord_Property_List = "http://127.0.0.1/source/api/LandlordPropertyList.php"
var count: Int = 0
var landlordPropertyArray: [PropertyList]? = []
override func viewDidLoad() {
super.viewDidLoad()
fetchData()
propertyTabel.dataSource = self
propertyTabel.delegate = self
let nibName = UINib(nibName: "PropertyCell", bundle:nil)
self.propertyTabel.register(nibName, forCellReuseIdentifier: "Cell")
}
func fetchData(){
let urlRequest = URLRequest(url: URL(string: URL_Landlord_Property_List)!)
let task = URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
if error != nil{
print(error!)
return
}
print(data!)
self.landlordPropertyArray = [PropertyList]()
self.count = (self.landlordPropertyArray?.count)!
do{
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [String: AnyObject]
if let datafromjson = json["landlords_property_list"] as? [[String: AnyObject]] {
print(datafromjson)
for data in datafromjson{
var property = PropertyList()
if let id = data["ID"] as? Int,let code = data["Code"] as? String, let address1 = data["Address"] as? String
{
property.id = id
property.code = code
property.address1 = address1
}
self.landlordPropertyArray?.append(property)
}
print(self.landlordPropertyArray)
}
DispatchQueue.main.async {
self.propertyTabel.reloadData()
}
}catch let error {
print(error)
}
}
task.resume()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return (landlordPropertyArray?.count)!
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Configure the cell...
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! PropertyCell
cell.propertyCodeLbl.text = self.landlordPropertyArray?[indexPath.item].code
cell.addressLbl.text = self.landlordPropertyArray?[indexPath.item].address1
return cell
}
func getDataFromTableViewCellToViewController(sender :UITableViewCell) {
//make a callback here
}
}
(*) Marked fields are updated code
Call fetchData() function after tableview delegate and datasource assigning
propertyTabel.dataSource = self
propertyTabel.delegate = self
Updated answer is
Create Cell Class like this
import UIKit
class YourTableViewCell: UITableViewCell {
#IBOutlet weak var profileImageView: UIImageView!
#IBOutlet weak var userNameLabel: UILabel!
#IBOutlet weak var timeDateLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
self.backgroundColor = UIColor.tableViewBackgroundColor()
self.selectionStyle = .none
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
class func cellForTableView(tableView: UITableView, atIndexPath indexPath: IndexPath) -> YourTableViewCell {
let kYourTableViewCell = "YourTableViewCellIdentifier"
tableView.register(UINib(nibName:"RRLoadQuestionsTableViewCell", bundle: Bundle.main), forCellReuseIdentifier: kYourTableViewCell)
let cell = tableView.dequeueReusableCell(withIdentifier: kYourTableViewCell, for: indexPath) as! YourTableViewCell
return cell
}
}
Then create UIViewController Class and place UITableView on it and link with outlet
class YourViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, UITextViewDelegate {
#IBOutlet weak var tableView: UITableView!
var dataSource = [LoadYourData]()
// MARK: - Init & Deinit
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: "YourViewController", bundle: Bundle.main)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
NotificationCenter.default.removeObserver(self)
}
override func viewDidLoad() {
super.viewDidLoad()
setupViewControllerUI()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
}
// MARK: - UIViewController Helper Methods
func setupViewControllerUI() {
tableView.estimatedRowHeight = 44.0
tableView.rowHeight = UITableViewAutomaticDimension
tableView.delegate = self
tableView.dataSource = self
loadData()
}
func loadData() {
// Write here your API and reload tableview once you get response
}
// MARK: - UITableView Data Source
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataSource.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = YourTableViewCell.cellForTableView(tableView: tableView, atIndexPath: indexPath)
// assign here cell.name etc from dataSource
return cell
}

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