error "secondViewController does not have a member named "mastername" - swift

I am using a sample from IOS 8 App Development Essentials. I added a variable to my second controller but keep getting this error.
Second controller code:
Class SocondDetailController: UIViewController{
var mastername: String?
...
}
First controller code:
override func prepareForSegue(segue: UIStoryboardSegue,sender: AnyObject?)
{
if segue.identifier == "ShowDetails"
{        
let detailViewController = segue.destinationViewController as SocondDetailController
let myIndexPath = self.tableView.indexPathForSelectedRow()
let row = myIndexPath?.row
SocondDetailController.mastername = tableData[row!]
}
}
I am new to Swift and IOS development. Just starting at age 71.
I have been using VB.Net for a long time.
Please help.
Thanks.

This row:
SocondDetailController.mastername = tableData[row!]
should be:
detailViewController.mastername = tableData[row!]
mastername is an instance property, and as such you have to access to it through an instance of SocondDetailController, and not the SocondDetailController type itself.
Also, although probably not needed due to the logic in your view controller, I'd avoid using the forced unwrapping operator !, preferring a safer optional binding:
let row = myIndexPath?.row
if let row = row {
detailViewController.mastername = tableData[row]
}
or, more concisely:
if let row = myIndexPath?.row {
detailViewController.mastername = tableData[row]
}

Related

fatal errors with optionals not making sense

I keep getting a fatal error saying how a value was unwrapped and it was nil and I don't understand how. When I instantiate a view controller with specific variables they all show up, but when I perform a segue to the exact VC, the values don't show up.
Take these functions for example...
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
if let displayVC = storyboard?.instantiateViewController(withIdentifier: Constants.Storyboards.TeachStoryboardID) as? SchoolEventDetailsViewController {
displayVC.selectedEventName = events[indexPath.row].eventName
displayVC.selectedEventDate = documentsDate[indexPath.row].eventDate
displayVC.selectedEventCost = documentsCost[indexPath.row].eventCost
displayVC.selectedEventGrade = documentsGrade[indexPath.row].eventGrade
displayVC.selectedEventDocID = documentsID[indexPath.row]?.docID
navigationController?.pushViewController(displayVC, animated: true)
}
}
This combined with this function :
func verifyInstantiation() {
if let dateToLoad = selectedEventDate {
dateEditableTextF.text = dateToLoad
}
if let costToLoad = selectedEventCost {
costEditableTextF.text = costToLoad
}
if let gradesToLoad = selectedEventGrade {
gradesEditableTextF.text = gradesToLoad
}
if let docIDtoLoad = selectedEventDocID {
docIDUneditableTextF.text = docIDtoLoad
}
if let eventNameToLoad = selectedEventName {
eventNameEditableTextF.text = eventNameToLoad
}
}
Helps load the data perfectly, but when I try to perform a segue from a search controller the data is not there.
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.title = selectedEventName
I set the title of the vc to have the event name , and I also recently added a text field to store it as well for experimental purposes (this question).
Now the issue is I want to do a data transfer from an Algolia Search Controller to that VC and I got all the other fields to show up, except for one and that was the document ID. So I created a completion handler function to get the document ID as a string and have it inserted into the vc when the segue is performed, just like how it's there when the vc is instantiated.
Here is the function :
func getTheEventDocID(completion: #escaping ((String?) -> ())) {
documentListener = db.collection(Constants.Firebase.schoolCollectionName).whereField("event_name", isEqualTo: selectedEventName ?? navigationItem.title).addSnapshotListener(includeMetadataChanges: true) { (querySnapshot, error) in
if let error = error {
print("There was an error fetching the documents: \(error)")
} else {
self.documentsID = querySnapshot!.documents.map { document in
return EventDocID(docID: (document.documentID) as! String)
}
let fixedID = "\(self.documentsID)"
let substrings = fixedID.dropFirst(22).dropLast(3)
let realString = String(substrings)
completion(realString)
}
}
}
I thought either selectedEventName or navigationItem.title would get the job done and provide the value when I used the function in the data transfer function which I will show now :
//MARK: - Data Transfer From Algolia Search to School Event Details
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
otherVC.getTheEventDocID { (eventdocid) in
if let id = eventdocid {
if segue.identifier == Constants.Segues.fromSearchToSchoolEventDetails {
let vc = segue.destination as! SchoolEventDetailsViewController
vc.selectedEventName = self.nameTheEvent
vc.selectedEventDate = self.dateTheEvent
vc.selectedEventCost = self.costTheEvent
vc.selectedEventGrade = self.gradeTheEvent
vc.selectedEventDocID = id
}
}
}
}
But it ends up showing nothing when a search result is clicked which is pretty upsetting, I can't understand why they're both empty values when I declared them in the SchoolEventDetailsVC. I tried to force unwrap selectedEventName and it crashes saying there's a nil value and I can't figure out why. There's actually a lot more to the question but I just tried to keep it short so people will actually attempt to read it and help since nobody ever reads the questions I post, so yeah thanks in advance.
I'm a litte confused what the otherVC is, which sets a property of itself in the getTheEventDocID, whilste in the closure you set the properties of self, which is a different controller. But never mind, I hope you know what you are doing.
Since getTheEventDocID runs asynchronously, the view will be loaded and displayed before the data is available. Therefore, viewDidLoad does not see the actual data, but something that soon will be outdated.
So, you need to inform the details view controller that new data is available, and refresh it's user interface. Something like
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
otherVC.getTheEventDocID { (eventdocid) in
if let id = eventdocid {
if segue.identifier == Constants.Segues.fromSearchToSchoolEventDetails {
let vc = segue.destination as! SchoolEventDetailsViewController
vc.selectedEventName = self.nameTheEvent
vc.selectedEventDate = self.dateTheEvent
vc.selectedEventCost = self.costTheEvent
vc.selectedEventGrade = self.gradeTheEvent
vc.selectedEventDocID = id
vc.updateUI()
}
}
}
}
and in the destination view controller:
class SchoolEventDetailsViewController ... {
override func viewDidLoad() {
super.viewDidLoad()
updateUI()
}
func updateUI () {
navigationItem.title = selectedEventName
// and so on
}
}
Ok so I decided to attempt a workaround and completely ditched the getTheEventDocID() method because it was just causing me stress. So I decided to ditch Firebase generated document IDS and just use 10 digit generated ids from a function I made. I also figured out how to add that exact same 10 digit id in the Algolia record by just storing the random 10 digit id in a variable and using it in both places. So now instead of using a query call to grab a Firebase generated document ID and have my app crash everytime I click a search result, I basically edited the Struct of the Algolia record and just added an eventDocID property that can be used with hits.hitSource(at: indexPath.row).eventDocID.
And now the same way I added the other fields to the vc by segue data transfer, I can now do the same thing with my document ID because everything is matching :).

