How to save data from ViewController using NSCoding in Swift [duplicate] - ios

This question already has an answer here:
How to store value generated from a ViewController using NSCoding in Swift
(1 answer)
Closed 7 years ago.
I need to save data from a segued ViewController (“ScoreView.swift”) to “ScoreHistory.swift” using NSCoding. I tried but the data isn't showing up in "ScoreTableViewController.swift". What am I missing?
I have this ScoreView.swift which has the following code: (Pls note that this is a "segued" view where data has been passed from another ViewController)
class ScoreView: UIViewController {
var dateToday = NSDate()
var score: ScoreHistory?
var numberofquestions:String = ""
var scorepassed:String = ""
var scorepercentpassed:String = ""
var scoreremarkspassed:String = ""
var totalduration:String!
var incorrectanswerspassed:String = ""
var skippedquestionspassed:String = ""
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
datePlayedLabel.text = dateToday.description
totalScoreLabel.text = scorepassed
scorePercentage.text = scorepercentpassed
totalAnsweredLabel.text = numberofquestions
totalDurationLabel.text = totalduration
gameStatusLabel.text = "Exam Finished"
// NSCoding
if let score = score {
datePlayedLabel.text = score.datePlayed
totalScoreLabel.text = score.totalScore
totalAnsweredLabel.text = score.totalAnswered
totalDurationLabel.text = score.totalDuration
gameStatusLabel.text = score.gameStatus
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if backMenu === sender {
let datePlayed = datePlayedLabel.text ?? ""
let totalScore = totalScoreLabel.text ?? ""
let totalAnswered = totalAnsweredLabel.text ?? ""
let totalDuration = totalDurationLabel.text ?? ""
let gameStatus = gameStatusLabel.text ?? ""
// Set the score to be passed to ScoreTableViewController after the unwind segue.
score = ScoreHistory(datePlayed: datePlayed, totalScore: totalScore, totalAnswered: totalAnswered, totalDuration: totalDuration, gameStatus: gameStatus)
}
NSKeyedArchiver.archiveRootObject(score!, toFile: ScoreHistory.ArchiveURL.path!)
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(true)
datePlayedLabel.text = dateToday.description
totalScoreLabel.text = scorepassed
scorePercentage.text = scorepercentpassed
totalAnsweredLabel.text = numberofquestions
totalDurationLabel.text = totalduration
gameStatusLabel.text = "Exam Finished"
}
// Labels
}
}
I have ScoreHistory.swift, which has the following code:
class ScoreHistory: NSObject, NSCoding {
// MARK: Properties
var datePlayed: String
var totalScore: String
var totalAnswered: String
var totalDuration: String
var gameStatus: String
// MARK: Archiving Paths
static let DocumentsDirectory = NSFileManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first!
static let ArchiveURL = DocumentsDirectory.URLByAppendingPathComponent("scores")
// MARK: Types
struct PropertyKey {
static let datePlayedKey = "datePlayed"
static let totalScoreKey = "totalScore"
static let totalAnsweredKey = "totalAnswered"
static let totalDurationKey = "totalDuration"
static let gameStatusKey = "gameStatus"
}
// MARK: Initialization
init?(datePlayed: String, totalScore: String, totalAnswered: String, totalDuration: String, gameStatus: String) {
// Initialize stored properties.
self.datePlayed = datePlayed
self.totalScore = totalScore
self.totalAnswered = totalAnswered
self.totalDuration = totalDuration
self.gameStatus = gameStatus
super.init()
}
// MARK: NSCoding
func encodeWithCoder(aCoder: NSCoder) {
aCoder.encodeObject(datePlayed, forKey: PropertyKey.datePlayedKey)
aCoder.encodeObject(totalScore, forKey: PropertyKey.totalScoreKey)
aCoder.encodeObject(totalAnswered, forKey: PropertyKey.totalAnsweredKey)
aCoder.encodeObject(totalDuration, forKey: PropertyKey.totalDurationKey)
aCoder.encodeObject(gameStatus, forKey: PropertyKey.gameStatusKey)
}
required convenience init?(coder aDecoder: NSCoder) {
let datePlayed = aDecoder.decodeObjectForKey(PropertyKey.datePlayedKey) as! String
let totalScore = aDecoder.decodeObjectForKey(PropertyKey.totalScoreKey) as! String
let totalAnswered = aDecoder.decodeObjectForKey(PropertyKey.totalAnsweredKey) as! String
let totalDuration = aDecoder.decodeObjectForKey(PropertyKey.totalDurationKey) as! String
let gameStatus = aDecoder.decodeObjectForKey(PropertyKey.gameStatusKey) as! String
// Must call designated initializer.
self.init(datePlayed: datePlayed, totalScore: totalScore, totalAnswered: totalAnswered, totalDuration: totalDuration, gameStatus: gameStatus)
}
}
Here is the full code of ScoreTableViewController.swift:
class ScoreTableViewController: UITableViewController {
// MARK: Properties
var scores = [ScoreHistory]()
var dateToday = NSDate()
override func viewDidLoad() {
super.viewDidLoad()
// Load any saved scores, otherwise load sample data.
if let savedScores = loadScores() {
scores += savedScores
} else {
// Load the sample data.
loadSampleScores()
}
}
func loadSampleScores() {
let score1 = ScoreHistory(datePlayed: dateToday.description, totalScore: "0", totalAnswered: "0", totalDuration: "0", gameStatus: "started")!
scores += [score1]
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return scores.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// Table view cells are reused and should be dequeued using a cell identifier.
let cellIdentifier = "ScoreHistoryTableViewCell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! ScoreHistoryTableViewCell
// Fetches the appropriate note for the data source layout.
let score = scores[indexPath.row]
cell.datePlayedLabel.text = score.datePlayed
cell.totalScoreLabel.text = score.datePlayed
cell.totalScoreLabel.text = score.totalScore
cell.totalAnsweredLabel.text = score.totalAnswered
cell.totalDurationLabel.text = score.totalDuration
cell.gameStatusLabel.text = score.gameStatus
return cell
}
// Override to support conditional editing of the table view.
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
return true
}
// Override to support editing the table view.
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
// Delete the row from the data source
scores.removeAtIndex(indexPath.row)
saveScores()
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
}
}
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "ShowDetail" {
let scoreDetailViewController = segue.destinationViewController as! ScoreViewController
// Get the cell that generated this segue.
if let selectedScoreCell = sender as? ScoreHistoryTableViewCell {
let indexPath = tableView.indexPathForCell(selectedScoreCell)!
let selectedScore = scores[indexPath.row]
scoreDetailViewController.score = selectedScore
}
}
}
// MARK: NSCoding
func saveScores() {
let isSuccessfulSave = NSKeyedArchiver.archiveRootObject(scores, toFile: ScoreHistory.ArchiveURL.path!)
if !isSuccessfulSave {
print("Failed to save scores...")
}
}
func loadScores() -> [ScoreHistory]? {
return NSKeyedUnarchiver.unarchiveObjectWithFile(ScoreHistory.ArchiveURL.path!) as? [ScoreHistory]
}
#IBAction func unwindToScoreList(sender: UIStoryboardSegue) {
if let sourceViewController = sender.sourceViewController as? ScoreViewController, score = sourceViewController.score {
if let selectedIndexPath = tableView.indexPathForSelectedRow {
// Update an existing note.
scores[selectedIndexPath.row] = score
tableView.reloadRowsAtIndexPaths([selectedIndexPath], withRowAnimation: .None)
// Add a new score.
let newIndexPath = NSIndexPath(forRow: scores.count, inSection: 0)
scores.append(score)
tableView.insertRowsAtIndexPaths([newIndexPath], withRowAnimation: .Bottom)
saveScores()
}
}
}
}
GOAL: My goal is to record/store all session data from “ScoreView.swift” whenever a user finishes a quiz game.
The "ScoreView" is shown after each quiz game, I plan to record each quiz results in "ScoreHistory.swift." How do I do it?

