Hi Everyone,
I am working on creating a journal style app in core data. I am having issues with updating views when the core data entries are changed and looking for some help.
I have 3 view controllers (VC 1 is a table view of all the journal entries; VC 2 is a table view with a single cell entry displaying the different entry data; VC 3 is a table view with a single cell where the entries are text fields so they can be changed). User clicks on a cell in VC 1 and it presents VC 2 which is a static display of the data. It passes in the entry that the user tapped on VC1 into VC2 to populate the data. There is an edit button that the user taps which displays VC 3. When the user is in VC 3 they can edit any of the entry attributes. On VC 3 there is a save button which saves the data to Core Data and dismisses VC 3 showing the user VC 2. When VC 2 reappears, none of the updated data is there. I have to close the app and re-navigate to VC 2 to see the data.
I know in SwiftUI I could use @Environment object but what about UI Kit? I’ve tried delegates and protocols and haven’t had any luck. I think the issue is that the data in VC 2 is being configured and passed in from VC 1.
My problem is that when VC 3 dismisses, the values in VC2 do not update. Can anyone please offer some guidance?
Thank you!
Code below for 3 separate view controllers:
import UIKit
import CoreData
class TableViewController1: UITableViewController {
var entry: JournalEntry?
var journalEntries: [JournalEntry] = []
private let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
var fetchedEntryRC:NSFetchedResultsController<JournalEntry>?
private let appDelegate = UIApplication.shared.delegate as! AppDelegate
@IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Set datasource and delegate for the table
tableView.delegate = self
tableView.dataSource = self
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// Get a reference to VC2
let VC2 = storyboard?.instantiateViewController(identifier: Constants.Storyboard.VC2) as! TableViewController2
// Get the entry for this indexpath and pass to VCc
let entry = indexPath.row
VC2.delegate = self
VC2.entry = entry
VC2.modalPresentationStyle = .overFullScreen
present(VC2, animated: true) {
print("presented")
}
}
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return journalEntries.count
}
func refresh() {
// Get a fetch request for ShaveEntries
let request:NSFetchRequest<JournalEntry> = JournalEntry.fetchRequest()
// Set a sort descriptor
let sortByDate = NSSortDescriptor(key: "date", ascending: false)
// Sort the data by the sort descriptors
request.sortDescriptors = [sortByDate]
do {
// Exectute the fetch
journalEntries = try context.fetch(request)
}
catch {
print("Couldn't fetch entries")
}
// Tell tableview to request data
tableView.reloadData()
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Create a reusable cell
let cell = tableView.dequeueReusableCell(withIdentifier: Constants.Storyboard.ENTRY_CELL) as! EntryCell
let entry = indexPath.row
// Pass the entry to the cell View and configure it
cell.setCell(entry)
return cell
} //: cellForRowAt
extension TableViewController1: EntryModifiedProtocol {
func entryChanged() {
refresh()
}
}
}
protocol EntryModifiedProtocol {
func entryChanged()
}
class TableViewController2: UITableViewController {
private let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
var fetchedEntryRC:NSFetchedResultsController<ShaveEntry>?
private let appDelegate = UIApplication.shared.delegate as! AppDelegate
// Variables
var entry: JournalEntry?
var delegate:EntryModifiedProtocol?
// Text Outlets
@IBOutlet weak var dateLabel: UILabel!
@IBOutlet weak var nameLabel: UILabel!
@IBOutlet weak var editButton: UIButton!
@IBOutlet weak var backButton: UIButton!
@IBOutlet weak var notesTV: UITextView!
@IBOutlet var entryTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
entryTableView.dataSource = self
entryTableView.delegate = self
} //: viewDidLoad
override func viewWillAppear(_ animated: Bool) {
if entry !== nil && entry?.date != nil {
// Configure the entry fields
configureLabels(entry: entry!)
}
}
func formatDate(_ date:Date) -> String {
let date = entry!.date
let df = DateFormatter()
df.dateFormat = "MM/dd/yyyy"
return df.string(from: date!)
}
func configureLabels(entry:ShaveEntry) {
dateLabel.text = formatDate(entry.date!)
nameLabel.text = entry.name
notesTV.text = entry.notes
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
@IBAction func editTapped(_ sender: Any) {
let VC3 = storyboard?.instantiateViewController(identifier: Constants.Storyboard.VC3) as! TableViewController3
VC3.entry = entry
VC3.delegate = self
VC3.modalPresentationStyle = .overFullScreen
present(VC3, animated: true, completion: nil)
}
@IBAction func backTapped(_ sender: Any) {
dismiss(animated: true) {
print("dismissed")
}
}
}
protocol ChangedEntryDelegate {
func EntryChanged()
}
class TableViewController3: UITableViewController, UITextFieldDelegate{
var delegate:ChangedEntryDelegate?
//References to App delegate and Core Data context
private let aDelegate = UIApplication.shared.delegate as! AppDelegate
private let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
// Variables
var entry: JournalEntry?
@IBOutlet weak var datePicker: UIDatePicker!
// Button Outlets
@IBOutlet weak var cancelButton: UIButton!
@IBOutlet weak var saveButton: UIButton!
// Text Field Outlets
@IBOutlet weak var nameTF: UITextField!
@IBOutlet weak var notesTV: UITextView!
@IBOutlet weak var scrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
// Set date picker to compact calendar and date only
datePicker.preferredDatePickerStyle = .compact
datePicker.datePickerMode = .date
if entry != nil && entry?.date != nil {
// This is an exising entry, so need to present the data for that entry
datePicker.date = entry!.date!
nameTF.text = entry!.name
notesTV.text = entry!.notes
}
// Set the textfield delegates to self
nameTF.delegate = self
}
@IBAction func saveTapped(_ sender: Any) {
// Check if creating a new entry or edititng an exiting entry
if entry == nil {
// Create a new entry
let e = JournalEntry(context: context)
// Configure the properties
e.date = datePicker.date
e.name = nameTF.text
e.notes = notesTV.text
}
else {
// Editing an existing entry, so update values (entry != nil, so can force unwrap entry)
entry!.date = datePicker.date
entry!.name = nameTF.text
entry!.notes = notesTV.text
} //: Else
// Save the core data context
aDelegate.saveContext()
// Let the delegate know that the entry was added
delegate?.EntryChanged()
// Dismiss the popup
dismiss(animated: true, completion: nil)
}
}