TableView segue with multipe arrays

I have a table view with two sections, both calling two different arrays.
var data1 = [Data]()
var data2 = [Data]()
let section = ["Section1", "Section2"]
How can I pass the information of both through a segue?
This is my information for the segue, "Data" is a struct on a seperate file.
let destination = segue.destinationViewController as! DetailsViewController
let selectedInfo = data1[indexPath.row]
destination.detailsTitle.text = selectedInfo.dataTitle
destination.detailsImage.image = selectedInfo.dataImage
destination.detailsInfo.text = selectedInfo.dataDetails
destination.detailsGenre.text = selectedInfo.dataGenre
But I have two arrays, and I'm not sure how to go about it. Also, this information doesn't work. It says the passed information is nil and my app crashes. Both arrays have information append to it.
This is the whole segue:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == cellIdentifier {
let destination = segue.destinationViewController as! DetailsViewController
if let indexPath = self.tableView.indexPathForSelectedRow {
let selectedInfo = data1[indexPath.row]
destination.detailsTitle.text = selectedInfo.dataTitle
destination.detailsImage.image = selectedInfo.dataImage
destination.detailsInfo.text = selectedInfo.dataDetails
destination.detailsGenre.text = selectedInfo.dataGenre
}
}
}
The information on my arrays is this...
let pic1 = UIImage(named: "killlakill")
var animeInfo = Data(title: "Kill la Kill", image: pic1!, details: "The story is set on a high school that the student council president Satsuki Kiryuuin rules by force. Wielding a giant Basami scissors sword, the wandering transfer student Ryuuko Matoi brings about upheaval on the campus. Ryuuko searches for the mysterious figure who caused her father's death, but confronting her are the student council's four divine kings. Fortunately, Ryuuko is aided by a talking sailor uniform who tells her, Wear me. When I am worn by you, this power will become manifest.", genre: "School, Comedy, Action", episodes: "24")
data1.append(animeInfo)
And so on...
In the table view controller:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if let row = self.tableView.indexPathForSelectedRow?.row {
if let section = self.tableView.indexPathForSelectedRow?.section {
let destination = segue.destinationViewController as! DetailsViewController
if section == 0 {
let selectedInfo = data1[row]
destination.data = selectedInfo
}
else if section == 1 {
let selectedInfo = data2[row]
destination.data = selectedInfo
}
}
}
}
In the second view controller, have:
var data = Data()
Then use the information from data to fill in your labels and such:
override func viewDidLoad() {
super.viewDidLoad()
detailsTitle.text = data.dataTitle
detailsImage.image = data.dataImage
detailsInfo.text = data.dataDetails
detailsGenre.text = data.dataGenre
}
Change your data property to:
let data = [Data, Data]
And then in your segue use:
let selectedInfo = data[indexPath.section][indexPath.row]

Pass object from tableview to destination with revealviewcontroller in between

