Tab bar button throws NSException error, why? - swift

Issue
I am using Xcode 10, Swift 5. I tried to rename some variables and arrays. I changed them all back to the original name though. After I did this my tab bar button throws an error at AppDelegate: Thread 1: signal SIGABRT
console output:
2019-05-23 12:46:56.511809-0400 DIY Home Repair[1958:326215] * Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[ setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key videoImageView.'
* First throw call stack:
(0x1d8b30518 0x1d7d0b9f8 0x1d8a4dec0 0x1d94b5a00 0x2054fb6fc 0x204d31324 0x1d8a1df38 0x204d2df7c 0x2052753a4 0x104560b80 0x104560d7c 0x20528ea38 0x20528ef38 0x20525b740 0x205278a60 0x2054fbe54 0x1dcfbe1f0 0x1dcfc3198 0x2054e7e88 0x204a3abe4 0x204a35478 0x204a33744 0x2054b8994 0x2054b8c50 0x2054c6758 0x2054c2360 0x2054b86d8 0x204a3b9e8 0x204a3bf7c 0x204a3d210 0x204a20420 0x2054fbe54 0x1dcfbe1f0 0x1dcfc3198 0x1dcf260a8 0x1dcf54108 0x1dcf54cf8 0x1d8ac189c 0x1d8abc5c4 0x1d8abcb40 0x1d8abc354 0x1dacbc79c 0x205073b68 0x10455d750 0x1d85828e0)
libc++abi.dylib: terminating with uncaught exception of type NSException
(lldb)
Attempting to solve issue
I checked all of my segues, class names - they match controllers. I checked all of the code to ensure all the variables and arrays are accurate. I deleted the segue from the rootTabBarController to the ViewController and reconnected. I commented out all of the code to see if there was something in the viewController that was throwing this error. I cleaned the build folder and restarted Xcode. None of this worked.
Code for the viewController
Keep in mind, I commented this all out and added a blank viewDidLoad func. It still threw the NSExeption.
import UIKit
class ProductViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
var videos: [Video] = []
override func viewDidLoad() {
super.viewDidLoad()
videos = Video.createVideoArray()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "MasterToDetail" {
let destVC = segue.destination as! DetailViewController
destVC.video = sender as? Video
}
}
}
extension ProductViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return videos.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let video = videos[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "VideoCell") as! VideoCell
cell.setVideo(video: video)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let video = videos[indexPath.row]
performSegue(withIdentifier: "MasterToDetail", sender: video)
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
}

Related

Table View, View Controller, Table Cell nil error

I've created a ViewController with my Storyboard linked to it. I've done this before and it has worked fine. This time tho, when I use almost the exact same code for the table view and the table view cell, it returns a nil error when running. I am not sure what the reason exactly is.
//
// IngView1.swift
// Fake
//
// Created by Ian Dong on 11/16/20.
//
import UIKit
class IngView1: UIViewController, UITableViewDataSource {
#IBOutlet weak var display: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
display.dataSource = self
}
var data = ["Oil 5","Beef 3","Chicken 5","Potato 4","Cheese 3","Fish 3","Cabage 4"]
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "IngReUse")! // Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
let text = data[indexPath.row]
cell.textLabel?.text = text
return cell
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
}
Are you sure you properly set prototype cell's identifier? You can do this in the storyboard.
Or if you're not using prototype cell, you can register it in code
display.register(UITableViewCell.self, forCellReuseIdentifier: "IngReUse")

Nothing Happens When UITableView is Clicked (trying to send data between views)

