I keep getting error after I added UISearchController in tableview - swift

I keep getting this message fatal error: unexpectedly found nil while unwrapping an Optional value
(lldb)
on this line(marked with */ */)
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
*/ if resultSearchController.active */
{
return self.filtered.count
}
else
{
return self.entries.count
}
}
The table view is loaded from coreData
class NotesListTableViewController: UITableViewController ,UISearchResultsUpdating {
var managedObjectContext : NSManagedObjectContext!
var entries: [NSManagedObject]!
var filtered :[NSManagedObject] = []
var resultSearchController:UISearchController!
entries is used to load from coredata and filtered is the used to store data after searching
can anybody please help me with this

The issue is that resultSearchController is nil.
You can either, change the ! on the property to a ?, then the compiler will force you to handle the case where it might be nil whenever you try to access it. Or, you can review why it is nil and fix that. For example, are you never creating it in the first place?

Related

NSTableView not appearing at all

I've just started working on my first project for macOS and am having trouble setting up a NSTableView. When I run it the window will appear but there is nothing in it. I've made sure all the objects have the correct class in the identity inspector and can't seem to find what I'm doing wrong.
The goal of the app is to make a notes app. I want a tableView which displays the titles of all the notes in the database, in a single column, so when you click on the cell the note will then be displayed in the rest of the window.
Here's the code:
import Foundation
import AppKit
import SQLite
class NoteCloudVC: NSViewController {
// Declare an array of Note objects for populating the table view
var notesArray: [Note] = []
// IBOutlets
#IBOutlet weak var tableView: NSTableView!
// ViewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
// set the tableViews delegate and dataSource to self
tableView.delegate = self
tableView.dataSource = self
//Establsih R/W connection to the db
do {
let path = NSSearchPathForDirectoriesInDomains(
.applicationSupportDirectory, .userDomainMask, true
).first! + "/" + Bundle.main.bundleIdentifier!
// create parent directory iff it doesn’t exist
try FileManager.default.createDirectory(
atPath: path,
withIntermediateDirectories: true,
attributes: nil
)
let db = try Connection("\(path)/db.sqlite3")
//Define the Notes Table and its Columns
let notes = Table("Notes")
let id = Expression<Int64>("ID")
let title = Expression<String>("Title")
let body = Expression<String>("Body")
/*
Query the data from NotesAppDB.sqlite3 into an array of Note objs
Then use that array to populate the NSTableView
*/
for note in try db.prepare(notes) {
let noteToAdd = Note(Int(note[id]), note[title], note[body])
notesArray.append(noteToAdd)
}
} catch {
print(error)
}
}
// viewWillAppear
override func viewWillAppear() {
super.viewWillAppear()
tableView.reloadData()
}
}
// NSTableViewDataSource Extension of the NoteCloudVC
extension NoteCloudVC: NSTableViewDataSource {
// Number of rows ~ returns notesArray.count
func numberOfRows(in tableView: NSTableView) -> Int {
return notesArray.count
}
}
// NSTableViewDelegate extension of the NoteCloudVC
extension NoteCloudVC: NSTableViewDelegate {
// Configures each cell to display the title of its corresponding note
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
//configure the cell
if tableColumn?.identifier == NSUserInterfaceItemIdentifier(rawValue: "NotesColumn") {
let cellIdentifier = NSUserInterfaceItemIdentifier(rawValue: "NotesCell")
guard let noteCell = tableView.makeView(withIdentifier: cellIdentifier, owner: self) as? NotesCell else { return nil }
let note = notesArray[row]
noteCell.noteTitle.stringValue = note.title
return noteCell
}
return nil
}
}
// NotesCell class
class NotesCell: NSTableCellView {
// IBOutlet for the title
#IBOutlet weak var noteTitle: NSTextField!
}
I'm pretty familiar with UIKit so I thought the learning curve of AppKit would be a little better than SwiftUI, so if anyone could provide some guidance about where I've gone wrong that would be very much appreciated. Also if it will be a better use of my time to turn towards SwiftUI please lmk.
Here's the values while debugging:
It's reading the values from the table correctly, so I've at least I know the problem lies somewhere in the tableView functions.
The most confusing part is the fact that the header doesn't even show up. This is all I see when I run it:
Here are some images of my storyboard as well:
This is for an assignment for my software modeling and design class where my professor literally doesn't teach anything. So I'm very thankful for everyone who helps with this issue because y'all are basically my "professors" for this class. When I move the tableView to the center of the view controller in the story board I can see a little dash for the far right edge of the column but that's it, and I can't progress any further without this tableView because the whole app is dependant upon it.
So, it turns out that the code itself wasn't actually the problem. I had always used basic swift files when writing stuff for iOS so it never occured to me that I'd need to import Cocoa to use AppKit but that's where the problem lied all along. Using this code inside the auto-generated ViewController class that had Cocoa imported did the trick. Also I got rid of the extensions and just did all the Delegate/ DataSource func's inside the viewController class.

