Swift and Parse nil on prepareforsegue - ios

Im writing a table view controller using an array from Parse. I need to send data through the segue but I dont know why its always returning nil. I will have to send images and text, but for now im just taking the text from the label title1 to insert it into a variable type String named example.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "toDetail" {
var detailScene = segue.destinationViewController as! detailViewController
let cell = sender as! UITableViewCell
let indexPath = tableView?.indexPathForCell(cell)
detailScene.example = self.timelineData[indexPath!.row].title1?.text
}
}
For the array I have used:
var timelineData : NSMutableArray = NSMutableArray()
And the function loaddata, that is used with the function viewDidAppear
func loaddata () {
timelineData.removeAllObjects()
var findTimelineData:PFQuery = PFQuery(className: "ii")
findTimelineData.findObjectsInBackgroundWithBlock{
(objects, error)->Void in
if error == nil{
for object in objects!{
let ii:PFObject = object as! PFObject
self.timelineData.addObject(ii)
}
let array:NSArray = self.timelineData.reverseObjectEnumerator().allObjects
self.timelineData = NSMutableArray(array: array)
self.tableView.reloadData()
}
}
}

Try this. It should at least get you to the point where you're grabbing the information from the correct cell. I'm assuming your timelineData array is full of text, so that's how I accessed it, by casting it as a String. If it is full of a different type of object you need to cast it differently.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "toDetail" {
if let detailScene = segue.destinationViewController as? detailViewController {
if let indexPath = tableView.indexPathForSelectedRow()?.row {
var object = self.timelineData[indexPath] as! PFObject
var title = object["title"] as! String
detailScene.example = title
}
}
}
}

It could be that the return value from indexPathForCell is returning nil or a bad value. There are quite a few articles about this function being unreliable.
A common suggestion is to use indexPathForRowAtPoint(cell.center) instead.

There is definitely something wrong with your last line of code:
detailScene.example = self.timelineData[indexPath!.row].title1?.text
I assume that self.timelineData array that holds some data.
If your array contain String class you don't need to append .title1?.text part.
If your array contain UILabel class you need change line to
detailScene.example = self.timelineData[indexPath!.row].text
because UILabel have no property called title1.
In general I think this happened because class in self.timelineData array have no property called title1.

Related

How to change an array that is inserted on a TableCell from information I have in another ViewController?