So I have a two page app. The purpose of the app being the user can store expenses. They log a name and amount (attributes) and this data is stored in Expenses (entity). I have figured out how to create core data values, delete and retrieve. I am now working on updating. This will work by the user tapping on a table in the first view (ExpensesViewController) where the expenses are stored and this takes them to the 2nd view (EditExpensesViewController) where they can update the value back into core data. I am stuck on this 'data transfer' between the views.
I am using the storyboard and connected the first view to the second via 'show' set the segue identifier as 'editExpense'. However nothing happens when the table row is tapped. Any idea why it's not working and what I may have missed out? See here for GIF
ExpensesViewController
import UIKit
import CoreData
class ExpensesViewController: UIViewController {
#IBOutlet weak var totalLabel: UILabel!
#IBOutlet weak var tableView: UITableView!
var expenses_array = [Expenses]()
var send_array = [Expenses]()
override func viewDidLoad(){
super.viewDidLoad()
retrieveExpenses()
}
func retrieveExpenses(){
let fetchRequest: NSFetchRequest<Expenses> = Expenses.fetchRequest()
do {
let expenses = try PersistenceService.context.fetch(fetchRequest)
self.expenses_array = expenses
self.tableView.reloadData()
} catch {
print(error.localizedDescription )
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "editExpense") {
let secondViewController = segue.destination as! EditExpensesViewController
secondViewController.send_array = send_array
}
}
}
extension ExpensesViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return expenses_array.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .value1, reuseIdentifier: nil)
cell.textLabel?.text = expenses_array[indexPath.row].name
cell.detailTextLabel?.text = expenses_array[indexPath.row].amount
return cell
}
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if (editingStyle == .delete) {
let fetchRequest: NSFetchRequest<Expenses> = Expenses.fetchRequest()
do {
let result = try PersistenceService.context.fetch(fetchRequest)
// Delete from Core Data and remove from the arrays then save
if result.contains(expenses_array[indexPath.row]){
PersistenceService.context.delete(expenses_array[indexPath.row])
expenses_array = expenses_array.filter { $0 != expenses_array[indexPath.row] }
PersistenceService.saveContext()
self.getTotalExpenses()
self.tableView.reloadData()
}
} catch {
print(error.localizedDescription )
}
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
send_array = [self.expenses_array[indexPath.row]]
self.performSegue(withIdentifier: "editExpense", sender: self)
}
}
EditExpensesViewController
import UIKit
import CoreData
class EditExpensesViewController: UIViewController {
var send_array = [Expenses]() // Defined from the previous view controller
override func viewDidLoad() {
super.viewDidLoad()
print(send_array)
}
}
First of all conform to tableView delegates and dataSource in your viewDidLoad() :
tableView.delegate = self
tableView.dataSource = self
Delete segue from stroyboard and we will present the controller in code using :
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "editExpense") {
let secondViewController = segue.destination as! EditExpensesViewController
secondViewController.send_array = send_array
// "someIdentifier" is the identifier of secondController in storyboard
storyboard?.instantiateViewController(withIdentifier: "someIdentifier")
present(secondViewController, animated: true, completion: nil)
}
}
Be aware to put storyboard identifier for second controller in storyboard using attribute inspector
The problem is that your first view controller is the UITableViewDataSource only. That is not enough. It needs to be the UITableViewDelegate too. didSelectRowAt Is a delegate method, not a data source method, and will not be called unless this view controller is the table views delegate and is explicitly declared as conforming to UITableViewDelegate.

How to refresh a ViewTable from a separate ViewController

I'm trying to create a swift program with a main ViewController, and a secondary VC that controls a pop up that overlays the screen. In this pop up, you can enter in login credentials, which will display in a tableView in the main ViewController.
So far I'm able to save the entered values in the secondary VC to core data, but I'm not able to reloadData() for the tableView in the first VC, because it just cant see the reloadData() function (must not be a public function? Not really sure, as I'm new to swift).
Could someone help me figure out the best way to go about doing this?
Main ViewController code:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
var account = [Account]()
#IBAction func onAddAccount(_ sender: Any) {
self.tableView.reloadData()
print("RELOADED")
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
#IBAction func onBlank() {
self.tableView.reloadData()
}
}
extension ViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return account.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .subtitle, reuseIdentifier: nil)
cell.textLabel?.text = account[indexPath.row].email
cell.detailTextLabel?.text = account[indexPath.row].password
return cell
}
}
I am relatively new to swift myself. But I think what you are looking for is a Delegate will tell the main controller that the data has been updated and what it's been updated too. It also seems you might need a datasource? So that you can implement model, view, controller architecture.

