Passing CoreData object between VC’s - ios

I am looking for some clarification and hope someone can help me understand.
My app is completely built programmatically and I’m not using storyboards at all, therefore I don’t have any segues.
I’m trying to pass a CoreData object from one VC to another via the didSelectRowAt method of a tableView.
I have the object being returned at the indexPath correctly, but appear to have issues in passing that object across to the new VC.
In this code extract, my only concern at the moment is to get the CoreData object passed so I can then access all of the associated data.
ie: when the place is passed, I would like to print the name, latitude, longitude.
This will allow me to continue on with the app and use that data in the new VC.
Thanks in advance.
// PlacesVC
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let placeOnMap = fetchedResultsController.object(at: indexPath)
let destVC = MapVC()
destVC.placePassed = placeOnMap
self.navigationController?.pushViewController(destVC, animated: true)
print(placeOnMap.placeName!)
}
// MapVC
import UIKit
class MapVC: UIViewController {
var placePassed = PlaceEntity?
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .green
// print(placePassed.placeName!)
// print(placePassed.latitude!)
// print(placePassed.longitude!)
}
}
Updated Information
In the PlacesVC(), the following is occurring.
If I option click on the let placeOnMap, I get…
Declaration: let placeOnMap: PlaceEntity
The line of code… destVC.placePassed = placeOnMap, gives the following error.
Cannot assign value of type 'PlaceEntity' to type 'PlaceEntity?.Type'
On the MapVC()
If I option click on the placePassed var, I get…
Declaration: let placePassed: PlaceEntity?.Type
Also, this line of code… var placePassed = PlaceEntity?, gives the following error.
Expected member name or constructor call after type name
and If I uncomment the print statement on the destVC, this error pops up…
Type 'PlaceEntity?' has no member 'placeName'
I believe my issues are something to do with the way I am setting the var on the receiving VC because it is adding the .Type to the declared var.
This is the part I am not understanding. Thanks

Related

Calling viewDidLoad in another controller

I'm trying to implement a "forward message" feature in my chat app. So, when I click on the button "forward" in my chatVC the popover VC appears with the users that are on my friends list.
The question is, can I call my chatVC viewDidLoad to refresh the user information? (Otherwise, the user information would be the same as it was before clicking the "forward" button.)
Here's my code where delegate is my chatVC:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let friend = friendsList[indexPath.row]
delegate?.friendProfileImage = friend.profileImage
delegate?.friendName = friend.name
delegate?.friendId = friend.id
delegate?.friendIsOnline = friend.isOnline
delegate?.friendLastLogin = friend.lastLogin
delegate?.forwardedMessage = true
delegate?.messages = []
delegate?.collectionView.reloadData()
delegate?.loadNewMessages = false
delegate?.viewDidLoad()
dismiss(animated: true, completion: nil)
}
As a rule you should try and loosely couple independent classes as much as possible. That is one of the purposes of a delegation pattern - Class "A" can inform a delegate that something has happened and provide some data. Class A doesn't need to know (and shouldn't know) anything more about the delegate object, other than it implements the delegate method.
Your current code is tightly coupled; you are updating properties on the delegate object and reloading a collection view.
Your delegation pattern should look something like this:
protocol FriendSelectionDelegate {
func didSelectRecipient(friend: Friend)
}
Then in your pop up view controller all you need is:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let friend = friendsList[indexPath.row]
delegate?.didSelectRecipient(friend: friend)
dismiss(animated: true, completion: nil)
}
And in your ChatVC:
extension ChatVC: FriendSelectionDelegate {
func didSelectRecipient(friend: Friend) {
self.friendProfileImage = friend.profileImage
self.friendName = friend.name
self.friendId = friend.id
self.friendIsOnline = friend.isOnline
self.friendLastLogin = friend.lastLogin
self.forwardedMessage = true
self.messages = []
self.loadNewMessages = false
self.updateView()
}
}
The updateView function performs whatever operations are required to update the views. You can call that same function in your viewDidLoad to set the initial state of the views
As a further improvement, consider whether you need all of those individual properties (friendName, friendId etc). Can you just store a single property, var selectedFriend: Friend or something and access the properties from that Friend. You can then use a didSet on that selectedFriend property to call your updateView function.
You need put the code in a function that you call from viewDidLoad and your delegate

When trying to segue to a view controller from a table view i get this error: Unexpectedly found nil while unwrapping