The easiest solution is to save the changed values from the UITextField instances back to the score instance in ScoreView (why is score optional at all since you always pass a non-optional score instance ??) and unwind the segue.
Then the array is saved in the method unwindToScoreList of ScoreTableViewController
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if backMenu === sender {
score?.datePlayed = datePlayedLabel.text ?? ""
score?.totalScore = totalScoreLabel.text ?? ""
score?.totalAnswered = totalAnsweredLabel.text ?? ""
score?.totalDuration = totalDurationLabel.text ?? ""
score?.gameStatus = gameStatusLabel.text ?? ""
}
}
No archiving in ScoreView !

Your loadScores function is loading an archived array of scores:
func loadScores() -> [ScoreHistory]? {
return NSKeyedUnarchiver.unarchiveObjectWithFile(ScoreHistory.ArchiveURL.path!) as? [ScoreHistory]
}
In your segue, you are only archiving a single score. You can't archive a ScoreHistory instance and expect to unarchive a ScoreHistory array. Where you currently have:
score = ScoreHistory(datePlayed: datePlayed, totalScore: totalScore, totalAnswered: totalAnswered, totalDuration: totalDuration, gameStatus: gameStatus)
You need to change this to:
var scores = loadScores() ?? []
score = ScoreHistory(datePlayed: datePlayed, totalScore: totalScore, totalAnswered: totalAnswered, totalDuration: totalDuration, gameStatus: gameStatus)
scores.append(score)
saveScores(scores)
Where loadScores and saveScores are the same as the code in ScoreTableViewController, although I've added the scores to save as a parameter given this code creates a local var.
UPDATE: It's late and I wasn't paying enough attention. You need to handle loadScores returning nil, and of course scores should be var not let or you won't be able to add to it. With these changes, scores should no longer be optional, so you won't need to unwrap it.

Related

Can only delete an object from the Realm it belongs to Swift