I need your help! I don´t know how to change an array that is inserted on a TableCell from information I have in another ViewController. It’s a little bit messed up, but I’m gonna show you by my code.
Here I have a ViewController conformed by many switches that correspond to different categories of coupons, this is the code:
class FiltersViewController: UIViewController {
#IBOutlet weak var restaurantsSwitch: UISwitch!
#IBOutlet weak var sportsSwitch: UISwitch!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func returnHome(_ sender: Any) {
let vc = self.storyboard!.instantiateViewController(withIdentifier: "home") as! HomeViewController
self.present(vc, animated: false, completion: nil)
}
#IBAction func restaurants(_ sender: UISwitch) {
if restaurantsSwitch.isOn == true{
tuxtlaSwitch.isOn = false
sevillaSwitch.isOn = false
coapaSwitch.isOn = false
coyoacanSwitch.isOn = false
universidadSwitch.isOn = false
polancoSwitch.isOn = false
}
}
#IBAction func sports(_ sender: UISwitch) {
if sportsSwitch.isOn == true{
tuxtlaSwitch.isOn = false
sevillaSwitch.isOn = false
coapaSwitch.isOn = false
coyoacanSwitch.isOn = false
universidadSwitch.isOn = false
polancoSwitch.isOn = false
}
}
}
I’ve only show you two switches at the example with the purpose of not filling this with many code, but there are like 15 switches.
And in the other ViewController, which is connected to this one, the HomeViewController, contains coupons that comes from a JSON, and conforms an array of ten items displayed on a TableViewCell, the code:
class HomeViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var data : NSArray = []
var mainData : NSArray = []
var couponsImg : [UIImage] = []
var couponsTitle : [String] = []
var couponsDesc : [String] = []
var couponsCat : [String] = []
func getCoupons(){
let miURL = URL(string: RequestConstants.requestUrlBase)
let request = NSMutableURLRequest(url: miURL!)
request.httpMethod = "GET"
if let data = try? Data(contentsOf: miURL! as URL) {
do {
let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? NSDictionary
let parseJSON = json
let object = parseJSON?["object"] as! NSDictionary
let mainCoupon = object["mainCoupon"] as! NSArray
let coupons = object["coupons"] as! NSArray
self.mainData = mainCoupon
self.data = coupons
self.couponImg1 = (mainCoupon[0] as AnyObject).value(forKey: "urlImage") as! String
self.couponImg2 = (mainCoupon[1] as AnyObject).value(forKey: "urlImage") as! String
self.couponTitle1 = (mainCoupon[0] as AnyObject).value(forKey: "nameStore") as! String
self.couponTitle2 = (mainCoupon[1] as AnyObject).value(forKey: "nameStore") as! String
self.couponDesc1 = (mainCoupon[0] as AnyObject).value(forKey: "promoDescription") as! String
self.couponDesc2 = (mainCoupon[1] as AnyObject).value(forKey: "promoDescription") as! String
self.couponCat1 = (mainCoupon[0] as AnyObject).value(forKey: "category") as! String
self.couponCat2 = (mainCoupon[1] as AnyObject).value(forKey: "category") as! String
self.couponsImg = [couponImage1!, couponImage2!, couponImage3!, couponImage4!, couponImage5!, couponImage6!, couponImage7!, couponImage8!, couponImage9!, couponImage10!]
self.couponsTitle = [couponTitle1, couponTitle2, couponTitle3, couponTitle4, couponTitle5, couponTitle6, couponTitle7, couponTitle8, couponTitle9, couponTitle10]
self.couponsDesc = [couponDesc1, couponDesc2, couponDesc3, couponDesc4, couponDesc5, couponDesc6, couponDesc7, couponDesc8, couponDesc9, couponDesc10]
self.couponsCat = [couponCat1, couponCat2, couponCat3, couponCat4, couponCat5, couponCat6, couponCat7, couponCat8, couponCat9, couponCat10]
} catch {
let error = ErrorModel()
error.phrase = "PARSER_ERROR"
error.code = -1
error.desc = "Parser error in get Notifications action"
}
}
}
#IBAction func showFilters(_ sender: Any) {
let vc = self.storyboard!.instantiateViewController(withIdentifier: "filters") as! FiltersViewController
self.present(vc, animated: false, completion: nil)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! HomeTableViewCell
cell.couponImg.image = couponsImg[indexPath.row]
cell.couponTitle.text = couponsTitle[indexPath.row]
cell.couponDescription.text = couponsDesc[indexPath.row]
cell.couponCategory.text = couponsCat[indexPath.row]
return cell
}
(Again I’ve only showed you two coupons for the example). The thing is that I need to apply some filters to the coupons on the TableCell. The first time the view appear it shows the 10 coupons correctly, but when I go to the filters an put it some of them ON it doesn’t make a difference, the method I was trying to use was something like this, first have an instance of the FiltersViewController class:
var filters = FilterViewController()
if filters.isMovingToParentViewController == true {
if filters.restaurantsSwitch.isOn == false {
self.couponsImg.remove(at: 0)
self.couponsImg.remove(at: 1)
self.couponsImg.remove(at: 2)
}
if filters.sportsSwitch.isOn == false {
self.couponsImg.remove(at: 3)
self.couponsImg.remove(at: 4)
self.couponsImg.remove(at: 5)
}
}
In the example bellow I’m trying to say that if a have the restaurant switch off, I’m going to delete the corresponding coupons of the restaurant category, and the same with the sports switch. But first of all I don’t know where to include this logic, in which method? And also I don’t know if this instruction is correct for my purposes. Can somebody give me a hand please???
Your logic is not working because you're instantiating a new FilterViewController, different from the FilterViewController associated with you screen.
You can solve this using delegate.
First, create the delegate:
protocol FilterDelegate {
func updateTable() }
Then, In your FilterViewController add this line:
weak var delegate:FilterDelegate?
You HomeViewController have to conform with this delegate, so:
class HomeViewController: FilterDelegate ... {
func updateTable() {
/* GET THE DATA FILTERED HERE */
tableview.reloadData()
}
In your FilterViewController:
#IBAction func returnHome(_ sender: Any) {
let vc = self.storyboard!.instantiateViewController(withIdentifier: "home") as! HomeViewController
self.delegate = vc
self.present(vc, animated: false, completion: nil)
delegate?.updateTable()
}
I think that should work.
EDIT:
Another approach is to create a segue between these two vcs and pass the which filters are active using the "prepare" function . Then you can take this information in your HomeVC and load your table based on the filters in the viewDidLoad function.
1 - Create a object Filters:
class Filters {
var tuxtlaSwitchIsOn: Bool
var sevillaSwitchIsOn: Bool
...
init(tuxtlaSwitchIsOn: Bool, sevillaSwitchIsOn: Bool, ...) {
self.tuxtlaSwitchIsOn = tuxtlaSwitchIsOn
self.sevillaSwitchIsOn = sevillaSwitchIsOn
...
}
}
2 - Add a attribute Filters to your HomeVC
class HomeViewController : ... {
...
var filtersActive: Filters?
...
}
3 - In your FilterViewController instantiate a Filter object indicating which filters are on
4 - In your FilterViewController prepare funs pass the Filter object to HomeVC
5 - In your HomeVC, get the Filter object and filter your data based on it.
Sure here is what you need. So you have a set of array filled with data and you want to apply filter on them. First, you need to create another array for filter results. This is because when user removes the filter, you still want to show the full list. To simplify, say you only have an array Foo: [String]. So you need to create another array called FooFiltered: [String] to hold the search result. Your can leave it empty when the view controller is loaded.
Next, in your filter section, it's recommended to use array filter technology like this post, but it's okay if you want to do it in your way. So all you need to do is to get elements from Foo array that match certain criteria and copy them into FooFiltered array. Here let me show you an example of doing filter manually
func filter() {
FooFiltered = [String]() //Clean up every time before search
for str in Foo {
if str == "criteria" {
FooFiltered.append(str)
}
}
}
Now you have a list of filtered items. You need a flag to tell table view which set of array to display. Say you have a flag called showSearchResult that is set to false originally. When you do the filter, set it to true. So your cellForRow will look like
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if showSearchResult {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as! Cell
cell.textField.text = FooFiltered[indexPath.row]
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as! Cell
cell.textField.text = Foo[indexPath.row]
return cell
}
}
You also need to update this flag to all your table view delegate method, like numberOfRowsInSection, etc.
Finally, with these codes, your table view is configured to show full results or filtered results base on the flag and you are setting that flag in the filter() function. The last thing to do is to ask tableView to reload data when the filter is done. So modify your filter function like this and you should be all set.
func filter() {
FooFiltered = [String]() //Clean up every time before search
showSearchResul = true
for str in Foo {
if str == "criteria" {
FooFiltered.append(str)
}
}
self.tableView.reloadData()
}