I am trying to pass an object from my tableview to the detail view. I am using the revealviewcontroller framework to have a slide out menu. Therefore I need to create a segue from the tableview to the revealviewcontroller and from here another one to the final detailviewcontroller.
That is why I can´t set the object in the detail view - any idea how to do so?
This is the used code:
if segue.identifier == "communityDetailSegue" {
// Get the cell that generated this segue.
if let selectedCommunityCell = sender as ? UITableViewCell {
let destination = segue.destinationViewController as!CommunityViewController
if let communityIndex = self.tableView.indexPathForCell(selectedCommunityCell) {
destination.community = self.communitiesOfCurrentUser[communityIndex.row]
print(self.communitiesOfCurrentUser[communityIndex.row].name)
}
}
}
And this is the exception.
Could not cast value of type 'SWRevealViewController' (0x10027b9f0) to 'CommunityViewController'
You get the error because the segue's destination VC is the SWRevealViewController and not the CommunityViewController.
One way of solving your problem would be to pass the value in two steps:
First, in prepareForSegue() you pass the value to the SWRevealViewController (you'll need a subclass for this one, e.g. MyRevealViewController):
if segue.identifier == "communityDetailSegue" {
// Get the cell that generated this segue.
if let selectedCommunityCell = sender as ? UITableViewCell {
let destination = segue.destinationViewController as! MyRevealViewController
if let communityIndex = self.tableView.indexPathForCell(selectedCommunityCell) {
destination.community = self.communitiesOfCurrentUser[communityIndex.row]
print(self.communitiesOfCurrentUser[communityIndex.row].name)
}
}
}
Then, in MyRevealViewControlleryou can pass the value as soon as it is set:
class MyRevealViewController : SWRevealViewController {
// Let's assume this is the outlet to your final VC:
IBOutlet let communityViewController: CommunityViewController!
var community: YourCommunityType {
didSet {
if let communityVC = self.communityViewController {
communityVC.community = self.community
}
}
}
}

Queue of functions - Swift

I have a table in which each cell there is the profile name of a user. I want that when the profile name is clicked, it will go to the profile page of the user. I created a delegate from the cell for this. But the problem is, the variable "goToProfileVar" is empty at the starting point. Then I assign a value to it with the "goToCell103" function. Then the assigned value should be used in the "prepareforsegue" to pass the username data to the profile page. The problem is that, prepareforseque functions works first and the "goToProfileVar" becomes empty and passed so, then the variable is assigned from the cell.username.text.
How can I queue these functions?
var goToProfileVar = String()
var goToProfileVar2 = String()
func goToCell103(cell: mainCell)
{
var goToCell103:mainCell? = cell as mainCell
var indexPath: NSIndexPath = self.resultsTable.indexPathForCell(goToCell103!)!
goToProfileVar = cell.usernameLbl.text!
goToProfileVar2 = cell.objectid.text!
println("\(goToProfileVar) first.")
println("\(goToProfileVar2) first.")
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if (segue.identifier == "goToProfile8") {
var svc = segue.destinationViewController as! generalProfileLast;
svc.dataPassed = goToProfileVar}
else if (segue.identifier == "goToLikers1") {
var svc = segue.destinationViewController as! likers;
svc.dataPassed = goToProfileVar2}
else if (segue.identifier == "goToComments1") {
var svc = segue.destinationViewController as! enterCommentVC;
svc.dataPassed = goToProfileVar2}
else if (segue.identifier == "goToComments2") {
var svc = segue.destinationViewController as! commenters;
svc.dataPassed = goToProfileVar2}
println("\(goToProfileVar) second.")
println("\(goToProfileVar2) second.")
}
It is in general bad approach to use current view state as a model. Likely your UI populated from somewhere, so use the same source of data to initialize your controllers in prepareForSegue.
If you do not want to change a lot of your architecture and still prefer your way you can use goToCell103 right in prepareForSegue with a sender as an input parameter. In a cell segues sender would be a taped cell.

Does Not Have Member named indexPathForSelecedRow().row

I am new in swift I am trying to send some information on click on cell of Table view using segue,but when i try to compile i get this error..
I am using Xcode 6.1 and SDK 8.1
override func prepareForSegue(segue: UIStoryboardSegue, sender:AnyObject?) {
if segue.identifier == "update"{
let selectedItems : NSManagedObject = arr[self.tableView.indexPathForSelectedRow().row] as NSManagedObject
let IVC : ViewController = segue.destinationViewController as ViewController
IVC.itemVar = selectedItems.valueForKey("item") as String
IVC.qtyVar = selectedItems.valueForKey("qty") as String
IVC.discVar = selectedItems.valueForKey("disc") as String
IVC.selectedItem = selectedItemse
}
}
You have to unwrap the returned indexPath
self.tableView.indexPathForSelectedRow()!.row
but do this only if you are sure, returned indexPath will always have value(it wont be nil)
Othrewise make the unwrapping this way:
if let indexPath = self.tableView.indexPathForSelectedRow(){
indexPath.row
}