I'm trying do delete the note from Realm in my NotesApp and facing this error: "Can only delete an object from the Realm it belongs to". This note has been saved before also in Realm and
could display it in my TableView by tapping on the date in my FSCalendar. I tried to replace realm.add(item) with realm.create(item), but also got the error: "Cannot convert value of type 'T' to expected argument type 'Object.Type' (aka 'RealmSwiftObject.Type')". I'm new in programming, so any help would be appreciated. Here's the relevant code code:
in my ToDoListItem.swift
class ToDoListItem: Object {
#objc dynamic var noteName: String = ""
#objc dynamic var date: Date = Date()
#objc dynamic var descriptionText: String = ""
#objc dynamic var noteImage = Data()
init(date: Date, noteName: String) {
self.date = date
self.noteName = noteName
}
override init() {
self.noteName = ""
self.date = Date()
self.descriptionText = ""
self.noteImage = Data()
}
}
in my RealmManager.swift
class RealmManager {
static let shared = RealmManager()
private let realm = try! Realm()
func write<T: Object>(item: T) {
realm.beginWrite()
realm.add(item)
try! realm.commitWrite()
}
func getObjects<T: Object>(type: T.Type) -> [T] {
return realm.objects(T.self).map({ $0 })
}
func delete<T: Object>(item: T) {
try! realm.write {
realm.delete(item)
}
}
}
in my ViewController where i can edit and delete the notes
#IBAction func didTapDelete() {
let note = ToDoListItem()
RealmManager.shared.delete(item: note)
self.deletionHandler?()
navigationController?.popToRootViewController(animated: true)
}
and finally in my TableViewController where the notes are displayed (honestly i think the problem is hidden here but cannot find it...
#IBOutlet var tableViewPlanner: UITableView!
#IBOutlet var calendarView: FSCalendar!
private var data = [ToDoListItem]()
var datesOfEvents: [String] {
return self.data.map { DateFormatters.stringFromDatestamp(datestamp: Int($0.date.timeIntervalSince1970)) }
}
var items: [ToDoListItem] = []
func getCount(for Date: String) -> Int {
var count: [String : Int] = [:]
for date in datesOfEvents {
count[date] = (count[date] ?? 0) + 1
}
return count[Date] ?? 0
}
func getEventsForDate(date: Date) -> [ToDoListItem] {
let string = DateFormatters.stringFromDatestamp(datestamp: Int(date.timeIntervalSince1970))
return self.data.filter {DateFormatters.stringFromDatestamp(datestamp: Int($0.date.timeIntervalSince1970)) == string }.sorted(by: {$0.date < $1.date})
}
override func viewDidLoad() {
super.viewDidLoad()
calendarView.rounding()
tableViewPlanner.rounding()
data = RealmManager.shared.getObjects(type: ToDoListItem.self)
self.items = self.getEventsForDate(date: Date())
calendarView.delegate = self
calendarView.dataSource = self
tableViewPlanner.delegate = self
tableViewPlanner.dataSource = self
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
self.calendarView.select(Date())
self.calendarView.reloadData()
refresh()
}
//MARK:- TableView Data Source
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.items.count //data.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: K.plannerCellIdentifier, for: indexPath) as! NoteTableViewCell
let note = self.items[indexPath.row]
cell.configureCell(note: note)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
let note = data[indexPath.row]
guard
let vc = storyboard?.instantiateViewController(identifier: K.infoVCIdentifier) as? InfoViewController else { return }
vc.note = note
vc.deletionHandler = { [weak self] in
self?.refresh()
}
vc.title = note.noteName
navigationController?.pushViewController(vc, animated: true)
}
//MARK:- User Interaction
#IBAction func didTapAddButton() {
guard
let vc = storyboard?.instantiateViewController(identifier: K.entryVCIdentifier) as? EntryViewController else { return }
vc.completionHandler = { [weak self] in
self?.refresh()
}
vc.title = K.entryVCTitle
navigationController?.pushViewController(vc, animated: true)
}
func refresh() {
DispatchQueue.main.async {
self.data = RealmManager.shared.getObjects(type: ToDoListItem.self)
self.tableViewPlanner.reloadData()
self.calendarView.reloadData()
}
}
}
extension PlannerViewController: FSCalendarDelegateAppearance & FSCalendarDataSource {
func calendar(_ calendar: FSCalendar, appearance: FSCalendarAppearance, eventDefaultColorsFor date: Date) -> [UIColor]? {
let dateString = DateFormatters.yearAndMonthAndDateFormatter.string(from: date)
if self.datesOfEvents.contains(dateString) {
return [UIColor.blue]
}
return [UIColor.white]
}
func calendar(_ calendar: FSCalendar, didSelect date: Date, at monthPosition: FSCalendarMonthPosition) {
self.items = self.getEventsForDate(date: date)
self.tableViewPlanner.reloadData()
}
func calendar(_ calendar: FSCalendar, numberOfEventsFor date: Date) -> Int {
let dateString = DateFormatters.yearAndMonthAndDateFormatter.string(from: date)
let count = self.getCount(for: dateString)
self.tableViewPlanner.reloadData()
return count
}
}
The real problem is the didTapDelete function -- Why are you creating a new note just to delete it (I hope it was only to test out realm delete syntax). You should delete the note object that you passed to the view controller to edit / delete. (vc.note in did select row -> self.note in the other VC - where didTapDelete is)
So your did tap delete will look like --
RealmManager.shared.delete(item: note)
//show deleted alert & go back
A little explanation on the error - Just instantiating a Realm object (ToDoListItem()) does not add it to Realm (the Database system). To delete / edit a realm object, it has to be either fetched from a realm (RealmManager.shared.getObjects(type: ToDoListItem.self)) or added to the realm.
I'd advise going through a Realm tutorial before jumping in the code (there are plenty of them)

How to display a multidimensional array under different sections in swift 3