SWIFT value.. has no member

I am new to swift and am looking for some help with this particular function I am trying to write.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let identifier = segue.identifier {
switch identifier {
case "Show Album":
let albumViewController = segue.destination as! AlbumViewController
let albumImageView = (sender as AnyObject).view as! UIImageView
if let index = index(of: covers, albumImageView) {
let album = Album(index: index)
albumViewController.album = album
}
On the line:
let albumImageView = (sender as AnyObject).view as! UIImageView
Getting the error:
ambiguous use of 'view'
Any help is appreciated.
sender as AnyObject casts sender to type AnyObject. AnyObject does not have a property with the name view, so calling .view on it does not make sense.

Casting as String to Int fails

I have been having some issues with my prepareForSegue call. Whenever I try to segue to the next view controller. It gets an error and crashes at the point where I am passing the tripLikes to the next view controller.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "toDetailScene") {
//Hooking up places-holder values
let viewController = segue.destinationViewController as! DetailViewController
let cell = sender as! CustomPFTableViewCell
viewController.tripName = cell.nameTextLabel.text!
viewController.tripAuthor = cell.authorTextLabel.text!
viewController.tripLikes = Int(cell.numLikes.text!)!
viewController.tripDescrip = cell.descriptionHolder
}
}
Each of the values that we are passing to are values in the destination view controller.
You're doing a lot of force-unwrapping with Int(cell.numLikes.text!)!. If any of those values are nil, your program will crash.
Why not try something safer, such as an if-let flow:
if let text = cell.numLikes.text {
if let textInt = Int(text) {
viewController.tripLikes = textInt
} else { // Int cannot be constructed from input
viewController.tripLikes = 0
}
} else { // cell.numLikes.text was nil
viewController.tripLikes = 0
}

Issue passing data from API