Explicitly unwrapping optional nil does not cause crash

A few folks asked this question before, yet no answer was accepted.
I have a UITableViewCell that contains a UITextField.
If I click slightly outside of the textField the row highlights. I want to prevent this.
To prevent it I do the following:
override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath! {
return nil
}
This works perfectly fine. Here is my question. The author of the tutorial states:
Note that returning nil from a method is only allowed if there is a
question mark (or exclamation point) behind the return type.
What does he mean that you can put an exclamation mark behind the optional return type? Why doesn't the program crash? Returning nil after I place an exclamation mark after the IndexPath return type doesn't crash. I thought ! would explicitly unwrap the nil and fail?
As of Swift 3, “Implicitly unwrapped optional” is not a separate type,
but an attribute on the declaration of a regular/strong optional.
For the details, see SE-0054 Abolish ImplicitlyUnwrappedOptional type.
A function with an IUO return type can return nil,
and assigning the return value to a variable makes that a regular
optional:
func foo() -> Int! {
return nil
}
let x = foo() // Type of `x` is `Int?`
print(x) // nil
Only if evaluation as an optional is not possible then the value
will be forced-unwrapped (and cause a runtime exception is the
value is nil):
let y = 1 + foo() // Fatal error: Unexpectedly found nil while unwrapping an Optional value
In your case, your
override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath!
method overrides the UITableViewController method
func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath?
and can return nil. This does not crash unless the caller unwraps
the value.
Remark: The above is meant as an explanation why your code compiles
and works. Generally, I do not see a good reason to use implicitly
unwrapped optional return types. The main use-cases of IUOs are
stated in SE-0054:
The ImplicitlyUnwrappedOptional ("IUO") type is a valuable tool for importing Objective-C APIs where the nullability of a parameter or return type is unspecified. It also represents a convenient mechanism for working through definite initialization problems in initializers.
One way to think this as a choise of the API Implementor. If implementor handles the input arguments it will not be any problem to the API User.
Lets create a drawing text class which just prints at console.
class TextDrawer {
var mustBeText: String!
func renderText(string: String) {
print(string)
}
func renderSafely() {
renderText(string: self.mustBeText ?? "Nothing found to be rendered")
// or
if let safeString = self.mustBeText {
renderText(string: safeString)
}
}
func renderUnsafely() {
renderText(string: mustBeText)
}
}
We have defined the mustBeText as String! means we are allowed to expect any string as well as nil argument.
Now, as we create a instance of the class as below:
let textDrawer = TextDrawer()
textDrawer.mustBeText = nil // this is allowed since the `mustBeText` is `String!`.
textDrawer.renderSafely() // prints -- Nothing found to be rendered
textDrawer.renderUnsafely() // crashes at runtime.
The renderUnsafaly() will crash since its not handling the nil case.

Private IBOutlets Swift

