NSManagedObject Array becomes nil when UITableView is scrolled - ios

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

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

My custom cells are not showing up in my tableview

So I have been trying to get my custom cells to show up on this tableview, but I am not sure as to why they are not showing up
I have already checked other stack overflow questions and tried their fixes, to no avail. Please ignore the aws stuff as you can see I have the text hard coded so I can just get them to appear for now.
This is the code within the class holding the tableview
import Foundation
import AWSDynamoDB
import AWSCognitoIdentityProvider
import UIKit
// this will be the main feed class showing the user data
class UserDetailTableViewController : UITableViewController {
// attributes for the custome cell
#IBOutlet weak var testing: UITextField!
#IBOutlet var Table: UITableView!
var response: AWSCognitoIdentityUserGetDetailsResponse?
var user: AWSCognitoIdentityUser?
var pool: AWSCognitoIdentityUserPool?
var questiondata : Array<Phototext> = Array()
override func viewDidLoad() {
tableView.delegate = self
tableView.dataSource = self
super.viewDidLoad()
self.pool = AWSCognitoIdentityUserPool(forKey: AWSCognitoUserPoolsSignInProviderKey)
if (self.user == nil) {
self.user = self.pool?.currentUser()
}
// grabbing data from our aws table
updateData()
self.refresh()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.navigationController?.setToolbarHidden(true, animated: true)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.setToolbarHidden(false, animated: true)
}
#IBAction func Questions(_ sender: Any) {
performSegue(withIdentifier: "ask", sender: self)
}
// MARK: - IBActions
#IBAction func signOut(_ sender: AnyObject) {
self.user?.signOut()
self.title = nil
self.response = nil
self.refresh()
}
// reloads the prior view
func refresh() {
self.user?.getDetails().continueOnSuccessWith { (task) ->
AnyObject? in
DispatchQueue.main.async(execute: {
self.response = task.result
self.title = self.user?.username
// saving the user name from the main menu
username123 = self.user?.username! ?? "broken"
})
return nil
}
}
// function that calls to our aws dynamodb to grab data from the
// user
//and re update questions
// the array list
func updateData(){
let scanExpression = AWSDynamoDBScanExpression()
scanExpression.limit = 20
// testing to grabt the table data upon startup
let dynamoDBObjectMapper = AWSDynamoDBObjectMapper.default()
dynamoDBObjectMapper.scan(Phototext.self, expression:
scanExpression).continueWith(block: {
(task:AWSTask<AWSDynamoDBPaginatedOutput>!) -> Any? in
if let error = task.error as NSError? {
print("The request failed. Error: \(error)")
} else if let paginatedOutput = task.result {
// passes down an array of object
for Photo in paginatedOutput.items as! [Phototext] {
// loading in the arraylist of objects
// adding the objects to an arraylist
self.questiondata.append(Photo)
}
DispatchQueue.main.async {
//code for updating the UI
}
}
return ()
})
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// returning the number of rows
return 3
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath:
IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier:
"Questionpost", for: indexPath) as! QuestionCell
cell.QuestionText.text = "call it"
cell.Subject.text = "a day"
return cell
}
}
}
Here is the code for the QuestionCell class
import UIKit
class QuestionCell: UITableViewCell {
#IBOutlet weak var Subject: UILabel!
#IBOutlet weak var QuestionText: UITextView!
}
The cell class is called QuestionCell and the identifier I left on the cell in the storyboard is Questionpost
Here is a photo of my story board:
I have fixed it by declaring an extension with the proper types.
extension UserDetailTableViewController: UITableViewDataSource,UITableViewDelegate{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// returning the number of rows
return 3
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Questionpost", for: indexPath) as! QuestionCell
cell.QuestionText.text = "call it"
cell.Subject.text = "a day"
return cell
}}
good explanation of what's going on, you need to conform to the UITableViewDataSource and UITableViewDelegate when you inbed a tableview.
Redundant conformance of TableView to protocol UITableViewDataSource with Xib Files

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

pass data when clicking the cell in mvvm