Data not passing in Swift

I'm working on a journal app and now I want people to be able to click on a cell/entry and then go to a more detailed view where they can read/edit their entry. The thing is, the strings I'm trying to pass to the next view aren't coming through.
Edit:
This is the code on the TableViewController:
class TableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
var items: [Entry] = []
var passedEntry:String = ""
func fetchData() {
do {
items = try context.fetch(Entry.fetchRequest())
print(items)
DispatchQueue.main.async {
self.tableView.reloadData()
}
} catch {
print("Couldn't fetch data")
}
}
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as UITableViewCell
let date = items.reversed()[indexPath.row].date
cell.textLabel?.text = items[indexPath.row].title
if let date = date {
let dateStamp = "Added on \(date)"
cell.detailTextLabel?.text = dateStamp
}
return cell
}
#objc(tableView:editActionsForRowAtIndexPath:) func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
let delete = UITableViewRowAction(style: .default, title: "Delete") { (action, indexPath) in
// delete item at indexPath
let item = self.items[indexPath.row]
self.context.delete(item)
(UIApplication.shared.delegate as! AppDelegate).saveContext()
self.items.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .fade)
}
return [delete]
}
public func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?){
if (segue.identifier == "showEntry") {
// initialize new view controller and cast it as your view controller
let viewController = EntryViewController()
// your new view controller should have property that will store passed value
viewController.passedEntry = passedEntry
}
}
public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
passedEntry = items[indexPath.row].title!
self.performSegue(withIdentifier: "showEntry", sender: self)
}
override func viewWillAppear(_ animated: Bool) {
fetchData()
}
This is the code on the EntryViewController which still prints nil:
class EntryViewController: UIViewController {
var passedEntry:String!
#IBOutlet weak var dreamTitle: UITextView!
#IBOutlet weak var dreamPost: UITextView!
#IBAction func saveChanges(_ sender: Any) {
}
override func viewWillAppear(_ animated: Bool) {
print(passedEntry)
}
I'm sorry for being a newbie, but I need some more detailed instructions...
Two issues:
The signature of prepareForSegue is wrong for Swift 3.
Never create view controllers with the default initializer when using storyboard
This main issue occurs in this line:
let viewController = EntryViewController()
You are creating a brand new instance of EntryViewController which is not the controller in the storyboard. You have to use the destination view controller of the segue:
func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showEntry" {
// initialize new view controller and cast it as your view controller
let viewController = segue.destination as! EntryViewController
// your new view controller should have property that will store passed value
let title = items[indexPath.row].title
viewController.passedEntry = title
}
}
And I recommend to declare the string in the destination view controller as non-optional empty string rather than an implicit unwrapped optional. Non-optional types will never crash the app.
var passedEntry = ""
You are defining prepareForSegue() as a local function inside the tableView-function. It will not be used. Put a debugger breakpoint into it and see yourself if it will be reached.

SIGABRT Error at App Delegate during runtime

I am getting the following error
libc++abi.dylib: terminating with uncaught exception of type NSException
The problem comes when I press the Unbutton on the main storyboard. It does not "show segue" as directed.
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
return true
}
}
My Home storyboard class is:
import UIKit
class Home: UITableViewController {
var array = ["item1, item2", "item3"]
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return array.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("customcell")
as! TableviewCell
cell.textLabel?.text = array[indexPath.item]
cell.Title = cell.textLabel?.text!
return cell
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "datasegue"){
let cell = sender as! TableviewCell
let detailview = segue.destinationViewController as! DetailViewController
detailview.PreTitile = cell.Title
}
}