I have a segue named "hydrogenSegue" from a "hydrogenBoxButton" to a "Hydrogen" view controller. However, I also wanted to implement a table view so I could search for an element. I tried to make the code so when the cell is clicked it will segue over to the element's view. I used hydrogen as an example here.
In my main ViewController.swift file, I have this to transfer the data:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
//If identifier equals the hydrogen element go to the elements Swift file
if segue.identifier == "hydrogenSegue" {
let hydrogenAtomicNumberPassing = segue.destination as! hydrogenViewController
hydrogenAtomicNumberPassing.hydrogenAtomicNumberPassed = hydrogenAtomicNumber
let hydrogenAtomicMassPassing = segue.destination as! hydrogenViewController
hydrogenAtomicMassPassing.hydrogenAtomicMassPassed = hydrogenAtomicMass
}
}
In the hydrogenViewController.swift file I have this:
import UIKit
class hydrogenViewController: UIViewController {
var hydrogenAtomicNumberPassed: Int!
var hydrogenAtomicMassPassed: Float!
#IBOutlet weak var hydrogenInformationLabel: UILabel!
#IBOutlet weak var hydrogenAtomicNumberLabel: UILabel!
#IBOutlet weak var hydrogenAtomicMassLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
//Setting the background color
self.view.backgroundColor = UIColor.gray
//Converting hydrogen's atomic number from an Int to a String
let hydrogenAtomicNumberString = String("\(hydrogenAtomicNumberPassed!)")
hydrogenAtomicNumberLabel.text = "Atomic Number: \(hydrogenAtomicNumberString)"
//Converting hydrogen's atomic mass from a Float to a String
let hydrogenAtomicMassString = String("\(hydrogenAtomicMassPassed!)")
hydrogenAtomicMassLabel.text = "Atomic Mass: \(hydrogenAtomicMassString)"
}
}
I am getting the error at:
let hydrogenAtomicNumberString = String("\(hydrogenAtomicNumberPassed!)")
I'm assuming it would happen to this line also if I fix only that line:
let hydrogenAtomicMassString = String("\(hydrogenAtomicMassPassed!)")
I have this code in my "searchViewController" (the .swift file used for the table view):
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("row selected : \(indexPath.row)")
if indexPath.row == 0 {
let hydrogenSearchSegue = UIStoryboard(name:"Main",
bundle:nil).instantiateViewController(withIdentifier: "hydrogenView") as!
hydrogenViewController
self.navigationController?.pushViewController(hydrogenSearchSegue,
animated:true)
}
}
When I click on the "Hydrogen" cell in the table view it crashes to this error:
Hydrogen cell
The crash
When I click on the "H" button in this image it will take me to the hydrogen view controller:
Image of the Hydrogen Button in the simulator (Top Left)
Image of the Hydrogen View Controller
I want the hydrogen cell to segue over to the hydrogen view controller just like the button can.
When this same issue came up earlier I just had an issue with the name of the segue in the storyboard. However, because there is no visible segue from the table view, I don't know how to fix the issue.
I've tried this:
performSegue(withIdentifier: "hydrogenSegue", sender: nil)
I was thinking that I could just reuse the "hydrogenSegue" from the button to the view controller but I get a SIGABRT error. It just says that there is no segue with the name "hydrogenSegue." It would be best if I could just reuse that segue in a way because everything is already connected but I now found out that the "searchViewController" can't recognize the segue. Any help is appreciated and my main goal is to just get the cell that is clicked on to move over to the element's designated view. I tried to provide as much information as possible without making it to long and if there is any more information needed, I should be able to provide it.
well. first answer
in your hydrogenViewController try with this lines.
var hydrogenAtomicNumberPassed: Int?
var hydrogenAtomicMassPassed: Float?
override func viewDidLoad(){
super.viewDidLoad()
self.viewBackgroundColor = .gray
}
override func viewWillAppear(){
super.viewWillAppear()
if let number = hydrogenAtomicNumberPassed
{
hydrogenAtomicNumberLabel.text = "Atomic Number: \(number)"
}
if let mass = hydrogenAtomicMassPassed
{
hydrogenAtomicMassLabel.text = "Atomic Mass: \(mass)"
}
}
Now, the segues only "lives" between a couple viewControllers, if you have a third view controller, the last will not recognize him.
other thing, you are using segues and navigation controller, from my point of view, it's a bad idea mixed both, I mean, there are specific apps that can use both ways to present views, only is a advice.
if you want to pass data with pushviewcontroller only use this line
if indexPath.row == 0 {
let hydrogenSearchSegue = UIStoryboard(name:"Main",bundle:nil).instantiateViewController(withIdentifier: "hydrogenView") as! hydrogenViewController
hydrogenSearchSegue.VAR_hydrogenViewController = YOURVAR_INYOURFILE
self.navigationController?.pushViewController(hydrogenSearchSegue, animated:true)
}
tell me if you have doubts, and I will try to help you.

