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?
Related
I am trying to get the value of a switch and pass it to an object in a view controller.
Below is the code of my view controller. I have created a label, switch outlet, and switch action.
#IBOutlet weak var firstTimeConducting: UILabel!
#IBOutlet weak var firstTime_Conduct: UISwitch!
#IBAction func firstTimeConductSwitchTap(_ sender: Any) {
firstTimeConductTapped()
}
override func viewDidLoad() {
super.viewDidLoad()
firstTimeConductTapped()
}
func firstTimeConductTapped() {
if firstTime_Conduct.isOn {
value = "Yes"
} else {
value = "No"
}
}
I have debugged it and it enters and gets the value as "yes" when I click on the switch from off to on state in simulator.
Then after returning to function firstTimeConductSwitchTap(), it crashes saying the following error
"Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[app.RequestFormViewController first_TimeConduct:]: unrecognized selector sent to instance 0x7fb900421fc0'
I am just retrieving the value of "value" variable and passing it to object to display.
Can you suggest whether I am following the correct procedure and why I get this error?
The error is pretty clear:
In Interface Builder there is a dead connection to a selector first_TimeConduct. Most likely you renamed the action method.
Search for first_TimeConduct with ⇧⌘F and delete the connection.
I am creating a sort of manual slideshow (i.e., the pictures move when the user taps a forward or backward button), and it is made with a Collection View. I had to create a custom collection cell view class, which I finished, but now I'm getting an error message in the iOS Simulator, "fatal error: unexpectedly found nil while unwrapping an Optional value" even though my code builds successfully. The error is in the ibCollectionView.dataSource = self line, and because I'm new to Swift and OOP (I learned about a month ago for the first time), I am confused by this error message, especially because there are no ? operators. I have included the problem part of my code (the part Xcode showed with the error message) below. Thank you!
import UIKit
class FluidIntakeMainMenuViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
//MARK: - Collection View Properties
#IBOutlet weak var ibCollectionView: UICollectionView!
#IBOutlet weak var ibCollectionViewLayout: UICollectionViewLayout!
var cellArray:[customCollectionViewCell] = Array(repeatElement(customCollectionViewCell(), count: 6))
let displayedCellDimensions = CGSize(width: 343, height: 248)
//MARK: - Xcode-generated Methods
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
ibCollectionView.dataSource = self
ibCollectionView.delegate = self
}
//My code for this class continues down here...
First and most important thing is to check the IBOutlet connection
that you made in your storyboard.
Try to remove the connection in storyboard and then reconnect it.
Secondly put a debugger on to check if it's still nil.
ibCollectionView.dataSource = self
I think you forgot about this:
override func viewDidLoad() {
super.viewDidLoad()
// Register cell classes
ibCollectionView?.register(YourCustomCell.self, forCellWithReuseIdentifier: "Cell")
And use "Cell" identifier in cellForItemAt...
Basically I'm trying to make a simple note app, and here I'm just trying to display the title and content.
Project (note) file
import Foundation
class Project
{
var title = " "
var content = " "
var after = " "
}
Note detail view controller file
class NoteDetailViewController: UIViewController
{
var project: Project!
#IBOutlet weak var titleTextField: UITextField!
#IBOutlet weak var contentTextField: UITextView!
#IBOutlet weak var afterTextField: UITextView!
override func viewWillAppear(animated: Bool)
{
super.viewWillAppear(animated)
titleTextField.text = project.title
contentTextField.text = project.content
afterTextField.text = project.after
contentTextField.sizeToFit()
afterTextField.sizeToFit()
}
override func viewWillDisappear(animated: Bool)
{
super.viewWillDisappear(animated)
project.title = titleTextField.text!
project.content = contentTextField.text!
}
}
When I reference any of the project. files, I get nil. I have no idea why I'm getting nil from a string?
You need to initialize the class "Project" correctly.
let project = Project()
It seems to me project is not initialized;
project = new Project()
so accessing its properties will eventually return nil because the object does not exist
Yes, initializing the property definitely fixes the issue but there's a reason behind the error being thrown, and its one that's worth understanding rather than just initializing the property to make the error go away.
When you declare your property as:
var project: Project! //- with a !
You are declaring an optional property that immediately gets unwrapped (Implicit Unwrapping), this means that the compiler expects you to handle the case where the property is nil, and it wont throw compile errors when you use the property (as it has already been unwrapped). Which is not ideal as Swift is designed to try to catch as many errors at compile time, rather than at run time, and by unwrapping your optional when you declare it you are missing out on all that goodness.
In your every day swift you would declare optional properties as so:
var project: Project?
Which would throw a compile time error if you try to access the property value without unwrapping it. For example when doing:
var project: Project?
override func viewWillAppear(animated: Bool)
{
super.viewWillAppear(animated)
titleTextField.text = project.title //- The compiler will scream at you here.
contentTextField.text = project.content
afterTextField.text = project.after
contentTextField.sizeToFit()
afterTextField.sizeToFit()
}
If you you don't need your property to be an optional, initialize the variable when you declare it, or do so in the constructor of your class (where it applies), and avoid Implicitly unwrapping your optionals as this can have the potential of resulting in run time errors.
Hope this helps!
I'm trying to enable or disable an #IBOutlet UIButton Item of a toolbar from a UIView.
The button should get disabled when the array that I'm using in EraseView.Swift is empty.
I tried creating an instance of the view controller but it gives me the error (found nil while unwrapping):
in EraseView:
class EraseView: UIView {
...
let editViewController = EditImageViewController()
//array has item
editViewController.undoEraseButton.enabled = true //here I get the error
...
}
I tried to put a global Bool that changed the value using it in EditImageViewController but it doesn't work:
var enableUndoButton = false
class EditImageViewController: UIViewController {
#IBOutlet weak var undoEraseButton: UIBarButtonItem!
viewDidLoad() {
undoEraseButton.enabled = enableUndoButton
}
}
class EraseView: UIView {
...
//array has item
enableUndoButton = true //here I get the error
...
}
I know it's simple but I can't let it work. Here's the situation:
The root of the problem is the line that says:
let editViewController = EditImageViewController()
The EditImageViewController() says "ignore what the storyboard has already instantiated for me, but rather instantiate another view controller with no outlets hooked up and use that." Clearly, that's not what you want.
You need to provide some way for the EraseView to inform the existing view controller whether there was some change to its "is empty" state. And, ideally, you want to do this in a way that keeps these two classes loosely coupled. The EraseView should only be informing the view controller of the change of the "is empty" state, and the view controller should initiate the updating of the other subviews (i.e. the button). A view really shouldn't be updating another view's outlets.
There are two ways you might do that:
Closure:
You can give the EraseView a optional closure that it will call when it toggles from "empty" and "not empty":
var emptyStateChanged: ((Bool) -> ())?
Then it can call this when the state changes. E.g., when you delete the last item in the view, the EraseView can call that closure:
emptyStateChanged?(true)
Finally, for that to actually do anything, the view controller should supply the actual closure to enable and disable the button upon the state change:
override func viewDidLoad() {
super.viewDidLoad()
eraseView.emptyStateChanged = { [unowned self] isEmpty in
self.undoEraseButton.enabled = !isEmpty
}
}
Note, I used unowned to avoid strong reference cycle.
Delegate-protocol pattern:
So you might define a protocol to do that:
protocol EraseViewDelegate : class {
func eraseViewIsEmpty(empty: Bool)
}
Then give the EraseView a delegate property:
weak var delegate: EraseViewDelegate?
Note, that's weak to avoid strong reference cycles. (And that's also why I defined the protocol to be a class protocol, so that I could make it weak here.)
The EraseView would then call this delegate when the the view's "is empty" status changes. For example, when it becomes empty, it would inform its delegate accordingly:
delegate?.eraseViewIsEmpty(true)
Then, again, for this all to work, the view controller should (a) declare that is conforms to the protocol; (b) specify itself as the delegate of the EraseView; and (c) implement the eraseViewIsEmpty method, e.g.:
class EditImageViewController: UIViewController, EraseViewDelegate {
#IBOutlet weak var undoEraseButton: UIBarButtonItem!
override func viewDidLoad() {
super.viewDidLoad()
eraseView.delegate = self
}
func eraseViewIsEmpty(empty: Bool) {
undoEraseButton.enabled = !empty
}
}
Both of these patterns keep the two classes loosely coupled, but allow the EraseView to inform its view controller of some event. It also eliminates the need for any global.
There are other approaches that could solve this problem, too, (e.g. notifications, KVN, etc.) but hopefully this illustrates the basic idea. Views should inform their view controller of any key events, and the view controller should take care of the updating of the other views.
I'm pretty new to coding in Swift and I'm not too sure what's happening here - can anyone help?
Thanks
import UIKit
class SecondViewController: UIViewController {
var toDoItems:[String] = []
#IBOutlet weak var toDoItem: UITextField!
#IBAction func addItem(sender: AnyObject) {
toDoItems.append(toDoItem.text)
fatal error: unexpectedly found nil while unwrapping an Optional value
(lldb)
println(toDoItems)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Most likely, your IBOutlet, toDoItem, was not bound successfully to a UITextField in Interface Builder. Check the outlets for your text field in Interface Builder and make sure it's hooked up successfully.
If the outlet is hooked up properly, another candidate source of this problem is the instantiation of the view controller itself. If you instantiated it programmatically (e.g. SecondViewController() instead of storyboard.instantiateViewControllerWithIdentifier(...)), that would also result in this error.