This code is from the locations tableView view controller, where the locations are serialized from a Four Square API and I grab the data, passing it back the view controller where I create an event. The data is serialized correctly, populating the tableView and all, so I won't bother posting that for now, just the segue method to pass the data.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let indexPath = self.tableView.indexPathForSelectedRow
let item = self.responseItems![indexPath!.row]
var eventVC = CreateEventViewController()
if segue.identifier == "pushBackToEventVC" {
if let venue = item["venue"] as? NSDictionary {
let locationString = venue["name"] as! String
let location = venue["location"]
//get lat/long strings
print(location)
eventVC.updateWithLocation(locationString)
eventVC.updateWithGeoPoint(PFGeoPoint(latitude: locationLatitude, longitude: locationLongitude))
}
eventVC = segue.destinationViewController as! CreateEventViewController
//pass location coordinates
}
//the rest of the segue method
}
The update methods in the crete event view controller, when I put a breakpoint on these methods I can see the data has been passed correctly, and the location (a PFGeoPoint) works okay but the locationString (a string) throws a "Bad instruction error" although it does print it correctly to the console
func updateWithLocation(locationString: String) {
print(locationString)
self.locationLabel.text = locationString
}
func updateWithGeoPoint(venueLocation: PFGeoPoint) {
// print(venueLocation)
self.event?.location = venueLocation
self.eventLocation = venueLocation
print(self.eventLocation)
}
Any ideas? It's basically working but won't update the label with the string although I can see it's been passed correctly. Thanks for the help as always.
Try using this code block instead,
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "pushBackToEventVC") {
let detailViewController = ((segue.destinationViewController) as! CreateEventViewController)
let indexPath = self.tableView!.indexPathForSelectedRow!
detailViewController.locationString = venue["location"]
}
}

How do you fetch NSSet from NSManagedObject in Swift?

I have a one to many relationship from Set to Card for a basic Flashcard App modelled in my Core Data.
Each Set has a set name, set description, and a relationships many card1s. Each Card1 has a front, back, and photo. In my table view, I've managed to retrieve all saved Sets from core data and display them. Now I want to fetch each Set's cards when a user clicks on the appropriate cell in my next view controller.
This is my code for the table view controller:
// MARK: Properties
var finalArray = [NSManagedObject]()
override func viewDidLoad() {
getAllSets()
println(finalArray.count)
}
func getAllSets() {
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext!
let fetchRequest = NSFetchRequest(entityName:"Set")
var error: NSError?
let fetchedResults = managedContext.executeFetchRequest(fetchRequest,error: &error) as? [NSManagedObject]
println("Am in the getCardSets()")
if let results = fetchedResults {
finalArray = results
println(finalArray.count)
}
else {
println("Could not fetch \(error), \(error!.userInfo)")
}
}
// MARK: Displaying the data
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return finalArray.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! SetTableViewCell
let sets = finalArray[indexPath.row]
cell.setName.text = sets.valueForKey("setName")as? String
cell.setDescription.text = sets.valueForKey("setDescription")as? String
return cell
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "ShowDetail" {
let dest = segue.destinationViewController as! Display
// Get the cell that generated this segue.
if let selectedCell = sender as? SetTableViewCell {
let indexPath = tableView.indexPathForCell(selectedCell)!
let selectedSet = finalArray[indexPath.row]
dest.recievedSet = selectedSet
}
}
}
In my destination view controller, how would I go about retrieving all the cards in that the recievedSet? I've tried converting the NSSet to an array and casting it to a [Card1] array but when I attempt to display the first Card1's front String property onto the label, the app crashes, giving me the error
CoreData: error: Failed to call designated initializer on NSManagedObject class 'NSManagedObject'
fatal error: Array index out of range
This is my code for the detailed viewController.
#IBOutlet weak var front: UILabel!
var finalArray = [Card1]()
finalArray = retrievedSet.allObjects as![Card1]
front.text = finalArray[0].front
Give your detail controller a property of type CardSet (I use "CardSet" because "Set" is a Swift built-in type name). You pass the selected set to this controller.
You could have a property by which you sort, or generate an array without a particular order with allObjects.
var cardArray = [Card1]()
var cardSet: CardSet?
viewDidLoad() {
super.viewDidLoad()
if let validSet = cardSet {
cardArray = validSet.cards.allObjects as! [Card1]
}
}
Your code is not working because finalArray is of type [CardSet], so finalArray[indexPath.row] is of type CardSet which is not transformable into type NSSet. Rather the relationship to Card1s is the NSSet you are looking for.
Finally, I recommend to give the detail controller a NSFetchedResultsController, have an attribute to sort by and use the passed CardSet in the fetched results controller's predicate.

Resources