I have been trying to create multidimensional arrays and display each array under each section .. but I keep ending up with a fatal error index out of range . The code that i display below is how I access my firebase data and print each array from the database.. issue is I get the data I assign an array to it create it but can’t seem to display it due to the error . Hope someone helps for reference regarding the error I have attached a screenshot as well.
#IBOutlet weak var tableview: UITableView!
var yourArray = [String]()
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
var sundaycoursenamearray = [String]()
var sundaycoursecodearray = [String]()
var sundaycourselocationarray = [String]()
var sundayfromarray = [String]()
var sundaytoarray = [String]()
var mondaycoursenamearray = [String]()
var mondaycoursecodearray = [String]()
var mondaycourselocationarray = [String]()
var mondayfromarray = [String]()
var mondaytoarray = [String]()
var tuesdaycoursenamearray = [String]()
var tuesdaycoursecodearray = [String]()
var tuesdaycourselocationarray = [String]()
var tuesdayfromarray = [String]()
var tuesdaytoarray = [String]()
var wednesdaycoursenamearray = [String]()
var wednesdaycoursecodearray = [String]()
var wednesdaycourselocationarray = [String]()
var wednesdayfromarray = [String]()
var wednesdaytoarray = [String]()
var thursdaycoursenamearray = [String]()
var thursdaycoursecodearray = [String]()
var thursdaycourselocationarray = [String]()
var thursdayfromarray = [String]()
var thursdaytoarray = [String]()
var fridaycoursenamearray = [String]()
var fridaycoursecodearray = [String]()
var fridaycourselocationarray = [String]()
var fridayfromarray = [String]()
var fridaytoarray = [String]()
var saturdaycoursenamearray = [String]()
var saturdaycoursecodearray = [String]()
var saturdaycourselocationarray = [String]()
var saturdayfromarray = [String]()
var saturdaytoarray = [String]()
var coursecodes = [[String]]()
var coursenames = [[String]]()
var courselocations = [[String]]()
var fromtimes = [[String]]()
var totimes = [[String]]()
var days = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"]
override func viewWillAppear(_ animated: Bool) {
let uid = Auth.auth().currentUser?.uid
if(uid == nil){
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let nextviewController = storyboard.instantiateViewController(withIdentifier: "loginscreen")
//self.present(profileViewController, animated: true, completion: nil)
self.present(nextviewController, animated: true, completion: nil)
}
for j in days {
for i in 1 ..< 10 {
let ref1 = Database.database().reference().child("users").child((uid)!).child("courses").child(j).child(String(i))
ref1.observeSingleEvent(of: .value, with: { snapshot in
if let dictionary = snapshot.value as? [String: AnyObject] {
// print(dictionary)
if j == "Sunday" {
if let points = dictionary["coursname"] as? String {
self.sundaycoursecodearray.append(points)
print(self.sundaycoursecodearray)
}
if let points1 = dictionary["coursecode"] as? String {
self.sundaycoursenamearray.append(points1)
print(self.sundaycoursenamearray)
}
if let points1 = dictionary["courseroomlocation"] as? String {
self.sundaycourselocationarray.append(points1)
print(self.sundaycourselocationarray)
}
if let points1 = dictionary["fromtime"] as? String {
self.sundayfromarray.append(points1)
print(self.sundayfromarray)
}
if let points1 = dictionary["totime"] as? String {
self.sundaytoarray.append(points1)
print(self.sundaytoarray)
}
}
if j == "Monday" {
if let points = dictionary["coursname"] as? String {
self.mondaycoursecodearray.append(points)
print(self.mondaycoursecodearray)
}
if let points1 = dictionary["coursecode"] as? String {
self.mondaycoursenamearray.append(points1)
print(self.mondaycoursenamearray)
}
if let points1 = dictionary["courseroomlocation"] as? String {
self.mondaycourselocationarray.append(points1)
print(self.mondaycourselocationarray)
}
if let points1 = dictionary["fromtime"] as? String {
self.mondayfromarray.append(points1)
print(self.mondayfromarray)
}
if let points1 = dictionary["totime"] as? String {
self.mondaytoarray.append(points1)
print(self.mondaytoarray)
}
}
if j == "Tuesday" {
if let points = dictionary["coursname"] as? String {
self.tuesdaycoursecodearray.append(points)
print(self.tuesdaycoursecodearray)
}
if let points1 = dictionary["coursecode"] as? String {
self.tuesdaycoursenamearray.append(points1)
print(self.tuesdaycoursenamearray)
}
if let points1 = dictionary["courseroomlocation"] as? String {
self.tuesdaycourselocationarray.append(points1)
print(self.tuesdaycourselocationarray)
}
if let points1 = dictionary["fromtime"] as? String {
self.tuesdayfromarray.append(points1)
print(self.tuesdayfromarray)
}
if let points1 = dictionary["totime"] as? String {
self.tuesdaytoarray.append(points1)
print(self.tuesdaytoarray)
}
}
if j == "Wednesday" {
if let points = dictionary["coursname"] as? String {
self.wednesdaycoursecodearray.append(points)
print(self.wednesdaycoursecodearray)
}
if let points1 = dictionary["coursecode"] as? String {
self.wednesdaycoursenamearray.append(points1)
print(self.wednesdaycoursenamearray)
}
if let points1 = dictionary["courseroomlocation"] as? String {
self.wednesdaycourselocationarray.append(points1)
print(self.wednesdaycourselocationarray)
}
if let points1 = dictionary["fromtime"] as? String {
self.wednesdayfromarray.append(points1)
print(self.wednesdayfromarray)
}
if let points1 = dictionary["totime"] as? String {
self.wednesdaytoarray.append(points1)
print(self.wednesdaytoarray)
}
}
if j == "Thursday" {
if let points = dictionary["coursname"] as? String {
self.thursdaycoursecodearray.append(points)
print(self.thursdaycoursecodearray)
}
if let points1 = dictionary["coursecode"] as? String {
self.thursdaycoursenamearray.append(points1)
print(self.thursdaycoursenamearray)
}
if let points1 = dictionary["courseroomlocation"] as? String {
self.thursdaycourselocationarray.append(points1)
print(self.thursdaycourselocationarray)
}
if let points1 = dictionary["fromtime"] as? String {
self.thursdayfromarray.append(points1)
print(self.thursdayfromarray)
}
if let points1 = dictionary["totime"] as? String {
self.thursdaytoarray.append(points1)
print(self.thursdaytoarray)
}
}
if j == "Friday" {
if let points = dictionary["coursname"] as? String {
self.fridaycoursecodearray.append(points)
print(self.fridaycoursecodearray)
}
if let points1 = dictionary["coursecode"] as? String {
self.fridaycoursenamearray.append(points1)
print(self.fridaycoursenamearray)
}
if let points1 = dictionary["courseroomlocation"] as? String {
self.fridaycourselocationarray.append(points1)
print(self.fridaycourselocationarray)
}
if let points1 = dictionary["fromtime"] as? String {
self.fridayfromarray.append(points1)
print(self.fridayfromarray)
}
if let points1 = dictionary["totime"] as? String {
self.fridaytoarray.append(points1)
print(self.fridaytoarray)
}
}
if j == "Saturday" {
if let points = dictionary["coursname"] as? String {
self.saturdaycoursecodearray.append(points)
print(self.saturdaycoursecodearray)
}
if let points1 = dictionary["coursecode"] as? String {
self.saturdaycoursenamearray.append(points1)
print(self.saturdaycoursenamearray)
}
if let points1 = dictionary["courseroomlocation"] as? String {
self.saturdaycourselocationarray.append(points1)
print(self.saturdaycourselocationarray)
}
if let points1 = dictionary["fromtime"] as? String {
self.saturdayfromarray.append(points1)
print(self.saturdayfromarray)
}
if let points1 = dictionary["totime"] as? String {
self.saturdaytoarray.append(points1)
print(self.saturdaytoarray)
}
}
self.coursecodes.append(self.sundaycoursenamearray)
self.coursecodes.append(self.mondaycoursenamearray)
self.coursecodes.append(self.tuesdaycoursenamearray)
self.coursecodes.append(self.wednesdaycoursenamearray)
self.coursecodes.append(self.thursdaycoursenamearray)
self.coursecodes.append(self.fridaycoursenamearray)
self.coursecodes.append(self.saturdaycoursenamearray)
self.coursenames.append(self.sundaycoursecodearray)
self.coursenames.append(self.mondaycoursecodearray)
self.coursenames.append(self.tuesdaycoursecodearray)
self.coursenames.append(self.wednesdaycoursecodearray)
self.coursenames.append(self.thursdaycoursecodearray)
self.coursenames.append(self.fridaycoursecodearray)
self.coursenames.append(self.saturdaycoursecodearray)
self.courselocations.append(self.sundaycourselocationarray)
self.courselocations.append(self.mondaycourselocationarray)
self.courselocations.append(self.tuesdaycourselocationarray)
self.courselocations.append(self.wednesdaycourselocationarray)
self.courselocations.append(self.thursdaycourselocationarray)
self.courselocations.append(self.fridaycourselocationarray)
self.courselocations.append(self.saturdaycourselocationarray)
self.fromtimes.append(self.sundayfromarray)
self.fromtimes.append(self.mondayfromarray)
self.fromtimes.append(self.tuesdayfromarray)
self.fromtimes.append(self.wednesdayfromarray)
self.fromtimes.append(self.thursdayfromarray)
self.fromtimes.append(self.fridayfromarray)
self.fromtimes.append(self.saturdayfromarray)
self.totimes.append(self.sundaytoarray)
self.totimes.append(self.mondaytoarray)
self.totimes.append(self.tuesdaytoarray)
self.totimes.append(self.wednesdaytoarray)
self.totimes.append(self.thursdaytoarray)
self.totimes.append(self.fridaytoarray)
self.totimes.append(self.saturdaytoarray)
self.tableview.reloadData()
}
})
} }
super.viewWillAppear(animated)
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return days[section]
}
func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return days.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return coursenames[section].count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! homeTableViewCell
// Configure the cell...
// cell.classcode?.text = sundaycoursenamearray[indexPath.section]
cell.classcode?.text = coursenames[indexPath.section][indexPath.row]
return cell
}
/*
// Override to support conditional editing of the table view.
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
return true
}
*/
/*
// Override to support editing the table view.
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
// Delete the row from the data source
tableView.deleteRows(at: [indexPath], with: .fade)
} else if editingStyle == .insert {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
*/
/*
// Override to support rearranging the table view.
override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) {
}
*/
/*
// Override to support conditional rearranging of the table view.
override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
// Return false if you do not want the item to be re-orderable.
return true
}
*/
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
override func viewDidLoad() {
super.viewDidLoad()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
}
You should create a model for your data then create arrays that include the model.
You can create a simple struct like so:
struct Course {
var name : String?
var code: String?
var location: String?
var toTime: String?
var fromTime: String?
}
After you create the model you can create an array that contains this type of struct.
Also for numberOfRowsInSection you don't have to use subscript. Just return:
return coursenames.count
That is why you get the error.