I know that our IBOutlets should be private, but for example if I have IBOutlets in TableViewCell, how should I access them from another ViewController? Here is the example why I'm asking this kind of question:
class BookTableViewCell: UITableViewCell {
#IBOutlet weak private var bookTitle: UILabel!
}
if I assign to the IBOutlet that it should be private, I got an error in another ViewController while I'm accessing the cell property: 'bookTitle' is inaccessible due to 'private' protection level
If I understand your question correctly, you are supposing the #IBOutlet properties should be marked as private all the time... Well it's not true. But also accessing the properties directly is not safe at all. You see the ViewControllers, TableViewCells and these objects use Implicit unwrapping on optional IBOutlets for reason... You don't need to init ViewController when using storyboards or just when using them somewhere in code... The other way - just imagine you are creating VC programmatically and you are passing all the labels to the initializer... It would blow your head... Instead of this, you come with this in storyboard:
#IBOutlet var myLabel: UILabel!
this is cool, you don't need to have that on init, it will just be there waiting to be set somewhere before accessing it's value... Interface builder will handle for you the initialization just before ViewDidLoad, so the label won't be nil after that time... again before AwakeFromNib method goes in the UITableViewCell subclass, when you would try to access your bookTitle label property, it would crash since it would be nil... This is the tricky part about why this should be private... Otherwise when you know that the VC is 100% on the scene allocated there's no need to be shy and make everything private...
When you for example work in prepare(for segue:) method, you SHOULD NEVER ACCESS THE #IBOutlets. Since they are not allocated and even if they were, they would get overwritten by some internal calls in push/present/ whatever functions...
Okay that's cool.. so what to do now?
When using UITableViewCell subclass, you can safely access the IBOutlets (ONLY IF YOU USE STORYBOARD AND THE CELL IS WITHIN YOUR TABLEVIEW❗️)
and change their values... you see
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// We shouldn't return just some constructor with UITableViewCell, but who cares for this purposes...
guard let cell = tableView.dequeueReusableCell(withIdentifier: "bookTableViewCell", for: indexPath) else { return UITableViewCell() }
cell.bookTitle.text = "any given text" // This should work ok because of interface builder...
}
The above case should work in MVC pattern, not MVVM or other patterns where you don't use storyboards with tableViewControllers and embed cells too much... (because of registering cells, but that's other article...)
I will give you few pointers, how you can setup the values in the cell/ViewController without touching the actual values and make this safe... Also good practice (safety) is to make the IBOutlets optional to be 100% Safe, but it's not necessary and honestly it would be strange approach to this problem:
ViewControllers:
class SomeVC: UIViewController {
// This solution should be effective when those labels could be marked weak too...
// Always access weak variables NOT DIRECTLY but with safe unwrap...
#IBOutlet var titleLabel: UILabel?
#IBOutlet var subtitleLabel: UILabel?
var myCustomTitle: String?
var myCustomSubtitle: String?
func setup(with dataSource: SomeVCDataSource ) {
guard let titleLabel = titleLabel, let subtitleLabel = subtitleLabel else { return }
// Now the values are safely unwrapped and nothing can crash...
titleLabel.text = dataSource.title
subtitleLabel.text = dataSource.subtitle
}
// WHen using prepare for segue, use this:
override func viewDidLoad() {
super.viewDidLoad()
titleLabel.text = myCustomTitle
subtitleLabel.text = myCustomSubtitle
}
}
struct SomeVCDataSource {
var title: String
var subtitle: String
}
The next problem could be this:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let destinationVC = segue.destination as? SomeVC else { return }
let datasource = SomeVCDataSource(title: "Foo", subtitle: "Bar")
// This sets up cool labels... but the labels are Nil before the segue occurs and even after that, so the guard in setup(with dataSource:) will fail and return...
destinationVC.setup(with: datasource)
// So instead of this you should set the properties myCustomTitle and myCustomSubtitle to values you want and then in viewDidLoad set the values
destinationVC.myCustomTitle = "Foo"
destinationVC.myCustomSubtitle = "Bar"
}
You see, you don' need to set your IBOutlets to private since you never know how you will use them If you need any more examples or something is not clear to you, ask as you want... Wish you happy coding and deep learning!
You should expose only what you need.
For example you can set and get only the text property in the cell.
class BookTableViewCell: UITableViewCell {
#IBOutlet weak private var bookTitleLabel: UILabel!
var bookTitle: String? {
set {
bookTitleLabel.text = newValue
}
get {
return bookTitleLabel.text
}
}
}
And then, wherever you need:
cell.bookTitle = "It"
Now outer objects do not have access to bookTitleLabel but are able to change it's text content.
What i usually do is configure method which receives data object and privately sets all it's outlets features.
I haven't come across making IBOutlets private to be common, for cells at least. If you want to do so, provide a configure method within your cell that is not private, which you can pass values to, that you want to assign to your outlets. The function within your cell could look like this:
func configure(with bookTitle: String) {
bookTitle.text = bookTitle
}
EDIT: Such a function can be useful for the future, when you change your cell and add new outlets. You can then add parameters to your configure function to handle those. You will get compiler errors everywhere, where you use that function, which allows you to setup your cell correctly wherever you use it. That is helpful in a big project that reuses cells in different places.