Swift 2.1 - Unexpectedly found nil while unwrapping an Optional on delegate method call

I have the following two ViewControllers (one is TableViewController) that
CategoryTableViewController has a delegate method which will be called when Table Cell is selected. Then CreateRecipeViewController implements this delegate method and handles the selected category string.
I'm getting fatal error: unexpectedly found nil while unwrapping an Optional at the line delegate.categoryController(self, didSelectCategory: categoryCell)
print(categoryCell) correctly prints the string value of selected table cell so I'm not sure why I'm getting this error.
I'm new to implementing protocol so there may be a high chance I'm doing this wrong. I'd much appreciate if some one can give me a clue of this error.
CategoryTableViewController (choose category)
protocol CategoryTableViewControllerDelegate: class {
func categoryController(controller: CategoryTableViewController, didSelectCategory category: String)
}
class CategoryTableViewController: UITableViewController {
...
weak var delegate: CategoryTableViewControllerDelegate!
...
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if let categoryCell = recipeCategory[indexPath.row].name {
print(categoryCell)
delegate.categoryController(self, didSelectCategory: categoryCell)
}
}
}
CreateRecipeViewController (receives selected category)
extension CreateRecipeViewController: CategoryTableViewControllerDelegate {
func categoryController(controller: CategoryTableViewController, didSelectCategory category: String) {
let selectedCategory = category
dismissViewControllerAnimated(true, completion: nil)
let cell = RecipeCategoryCell()
cell.configureSelectedCategoryCell(selectedCategory)
recipeCategoryTableView.reloadData()
}
}
UPDATE
The problem is that you forgot to assign a value to the property delegate. In other words, delegate is nil.
Since delegate is an implicitly unwarped optional type, you don't need to add a question mark or an exclamation mark to unwrap it. This is why you didn't see the mistake.
To solve this, just put a question mark after delegate:
delegate?.categoryController(self, didSelectCategory: categoryCell)
But that doesn't solve the problem! This way, categoryController(:didSelectCategory:) will never be called!
Unfortunately, I cannot think of a way to pass data between view controllers using delegation. But there is a simpler way to do that.
For simplicity, let's call CategoryTableViewController the sender and CreateRecipeViewController the receiver.
This is what I guess you want to do. You want the user to select a category in the sender and pass the selected category to the receiver. So in the sender, you need to call performSegueWithIdentifier, right?
When the view is going to perform a segue, prepareForSegue is called. You need to override that method:
override func prepareForSegue(segue: UIStoryBoardSegue) {
if segue.identifier == "your segue's identifier" {
let destinationVC = segue.destinationViewController as! reciever
destinationVC.selectedCategory = self.selectedCategory
}
}
There is some stuff that is undefined in the above code. Let's define them now.
The first thing is destination.selectedCategory. You add a property in the receiver:
var selectedCategory: String!
In the viewDidLoad of the receiver, you can use this property to know what the user selected. In other words, the data is passed to this property.
The second is self.selectedCategory. Let's define that as well, in the sender:
var selectedCategory: String!
In the tableView(:didSelectRowAtIndexPath:indexPath:) of sender, you need to assign self.selectedCategory the category selected:
self.categorySelected = recipeCategory[indexPath.row].name
And BOOM! That's it!

Swift Delegate and Optional