Read data from firebase and populate TableViewCell

Hello I have a tableviewcell where i can populate it with custom data from my pc, but i can't use my firebase data on the cell that i have made. I want to fill my cell with String and Int, not only Strings. My code is:
PlacesTableViewController Class
import UIKit
import FirebaseDatabase
class PlacesTableViewController: UITableViewController {
//MARK: Properties
#IBOutlet weak var placesTableView: UITableView!
//database reference
var dbRef:FIRDatabaseReference?
var places = [Places]()
var myList:[String] = []
//handler
var handle:FIRDatabaseHandle?
override func viewDidLoad() {
super.viewDidLoad()
dbRef = FIRDatabase.database().reference()
// Loads data to cell.
loadData()
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return places.count
//return myList.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Table view cells are reused and should be dequeued using a cell identifier.
let cellIdentifier = "PlacesTableViewCell"
guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? PlacesTableViewCell else {
fatalError("The dequeued cell is not an instance of PlacesTableView Cell.")
}
let place = places[indexPath.row]
cell.placeLabel.text = place.name
cell.ratingControl.rating = place.rating
//cell.placeLabel.text = myList[indexPath.row]
//cell.ratingControl.rating = myRatings[indexPath.row]
return cell
}
//MARK: Private Methods
private func loadData() {
handle = dbRef?.child("placeLabel").observe(.childAdded, with: { (snapshot) in
if let item = snapshot.value as? String
{
self.myList.append(item)
self.placesTableView.reloadData()
print (item)
}
})
/* handle = dbRef?.child("rating").observe(.childAdded, with: { (snapshot) in
if let item = snapshot.value as? String
{
self.myList.append(item)
self.placesTableView.reloadData()
}
})*/
/*guard let place1 = Places(name: "Veranda", rating: 4) else {
fatalError("Unable to instantiate place1")
}
places += [place1]*/
}
}
Places Class
import UIKit
class Places {
//MARK: Properties
var name: String
var rating: Int
//MARK:Types
struct PropertyKey {
static let name = "name"
static let rating = "rating"
}
//MARK: Initialization
init?(name: String, rating: Int) {
// Initialize stored properties.
self.name = name
self.rating = rating
// Initialization should fail if there is no name or if the rating is negative.
// The name must not be empty
guard !name.isEmpty else {
return nil
}
// The rating must be between 0 and 5 inclusively
guard (rating >= 0) && (rating <= 5) else {
return nil
}
}
}
PlacesTableViewCell Class
import UIKit
import FirebaseDatabase
class PlacesTableViewCell: UITableViewCell, UITableViewDelegate {
//MARK: Properties
#IBOutlet weak var placeLabel: UILabel!
#IBOutlet weak var ratingControl: RatingControl!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
Firebase Database
Assuming your database layout should instead look like this (see comments above):
...
placeLabel
|
-- XXY: "Veranda"
-- YYY: "Dio Con Dio"
rating
|
-- XXX: 4
-- YYY: 1
...
then try this:
private func loadData() {
dbRef!.child("placeLabel").observe(.childAdded) {
(snapshot) in
let label = snapshot.value as! String
self.updatePlace(snapshot.key, label: label)
}
dbRef!.child("rating").observe(.childAdded) {
(snapshot) in
let rating = snapshot.value as! Int
self.updatePlace(snapshot.key, rating: rating)
}
}
private var loadedLabels = [String: String]()
private var loadedRatings = [String: Int]()
private func updatePlace(_ key: String, label: String? = nil, rating: Int? = nil) {
if let label = label {
loadedLabels[key] = label
}
if let rating = rating {
loadedRatings[key] = rating
}
guard let label = loadedLabels[key], let rating = loadedRatings[key] else {
return
}
if let place = Places(name: label, rating: rating) {
places.append(place)
placesTableView.reloadData()
}
}
By the way, you can temporarily hack your database — using Firebase (nice!) web console — if you want to quickly validate the above solution.
Writing to Database. Try the following code to write the nodes in your database (i.e., this code reuses the same key across all place properties):
let key = dbRef!.child("placeLabel").childByAutoId().key
dbRef!.child("placeLabel").child(key).setValue(placeLab‌​el.text)
dbRef!.child("comment").child(key).setValue(commentText‌​Field.text)
dbRef!.child("rating").child(key).setValue(ratingContro‌​l.rating)
Hacking the Database. To edit the database manually, try:
open http://console.firebase.google.com
select your app
open database option
add a new node with the right key
delete the old node

tableview error while searching

Hi I have two arrays and only one array is updating with search bar.. I keep the TitleArray to show in tableView title and detailsArray to show in tableView subtitle.. once I start searching only title following my typing but subtitle nothing change.
#IBOutlet weak var AirportsTableView: UITableView!
var TitleArray = [String]()
var DetailsArray = [String]()
var NumberOfRows = 0
var filteredNamesArray = [String]()
var filteredDetailsArray = [String]()
var resultSearchController = UISearchController!()
**override func viewDidLoad() {
super.viewDidLoad()**
// Do any additional setup after loading the view.
self.resultSearchController = UISearchController(searchResultsController: nil)
self.resultSearchController.searchResultsUpdater = self
self.resultSearchController.dimsBackgroundDuringPresentation = false
self.resultSearchController.searchBar.sizeToFit()
self.resultSearchController.loadViewIfNeeded()
self.AirportsTableView.tableHeaderView = self.resultSearchController.searchBar
self.AirportsTableView.reloadData()
parseJSON()
}
func parseJSON() {
if let path = NSBundle.mainBundle().pathForResource("airports", ofType: "json") {
do {
let data = try NSData(contentsOfURL: NSURL(fileURLWithPath: path), options: NSDataReadingOptions.DataReadingMappedIfSafe)
let jsonObj = JSON(data: data)
if jsonObj != JSON.null {
// print("jsonData:\(jsonObj)")
NumberOfRows = jsonObj.count
for i in 0...NumberOfRows {
let City = jsonObj[i]["city"].string as String!
let Country = jsonObj[i]["country"].string as String!
let Iata = jsonObj[i]["iata"].string as String!
let Name = jsonObj[i]["name"].string as String!
self.TitleArray.append("\(City) - \(Country) - \(Iata)")
self.DetailsArray.append("\(Name)")
}
} else {
print("could not get json from file, make sure that file contains valid json.")
}
} catch let error as NSError {
print(error.localizedDescription)
}
} else {
print("Invalid filename/path.")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
// MARK: - Table view data source
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
if self.resultSearchController.active
{
return self.filteredNamesArray.count
} else
{
return self.TitleArray.count
}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as UITableViewCell?
if self.resultSearchController.active
{
cell!.textLabel?.text = self.filteredNamesArray[indexPath.row]
} else
{
cell!.textLabel?.text = self.TitleArray[indexPath.row]
cell!.detailTextLabel?.text = self.DetailsArray[indexPath.row]
}
return cell!
}
func updateSearchResultsForSearchController(searchController: UISearchController) {
self.filteredNamesArray.removeAll(keepCapacity: false)
let searchPredicate = NSPredicate(format: "SELF CONTAINS[c] %#", searchController.searchBar.text!)
let array = (self.TitleArray as NSArray).filteredArrayUsingPredicate(searchPredicate)
self.filteredNamesArray = array as! [String]
self.AirportsTableView.reloadData()
}
// MARK: - Segues
/*
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "AirportDetails" {
if let indexPath = self.AirportsTableView.indexPathForSelectedRow {
let airportDetail : Airports = TitleArray[indexPath.row]
let controller = (segue.destinationViewController as! UINavigationController).topViewController as! AllWaysFlightsViewController
controller.airportDetail = airportDetail
controller.navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem()
controller.navigationItem.leftItemsSupplementBackButton = true
}
}
}
*/
Instead of using two separate arrays use only one array and populate it with object containing both variables you are using to populate the tableView.
class Address {
var city: String
var detail: String
init(city: String, detail:String) {
self.city = city
self.detail = detail
}
}
Parse your json like this:
for i in 0...NumberOfRows {
let City = jsonObj[i]["city"].string as String!
let Country = jsonObj[i]["country"].string as String!
let Iata = jsonObj[i]["iata"].string as String!
let Name = jsonObj[i]["name"].string as String!
let city = "\(City) - \(Country) - \(Iata)"
let address = Address(city: city, detail: Name)
self.TitleArray.append(address)
self.filteredNamesArray.append(address)
}
Filter your title array containing addresses. Your titlearray and filtered array both contains same data for the first time you can refer to the json parsing for this. Here you can use one for filtering and when search bar is empty it user cancel his search you can re-populate your array from the other one.
func updateSearchResultsForSearchController(searchController: UISearchController) {
self.filteredNamesArray.removeAll(keepCapacity: false)
let searchPredicate = NSPredicate(format: "SELF.city CONTAINS[c] %#", searchController.searchBar.text!)
let array = (self.TitleArray as NSArray).filteredArrayUsingPredicate(searchPredicate)
self.filteredNamesArray = array as! [Address]
self.AirportsTableView.reloadData()
}
your tableView logic will be changed accordingly
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return self.filteredNamesArray.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as UITableViewCell?
let address = self.filteredNamesArray[indexPath.row]
cell!.textLabel?.text = address?.city
cell!.detailTextLabel?.text = address?.detail
return cell!
}
You need to change the way you approach filtering the data so that rather than just apply a predicate you explicitly iterate and check the predicate, if you find a match then you take that item and the corresponding description into your filtered arrays.
Something like:
func updateSearchResultsForSearchController(searchController: UISearchController) {
self.filteredNamesArray.removeAll(keepCapacity: false)
self.filteredDetailsArray.removeAll(keepCapacity: false)
let searchString = searchController.searchBar.text!
var index = 0
for title in self.TitleArray
if title.rangeOfString(searchString).location != NSNotFound {
self.filteredNamesArray.append(title)
self.filteredDetailsArray.append(self.DetailsArray[index])
}
index += 1
}
self.AirportsTableView.reloadData()
}