Swift Promises - Reload Data method is not binding the data

I'm using promises to retrieve information some methods via JSON. I'm filling the information with the function:
I'm trying to set those records in my TableView with:
#IBOutlet weak var usersTableView: UITableView!
var dataSource: [UserResponse]? {
didSet {
self.usersTableView.reloadData()
}
}
override func viewDidLoad() {
super.viewDidLoad()
loadFriends()
// Do any additional setup after loading the view.
}
func loadFriends() {
//UserService.getFriends()
let (request, promise) = UserService.getFriends()
promise.then { user in
self.dataSource = user
}.catch{
error in
SCLAlertView().showError("Error", subTitle: error.localizedDescription)
}
}
But is returning the error:
fatal error: unexpectedly found nil while unwrapping an Optional value
How I can fix this error?
Your usersTableView is an implicitly unwrapped optional (UITableView!). When you call the dataSource setter, the didSet property observer tries to call reloadData() on usersTableView, but it is nil, so you get a crash - you cannot call methods on nil.
It looks like you haven't connected the outlet to your usersTableView property in your storyboard. Alternatively, you're setting it to nil somewhere else in your code. This could be happening automatically if usersTableView isn't part of your view hierarchy when the view controller is loaded, since it is a weak variable, but I expect you have added it as a subview in the storyboard?

Can't unwrap optional when calling a global class

I have the following code in one of my classes.
override func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!) {
parkCode = tableView.cellForRowAtIndexPath(indexPath).text
RTATab.codeText.text = parkCode.substringToIndex(3)
RTATab.codeLetter.text = parkCode.substringFromIndex(3)
self.dismissModalViewControllerAnimated(true)
}
The RTATab referenced above is another class I have made (type UIViewController) and in that class I have declared it as a global class as show below as I need to access some of the textfields (codeText and codeLetter) in its view.
import UIKit
import messageUI
import CoreData
import QuartzCore
var RTATab : ViewController = ViewController()
class ViewController: UIViewController, MFMessageComposeViewControllerDelegate {
//some code
}
When I run this, I get a can't unwrap optional.none error on the line RTATab.codeText.text = parkCode.substringToIndex(3).
Can someone please help. Do I need to have an initialiser in viewController class?
Thanks
You are getting this error the text of the cell is nil. Before calling methods on parkCode, you must first check if it is nil:
let possibleParkCode = tableView.cellForRowAtIndexPath(indexPath).text
if let parkCode = possibleParkCode {
RTATab.codeText.text = parkCode.substringToIndex(3)
RTATab.codeLetter.text = parkCode.substringFromIndex(3)
}
You're getting the error because RTATab.codeText and RTATab.codeLetter are nil -- the way you're initializing RTATab doesn't actually link up its properties with the text fields in your storyboard. If you truly just need a global version of the view controller, you'd need to give it a storyboard ID and load it using something like:
var RTATab: ViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("RTA") as ViewController
However, my guess is that it's a view controller you've already displayed elsewhere that you want to update, in which case you're better off just setting up a data structure that can hold the "parkCode" values and pulling them back into the correct view controller when it's time to display them.
Either parkCode or RTATab.codeText don't exist (are nil). You need to check for their existence prior to dereferencing either of them.
override func tableView (tableView: ...) {
if let theParkCode = tableView.cellForRowAtIndexPath(indexPath).text {
parkCode = theParkCode;
if let theCodeText = RTATab.codeText {
theCodeText.text = parkCode?.substringToIndex(3)
}
if let theCodeLetter ... {
// ...
}
}
}
Note: the above code depends on how your ViewController (the class of RTATab) declares its instance variables for codeText and codeLetter - I've assumed as optionals.