I'm working on my first iOS app using swift. I'm trying to load the a value from one viewController into another. I am using a protocol, but I can't get it to execute properly. I have searched around both stack overflow and elsewhere but haven't been able to find an answer that works in my situation.
Here's the VC I'm trying to pull the value from:
protocol AddHelperVCDelegate {
func didFinishAddingHelper(controller: AddHelperViewController)
}
class AddHelperViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, NSFetchedResultsControllerDelegate {
#IBOutlet weak var tableView: UITableView!
let managedObjectContext = (UIApplication.sharedApplication().delegate as AppDelegate).managedObjectContext!
var fetchedResultsController:NSFetchedResultsController = NSFetchedResultsController()
var delegate:AddHelperVCDelegate! = nil
var helperBonus:NSNumber = 0
override func viewDidLoad() {
super.viewDidLoad()
fetchedResultsController = getFetchResultsController()
fetchedResultsController.delegate = self
fetchedResultsController.performFetch(nil)
// Do any additional setup after loading the view.
}
}
And here is where I am trying to move the value (and the view) back to a proceeding VC.
// UITableViewDelegate
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
var indexPathForRow = tableView.indexPathForSelectedRow()
println("indexPath for selected row is: \(indexPathForRow)")
let thisUser = fetchedResultsController.objectAtIndexPath(indexPath) as UserModel
var cell:UserCell = tableView.dequeueReusableCellWithIdentifier("helperCell") as UserCell
helperBonus = thisUser.effectiveCombat
helperBonus = Int(helperBonus)
println("helperBonus is: \(helperBonus)")
delegate.didFinishAddingHelper(self)
}
If I make the delegate an optional (delegate?.didFinishAddingHelper(self)) then nothing happens. If I do not, I get a crash with the error message:
indexPath for selected row is: Optional(<NSIndexPath: 0xc000000000018016> {length = 2, path = 0 - 3})
helperBonus is: 0
fatal error: unexpectedly found nil while unwrapping an Optional value
Now, I know I'm declaring delegate as nil, but that's the limit to my understanding of what's going on. I need to add the value in the proceeding VC in this function:
func didFinishAddingHelper(controller: AddHelperViewController) {
self.effectiveCombat = Int(controller.helperBonus)
controller.navigationController?.popViewControllerAnimated(true)
}
The crash is happening because AddHelperViewController's delegate property is nil. This is because you aren't setting it.
Wherever you create the AddHelperViewController, set its delegate on the next line:
let addHelperVC = AddHelperViewController()
addHelperVC.delegate = self
Then when you call the delegate property, it will point back to the view controller that created it.
If your AddHelperViewController is created using a storyboard, set delegate in the prepareForSegue(_:sender:) of the method that is about to show the new controller:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let vc = segue.destinationViewController as? AddHelperViewController {
// If we got here, vc is an AddHelperViewController
vc.delegate = self
}
}
var delegate:AddHelperVCDelegate! = nil
Should be
var delegate:AddHelperVCDelegate?
It is failing because you are using !, which means that you guarantee that, while it can't be initialized during the classes init, it will not be nil by the time you want to use it.
Aaron Brager is absolutely correct when he says you need to set the delegate property.
With your delegate declared as optional (with ?), you can call it only if the object is not nil (ignored otherwise):
delegate?.didFinishAddingHelper(self)
On a side note, you might also consider making the delegate property weak in order to help prevent retain cycles.

Using A Delegate to Pass a var

I have been pulling my hair out trying to get this 'Delegate' thing to work in Swift for an App I am working on.
I have two files: CreateEvent.swift and ContactSelection.swift, where the former calls the latter.
CreateEvent's contents are:
class CreateEventViewController: UIViewController, ContactSelectionDelegate {
/...
var contactSelection: ContactSelectionViewController = ContactSelectionViewController()
override func viewDidLoad() {
super.viewDidLoad()
/...
contactSelection.delegate = self
}
func updateInvitedUsers() {
println("this finally worked")
}
func inviteButton(sender: AnyObject){
invitedLabel.text = "Invite"
invitedLabel.hidden = false
toContactSelection()
}
/...
func toContactSelection() {
let contactSelection = self.storyboard?.instantiateViewControllerWithIdentifier("ContactSelectionViewController") as ContactSelectionViewController
contactSelection.delegate = self
self.navigationController?.pushViewController(contactSelection, animated: true)
}
ContactSelection's contents are:
protocol ContactSelectionDelegate {
func updateInvitedUsers()
}
class ContactSelectionViewController: UITableViewController {
var delegate: ContactSelectionDelegate?
override func viewDidLoad() {
super.viewDidLoad()
self.delegate?.updateInvitedUsers()
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
// Stuff
self.delegate?.updateInvitedUsers()
}
}
What am I doing wrong? I am still new and don't fully understand this subject but after scouring the Internet I can't seem to find an answer. I use the Back button available in the Navigation Bar to return to my CreateEvent view.
var contactSelection: ContactSelectionViewController = ContactSelectionViewController()
This is instantiating a view controller directly, and the value never gets used. Since it looks like you're using storyboards, this isn't a good idea since none of the outlets will be connected and you'll get optional unwrapping crashes. You set the delegate of this view controller but that's irrelevant as it doesn't get used.
It also isn't a good idea because if you do multiple pushes you'll be reusing the same view controller and this will eventually lead to bugs as you'll have leftover state from previous uses which might give you unexpected outcomes. It's better to create a new view controller to push each time.
In your code you're making a brand new contactSelection from the storyboard and pushing it without setting the delegate.
You need to set the delegate on the instance that you're pushing onto the navigation stack.
It's also helpful to pass back a reference in the delegate method which can be used to extract values, rather than relying on a separate reference in the var like you're doing.
So, I'd do the following:
Remove the var contactSelection
Add the delegate before pushing the new contactSelection object
Change the delegate method signature to this:
protocol ContactSelectionDelegate {
func updateInvitedUsers(contactSelection:ContactSelectionViewController)
}
Change your delegate calls to this:
self.delegate?.updateInvitedUsers(self)

Resources