Send array by segue to new view controller swift iOS 9

I am attempting to send an array of data to a new view controller and I'm currently getting the error fatal error: unexpectedly found nil while unwrapping an Optional value
Im using the API data from www.thecocktaildb.com
Example:
http://www.thecocktaildb.com/api/json/v1/1/search.php?s=margarita
Not sure what I'm doing wrong. Tried debugging and checking values before the segue in my search view controller and they're accurate.
Heres my code:
Main Storyboard
SearchViewController
class SearchViewController: UIViewController, UISearchBarDelegate, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var TableView: UITableView!
#IBOutlet weak var SearchBar: UISearchBar!
var valueToPass:Drinks!
var isSearching : Bool = false
class Drinks {
var idDrink: Int = 0
var strDrink: String = ""
var strCategory: String = ""
var strAlcoholic: String = ""
var strGlass: String = ""
var strInstructions: String = ""
var strDrinkThumb: String = ""
var strIngredient1: String = ""
var strIngredient2: String = ""
var strIngredient3: String = ""
var strIngredient4: String = ""
var strIngredient5: String = ""
var strIngredient6: String = ""
var strIngredient7: String = ""
var strIngredient8: String = ""
var strIngredient9: String = ""
var strIngredient10: String = ""
var strIngredient11: String = ""
var strIngredient12: String = ""
var strIngredient13: String = ""
var strIngredient14: String = ""
var strIngredient15: String = ""
var strMeasure1: String = ""
var strMeasure2: String = ""
var strMeasure3: String = ""
var strMeasure4: String = ""
var strMeasure5: String = ""
var strMeasure6: String = ""
var strMeasure7: String = ""
var strMeasure8: String = ""
var strMeasure9: String = ""
var strMeasure10: String = ""
var strMeasure11: String = ""
var strMeasure12: String = ""
var strMeasure13: String = ""
var strMeasure14: String = ""
var strMeasure15: String = ""
}
var TableData:Array< Drinks > = Array < Drinks >()
override func viewDidLoad() {
super.viewDidLoad()
for subView in self.SearchBar.subviews
{
for subsubView in subView.subviews
{
if let textField = subsubView as? UITextField
{
textField.attributedPlaceholder = NSAttributedString(string: NSLocalizedString("Search", comment: ""))
}
}
}
self.SearchBar.delegate = self
self.TableView.delegate = self
self.TableView.dataSource = self
}
func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
if self.SearchBar.text!.isEmpty {
self.isSearching = false
}else{
self.isSearching = true
let userSearchInput = self.SearchBar.text!.lowercaseString
let newString = userSearchInput.stringByReplacingOccurrencesOfString(" ", withString: "%20", options: NSStringCompareOptions.LiteralSearch, range: nil)
let postEndpoint: String = "http://www.thecocktaildb.com/api/json/v1/1/search.php?s=" + newString
guard let url = NSURL(string: postEndpoint) else {
print("Error: cannot create URL")
return
}
let urlRequest = NSURLRequest(URL: url)
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config)
let task = session.dataTaskWithRequest(urlRequest, completionHandler: { (data, response, error) in
guard let responseData = data else {
print("Error: did not receive data")
return
}
guard error == nil else {
print("error calling GET on www.thecocktaildb.com")
print(error)
return
}
let post: NSDictionary
do {
post = try NSJSONSerialization.JSONObjectWithData(responseData,
options: []) as! NSDictionary
} catch {
print("error trying to convert data to JSON")
return
}
var count = 1
if let drinks = post["drinks"] as? [NSDictionary] {
self.TableData.removeAll()
for drink in drinks {
let adrink = Drinks()
if let strDrink = drink["strDrink"] as? String {
print(String(count) + ". " + strDrink)
adrink.strDrink = strDrink
count++
}
if let strCategory = drink["strCategory"] as? String {
print(" Category: " + strCategory)
adrink.strCategory = strCategory
}
if let strDrinkThumb = drink["strDrinkThumb"] as? String {
print(" Thumbnail Image: " + strDrinkThumb)
adrink.strDrinkThumb = strDrinkThumb
}
self.TableData.append(adrink)
self.TableView.reloadData()
}
}
})
task.resume()
}
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return TableData.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
//title = TableData[indexPath.row].strDrink
cell.textLabel?.text = TableData[indexPath.row].strDrink;
let imageString = TableData[indexPath.row].strDrinkThumb
if (imageString == ""){
let noDrinkImage : UIImage = UIImage(named: "noimage.jpg")!
cell.imageView!.image = noDrinkImage
}else{
let drinkImage = UIImage(data: NSData(contentsOfURL: NSURL(string:TableData[indexPath.row].strDrinkThumb)!)!)
cell.imageView!.image = drinkImage
}
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
print(TableData[indexPath.row].strDrink)
valueToPass = TableData[indexPath.row]
//self.performSegueWithIdentifier("drinkSegue", sender: TableData[indexPath.row])
}
// hide kwyboard when search button clicked
func searchBarSearchButtonClicked(searchBar: UISearchBar) {
self.SearchBar.resignFirstResponder()
}
// hide keyboard when cancel button clicked
func searchBarCancelButtonClicked(searchBar: UISearchBar) {
self.SearchBar.text = ""
self.SearchBar.resignFirstResponder()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "DrinkSegue") {
// initialize new view controller and cast it as your view controller
let drinkViewController = segue.destinationViewController as! DrinkViewController
// your new view controller should have property that will store passed value
drinkViewController.passedValue = valueToPass
}
}
}
DrinkViewController.swift
class DrinkViewController: UIViewController {
#IBOutlet weak var DrinkNameLabel: UILabel!
var passedValue : SearchViewController.Drinks!
override func viewDidLoad() {
super.viewDidLoad()
DrinkNameLabel.text = passedValue!.strDrink
// 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.
}
}
Do it like this instead
In your didSelectRowAtIndexPath pass the array
self.performSegueWithIdentifier("drinkSegue", sender: TableData[indexPath.row])
Here you need to pass the array to your DrinkViewController
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "DrinkSegue") {
// initialize new view controller and cast it as your view controller
let drinkViewController = segue.destinationViewController as! DrinkViewController
// your new view controller should have property that will store passed value
drinkViewController.passedValue = valueToPass
// declare myArray in your drinkViewController and then assign it here
// now your array that you passed will be available through myArray
drinkViewController.myArray = sender
}
}
Update
After I got your project I noticed that the issue you had was that you did drag a segue from the tableView to the drinksController directly - what happened is that didSelectRowAtIndexPath will not be called and your sender will always be nil drinkViewController.myArray = sender as! Drinks.
I changed that by dragging the segue from the viewController to the drinksController instead.

Resources