I need to select the cell from tableview and my code as below:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath){
let vc = OfferDetailViewController(nibName: "OfferDetailViewController", bundle: nil)
let indexPath = (self.tableView.indexPathForSelectedRow)!
let offerViewModel1 = self.offerViewModel.sourceAt(index: indexPath.row)
vc.offerViewModel2 = offerViewModel1
navigationController?.pushViewController(vc, animated: true)
}
I am doing in the mvvm method. I have two screens.
listing the data in the tableview
on selecting the cell from tableview, it must go to other screen. It contains the details.
I have done to list the data. But on clicking I need to pass he pass. How to do?
I can't understanding did it should pass to viewmodel or viewcontroller.
foodviewmodel:-
class foodViewModel: NSObject {
var datasourceModel:foodDataSource
init(withdatasource offerDatasourceModel: foodDataSource) {
datasourceModel = offerDatasourceModel
}
func datafordisplay(atindex indexPath: IndexPath) -> foodModel{
return datasourceModel.dataListArray![indexPath.row]
}
func numberOfRowsInSection(section:Int) -> Int {
return (datasourceModel.dataListArray?.count)!
}
//indexpath.....
// func sourceAt(atindex indexPath: IndexPath) -> foodModel {
// return self.datasourceModel.dataListArray![indexPath.row]
// }
func sourceAt(index :Int) -> foodModel {
return self.datasourceModel.dataListArray![index]
}
}
foodDataSource .swift:-
var dataListArray:Array<foodModel>? = []
init(array :Array<[String:Any]>?) {
super.init()
var newArray:Array<[String:Any]> = []
if array == nil{
newArray = self.getJsonDataStored4()
}
else{
newArray = array!
}
var datalist:Array<foodModel> = []
for dict in newArray{
let model = foodModel(dictionary: dict)
datalist.append(model!)
}
self.dataListArray = datalist
}
}
typealias dummyDataSource4 = foodDataSource
extension dummyDataSource4{
func getJsonDataStored4() ->Array<Dictionary<String,String>>{
let jsonArray = [["id":"1","name":"Lunch Buffet","price":"Q28","location":"Doha, Qatar","imageurl":"","offertype":"Today's Offer","restaurtantname":"MOMS KITCHEN"],["id":"2","name":"Economy Meal","price":"Q65","location":"Doha, Qatar","imageurl":"","offertype":"Tomorrow's Offer","restaurtantname":"Mr.Shawarma"],["id":"3","name":"Sit-Down Buffet","price":"Q65","location":"Doha, Qatar","imageurl":"","offertype":"Weekend's Offer","restaurtantname":"Maharaja Darbar"],["id":"4","name":"Lunch Buffet","price":"Q28","location":"Doha, Qatar","imageurl":"","offertype":"Today's Offer","restaurtantname":"Shircz Garden"]] as Array<Dictionary<String,String>>
return jsonArray
}
Then i created fooddetailmodel,fooddetaildatasource,fooddetailviewmodel classes.
But i don't know what to pass
i have given the code:-
var offerViewModel2 :QM_OfferModel!
// #IBOutlet private weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}

App not deleting a row from .plist file

In a project that I am working on I have note-esque function that is acting as an Exercise/Training Log. This Training Log is made up of 5 files: Note.swift, NotesTableViewController.swift, NoteDetailViewController.swift, NoteDetailTableViewCell.swift, and NoteStore.swift. The class for this table is NotesTableViewController, which is a UIViewController with UITableViewDelegate, and UITableViewDataSource. This note taking feature works decently, populating the tableview, but fails to delete a note from the .plist file and continues to retrieve it when the app is reopened. I do not know if this is actually failure to save/load, or if something is going wrong somewhere else. I would appreciate any help at all. The files are as follows:
Note.swift
import Foundation
class Note : NSObject, NSCoding {
var title = ""
var text = ""
var date = NSDate() // Defaults to current date / time
// Computed property to return date as a string
var shortDate : NSString {
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "MM/dd/yy"
return dateFormatter.stringFromDate(self.date)
}
override init() {
super.init()
}
// 1: Encode ourselves...
func encodeWithCoder(aCoder: NSCoder) {
aCoder.encodeObject(title, forKey: "title")
aCoder.encodeObject(text, forKey: "text")
aCoder.encodeObject(date, forKey: "date")
}
// 2: Decode ourselves on init
required init(coder aDecoder: NSCoder) {
self.title = aDecoder.decodeObjectForKey("title") as! String
self.text = aDecoder.decodeObjectForKey("text") as! String
self.date = aDecoder.decodeObjectForKey("date") as! NSDate
}
}
NotesTableViewController.swift
import UIKit
class NotesTableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var OpenButton: UIBarButtonItem!
override func viewDidLoad() {
super.viewDidLoad()
// Leverage the built in TableViewController Edit button
self.navigationItem.leftBarButtonItem = self.editButtonItem()
OpenButton.target = self.revealViewController()
OpenButton.action = Selector("revealToggle:")
self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
self.tableView.reloadData()
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
// ensure we are not in edit mode
editing = false
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Here we pass the note they tapped on between the view controllers
if segue.identifier == "NoteDetailPush" {
// Get the controller we are going to
var noteDetail = segue.destinationViewController as! NoteDetailViewController
// Lookup the data we want to pass
var theCell = sender as! NoteDetailTableViewCell
// Pass the data forward
noteDetail.theNote = theCell.theNote
}
}
#IBAction func saveFromNoteDetail(segue:UIStoryboardSegue) {
// We come here from an exit segue when they hit save on the detail screen
// Get the controller we are coming from
var noteDetail = segue.sourceViewController as! NoteDetailViewController
// If there is a row selected....
if let indexPath = tableView.indexPathForSelectedRow() {
// Update note in our store
NoteStore.sharedNoteStore.updateNote(theNote: noteDetail.theNote)
// The user was in edit mode
tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
} else {
// Otherwise, add a new record
NoteStore.sharedNoteStore.createNote(theNote: noteDetail.theNote)
// Get an index to insert the row at
var indexPath = NSIndexPath(forRow: NoteStore.sharedNoteStore.count()-1, inSection: 0)
// Update tableview
tableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
}
}
// MARK: - Table view data source
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Just return the note count
return NoteStore.sharedNoteStore.count()
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// Fetch a reusable cell
let cell = tableView.dequeueReusableCellWithIdentifier("NoteDetailTableViewCell", forIndexPath: indexPath) as! NoteDetailTableViewCell
// Fetch the note
var rowNumber = indexPath.row
var theNote = NoteStore.sharedNoteStore.getNote(rowNumber)
// Configure the cell
cell.setupCell(theNote)
return cell
}
// Override to support editing the table view.
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
// Delete the row from the data source
NoteStore.sharedNoteStore.deleteNote(indexPath.row)
// Delete the note from the tableview
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
}
}
}
NoteDetailViewController
import UIKit
class NoteDetailViewController: UIViewController {
var theNote = Note()
#IBOutlet weak var noteTitleLabel: UITextField!
#IBOutlet weak var noteTextView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
// The view starts here. By now we either have a note to edit
// or we have a blank note in theNote property we can use
// Update the screen with the contents of theNote
self.noteTitleLabel.text = theNote.title
self.noteTextView.text = theNote.text
// Set the Cursor in the note text area
self.noteTextView.becomeFirstResponder()
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Whenever we leave the screen, update our note model
theNote.title = self.noteTitleLabel.text
theNote.text = self.noteTextView.text
}
#IBAction func CancelNote(sender: AnyObject) {
self.dismissViewControllerAnimated(true, completion: nil)
}
}
NoteDetailTableViewCell
import UIKit
class NoteDetailTableViewCell : UITableViewCell {
// The note currently being shown
weak var theNote : Note!
// Interface builder outlets
#IBOutlet weak var noteTitleLabel : UILabel!
#IBOutlet weak var noteDateLabel : UILabel!
#IBOutlet weak var noteTextLabel : UILabel!
// Insert note contents into the cell
func setupCell(theNote:Note) {
// Save a weak reference to the note
self.theNote = theNote
// Update the cell
noteTitleLabel.text = theNote.title
noteTextLabel.text = theNote.text
noteDateLabel.text = theNote.shortDate as String
}
}
and finally, NoteStore
import Foundation
class NoteStore {
// Mark: Singleton Pattern (hacked since we don't have class var's yet)
class var sharedNoteStore : NoteStore {
struct Static {
static let instance : NoteStore = NoteStore()
}
return Static.instance
}
// Private init to force usage of singleton
private init() {
load()
}
// Array to hold our notes
private var notes : [Note]!
// CRUD - Create, Read, Update, Delete
// Create
func createNote(theNote:Note = Note()) -> Note {
notes.append(theNote)
return theNote
}
// Read
func getNote(index:Int) -> Note {
return notes[index]
}
// Update
func updateNote(#theNote:Note) {
// Notes passed by reference, no update code needed
}
// Delete
func deleteNote(index:Int) {
notes.removeAtIndex(index)
}
func deleteNote(withNote:Note) {
for (i, note) in enumerate(notes) {
if note === withNote {
notes.removeAtIndex(i)
return
}
}
}
// Count
func count() -> Int {
return notes.count
}
// Mark: Persistence
// 1: Find the file & directory we want to save to...
func archiveFilePath() -> String {
let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
let documentsDirectory = paths.first as! NSString
let path = documentsDirectory.stringByAppendingPathComponent("NoteStore.plist")
return path
}
// 2: Do the save to disk.....
func save() {
NSKeyedArchiver.archiveRootObject(notes, toFile: archiveFilePath())
}
// 3: Do the reload from disk....
func load() {
let filePath = archiveFilePath()
let fileManager = NSFileManager.defaultManager()
if fileManager.fileExistsAtPath(filePath) {
notes = NSKeyedUnarchiver.unarchiveObjectWithFile(filePath) as! [Note]
} else {
notes = [Note]()
}
}
}
it look's like you're not calling the save method after changing creating,deleting or updating notes
you could add for example :
func deleteNote(index:Int) {
notes.removeAtIndex(index)
save()
}
or call the save methods on vievWillDisappear if you don't want to write a new plist after every change

Resources