Segue arrays not passing why? - ios

I'm at my wits end. I've got basically the same thing working (but with a simpler function to generate the variables in a test project) but now my code is passing blank arrays to the next view controller.
I'm just wanting to pass 2 arrays, the values for which are generated through a function, to the next VC - I've triple checked my code and I'm sure I've done it right but obviously not! Any ideas!?
1st VC :
import UIKit
class WorkoutSetupController: UIViewController {
#IBOutlet weak var timeInputField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()}
//Create function to generate a new random workout, with a random exercise array, and random reps array
func generateNewWorkout() -> (randomExerciseArray:[String], randomRepsArray:[Int]) {
let randomKey = Int(arc4random_uniform(4) + 3)
var workoutSet = [String]()
let possibleExercises = masterExerciseArray
var repsSet = [Int]()
while workoutSet.count < (randomKey) {
let randomIndex = Int(arc4random_uniform(UInt32(possibleExercises.count)))
workoutSet.append(possibleExercises[randomIndex])
}
while repsSet.count < (randomKey) {
repsSet.append(Int(arc4random_uniform(30)))
}
//return the values and print them to make sure the function is working (it is!)
let workoutToBePassed = workoutSet
let repsToBePassed = repsSet
print (workoutToBePassed, repsToBePassed)
return (workoutToBePassed, repsToBePassed)
}
//perform a manual segue to the WorkoutController page, using the function above to generate the values to be passed
#IBAction func workoutButtonPressed(_ sender: UIButton) {
performSegue(withIdentifier: "doItNow", sender: generateNewWorkout())
}
//set the variables to be passed to the new VC, accepting the format of the returned arrays from my function for the variables
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "doItNow" {
if let destVC = segue.destination as? WorkoutController, let variablesToBePassed = sender as? (arr1:[String],arr2:[Int]) {
print (variablesToBePassed.arr1, variablesToBePassed.arr2)
destVC.selectedWorkoutExerciseArray = variablesToBePassed.arr1
destVC.selectedWorkoutRepsArray = variablesToBePassed.arr2
}
}
}
}
second VC :
import UIKit
class WorkoutController: UIViewController {
//set the variables we're passing from the first VC with the right data type
var selectedWorkoutExerciseArray = [String]()
var selectedWorkoutRepsArray = [Int]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func printButtonPressed(_ sender: UIButton) {
//print the variables out - this is currently printing blank arrays arghhghghghh!
print (selectedWorkoutExerciseArray, selectedWorkoutRepsArray)
}
}
Where am I going wrong!? I can't see it anywhere! Please help!

The parameter labels of the tuple don't match, you have to check
let variablesToBePassed = sender as? (randomExerciseArray:[String], randomRepsArray:[Int]) {
This is a good example of the benefit of force unwrapping objects which must have a value of the expected type
let destVC = segue.destination as! WorkoutController
let variablesToBePassed = sender as! (arr1:[String],arr2:[Int])
If the code crashes it reveals the design error at once.

Related

Can't get the value of a var in another class

I'm trying to get the value of a String var from an another class, but when i'm using it on the new class, the value is empty.
I've got the MainViewController.swift class with :
var movieIDSelected = String()
#IBAction func tapPosterButton(_ sender: UIButton) {
switch sender.tag
{
case 101: movieIDSelected = theaterMovieID[0]
print(movieIDSelected) //The value isn't empty
break
}
}
And the second MovieViewController.swift class with :
var HomeView = ViewPop()
override func viewDidLoad() {
super.viewDidLoad()
let movieID = HomeView.movieIDSelected
print(movieID) //The value is empty
}
With your current code try this in MainVC
if let home = UIApplication.shared.keyWindow?.rootViewController as? ViewPop {
print("home exists ",home.movieIDSelected)
}
//
but you should have only 1 segue to the destinationVC and link it to the VC not a segue for every button , then implement prepareForSegue and fire performSegue inside the button action to make the segue
//
#IBAction func tapPosterButton(_ sender: UIButton) {
switch sender.tag
{
case 101: movieIDSelected = theaterMovieID[0]
print(movieIDSelected) //The value isn't empty
self.performSegue(withIdentifier: "goToNext", sender:1)
break
}
}
//
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let des = segue.destination as! MovieViewController
des.sendedValue = self.movieIDSelected
des.buttonNumber = sender as! Int
}
//
class MovieViewController : UIViewController {
var sendedValue = ""
var buttonNumber = 0 // default value won't affect
}

swift - return and pass multiple arrays and an Int via Segue

I'm nearly there with a small basic program I'm writing (still learning) and I've hit a roadblock.
I can now pass 1 array between 2 view controllers and successfully print it when I hit a button in the 2nd one.
However, what I really want to do is pass 2 arrays and an Integer, created from a function on the first VC and have them accessible via the 2nd VC.
Code for 1st VC is here :
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToWorkout" {
if let destVC = segue.destination as? WorkoutViewController {
destVC.bothArrays = self.testFunction()
}
}
//Get the new view controller using segue.destinationViewController.
//Pass the selected object to the new view controller.
}
func testFunction() -> [String] {
let randomArray1 = ["blah","blah","ploop"]
let randomArray2 = ["alan", "john"]
let randomInt = 5
return BOTH ARRAYS AND INT TO SEND TO THE NEXT VIEW CONTROLLER?
}
#IBAction func goPressed(_ sender: UIButton) {
performSegue(withIdentifier: "goToNextVC", sender: self)
}
and 2nd VC here :
class WorkoutViewController: UIViewController {
var randomArray1 = [String]()
var randomArray2 = [String]()
var randomInt = 0
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func nowButtonPressed(_ sender: UIButton) {
print(randomArray1)
print(randomArray2)
print(randomInt)
}
}
I can get it working with just one array but I need more than one array and a value to be passed! I've tried playing around with it (e.g. trying '-> [String], [String], Int) but no luck
Any help much appreciated!
You can simply use a tuple to include several variables of different types in a single variable. You should pass the tuple including all 3 variables in your performSegue function as the sender argument, then assign them to the relevant instance properties in the prepare(for:) method.
If you want to keep the function for generating the variables, you should change the return type of the function to a tuple that can fit the 3 variables.
func testFunction() -> (arr1:[String],arr2:[String],randInt:Int) {
let randomArray1 = ["blah","blah","ploop"]
let randomArray2 = ["alan", "john"]
let randomInt = 5
return (randomArray1, randomArray2, randomInt)
}
Then assign the return value of testFunction to the sender input argument of performSegue:
performSegue(withIdentifier: "goToNextVC", sender: testFunction())
Assign the variables:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToWorkout" {
if let destVC = segue.destination as? WorkoutViewController, let variablesToBePassed = sender as? (arr1:[String],arr2:[String],randInt:Int) {
destVC.randomArray1 = variablesToBePassed.arr1
destVC.randomArray2 = variablesToBePassed.arr2
destVC.randomInt = variablesToBePassed.randInt
}
}
}
As others have suggested, you can refactor your function to return a tuple, and then use that to pass to your other view controller:
//This is the tuple data type we use to pass 2 string arrays and an Int
typealias parameterTuple = ([String], [String], Int)
func testFunction() -> parameterTuple {
let randomArray1 = ["blah","blah","ploop"]
let randomArray2 = ["alan", "john"]
let randomInt = 5
return (randomArray1, randomArray2, randomInt)
}
func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToWorkout" {
if let destVC = segue.destination as? WorkoutViewController {
//refactor WorkoutViewController to have a parameters property
//of type parameterTuple, split out the tuple and pass each part to
//a different property in your WorkoutViewController
destVC.parameters = testFunction()
}
}
}

Swift passing calculated data back to previous view controller

I am creating my first simple budgeting app. Basically, I take a few user inputs like monthly income & savings goal. Then they click "start", & the app calculates stuff such as, their daily budget etc.
Here I'm running into trouble. After all the calculations, I display "how much you can spend each day" (e.g. $20 a day), which I pass forward through segues from their original inputs on the original screen.
Now, in this VC (UserInfoVC) I created a button which lets them add how much money they spent today. So when they click this "add money spent" button, I open a new VC (AddSubtractMoney) where I present a calculator where they can enter how much they spent today (i.e. $12) and click submit.
I run their input compared to their daily budget to get a New daily budget.
Now, I'm having trouble passing this updated number backwards, to display it on the previous VC on the label "dailySpendingLimitLabel". I know segues are not the best way to go about passing data backwards.
I've tried closures, but I end up getting lost in the syntax, and protocols and delegates (it's my 2nd month coding).
Is there a simple way to achieve passing this data back to the previous VC and populating the data in that previous display label?
Below is the code.
The First snippet is from the UserInfoVC where I display their originally entered data that I segued through. The Second snippet is from the AddSubtractMoney class where I placed the calculator and created an object "newestUpdate" inside a function that allows me to calculate the number they entered on the calculator minus their old daily budget. To arrive at a new budget which I want to present backwards to the UserInfoVC.
class UserInfoViewController : ViewController {
var userNamePassedOver : String?
var userDailyBudgetPassedOver : Double = 99.0
var userDailySavingsPassedOver : Double = 778.00
var userMonthlyEarningsPassedOver : Double?
var userDesiredSavingsPassedOver : Double?
var newAmountPassedBack : Double = 0.0
#IBOutlet weak var dailySavingsNumberLabel: UILabel!
#IBOutlet weak var userNameLabel: UILabel!
#IBOutlet weak var dailySpendingLimitLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
userNameLabel.text = userNamePassedOver
dailySpendingLimitLabel.text = String(format: "%.2f", userDailyBudgetPassedOver)
dailySavingsNumberLabel.text = String(format: "%.2f", userDailySavingsPassedOver)
}
#IBAction func addSubtractMoneyPressed(_ sender: UIButton) {
performSegue(withIdentifier: "addOrSubtractMoney", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "addOrSubtractMoney"{
let addOrSubtractMoneyVC = segue.destination as! AddSubtractMoney
addOrSubtractMoneyVC.dailyBudgetPassedThrough = userDailyBudgetPassedOver
}
}
}
extension UserInfoViewController: AddSubtractMoneyDelegate {
func calculatedValue(value: Double) {
dailySpendingLimitLabel.text = String(userDailyBudgetPassedOver - value)
}
}
import UIKit
protocol AddSubtractMoneyDelegate {
func calculatedValue(value: Double)
}
class AddSubtractMoney: UIViewController {
#IBOutlet weak var outputLabel: UILabel!
var runningNumber = ""
var finalNumberPassedOver : Double?
var amountPassedBackToUserInfo : Double = 0.0
var dailyBudgetPassedThrough : Double = 0.0
var delegate: AddSubtractMoneyDelegate?
override func viewDidLoad() {
super.viewDidLoad()
outputLabel.text = "0"
// Do any additional setup after loading the view.
}
#IBAction func buttonPressed(_ sender: UIButton) {
runningNumber += "\(sender.tag)"
outputLabel.text = runningNumber
}
#IBAction func submitNewInfo(_ sender: UIButton) {
// FIX FIX
AddSubtractMoneyController.addToMoneySpentArray(amountISpent: outputLabel.text!)
sendBackUpdatedNumber()
dismiss(animated: true, completion: nil)
}
#IBAction func allClearedPressed(_ sender: UIButton) {
runningNumber = ""
outputLabel.text = "0"
}
// THIS LINE PRODUCES THE CORRECT INPUT IN OUTPUT CONSOLE WHEN I PRINT- BUT I CANT FIGURE HOW TO TRANSFER IT BACK TO PREVIOUS VC
func sendBackUpdatedNumber(){
let newestUpdate = UserInfo(whatYouSpentToday: runningNumber, oldDailyBudgetPassed: dailyBudgetPassedThrough)
amountPassedBackToUserInfo = dailyBudgetPassedThrough - Double(runningNumber)!
newestUpdate.goalToSaveDaily = amountPassedBackToUserInfo
print(amountPassedBackToUserInfo)
self.delegate?.calculatedValue(value: amountPassedBackToUserInfo)
}
}
My suggestion is to use a callback closure. It's less code and easier to handle than protocol / delegate.
In AddSubtractMoney declare a callback variable and call it in sendBackUpdatedNumber passing the Double value
class AddSubtractMoney: UIViewController {
// ...
var callback : ((Double)->())?
// ...
func sendBackUpdatedNumber(){
let newestUpdate = UserInfo(whatYouSpentToday: runningNumber, oldDailyBudgetPassed: dailyBudgetPassedThrough)
amountPassedBackToUserInfo = dailyBudgetPassedThrough - Double(runningNumber)!
newestUpdate.goalToSaveDaily = amountPassedBackToUserInfo
print(amountPassedBackToUserInfo)
callback?(amountPassedBackToUserInfo)
}
}
In prepare(for segue assign the closure to the callback variable and add the code to be executed on return
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "addOrSubtractMoney"{
let addOrSubtractMoneyVC = segue.destination as! AddSubtractMoney
addOrSubtractMoneyVC.callback = { result in
print(result)
// do something with the result
}
addOrSubtractMoneyVC.dailyBudgetPassedThrough = userDailyBudgetPassedOver
}
}
Using delegate
if segue.identifier == "addOrSubtractMoney" {
let addOrSubtractMoneyVC = segue.destination as! AddSubtractMoney
addOrSubtractMoneyVC.dailyBudgetPassedThrough = userDailyBudgetPassedOver
addOrSubtractMoneyVC.delegate = self
}
}
You need to add delegate property in AddSubtractMoney class
var delegate: AddSubtractMoneyDelegate?
Create Protocol in AddSubtractMoney class
protocol AddSubtractMoneyDelegate {
func calculatedValue(value: Double)
}
And respond to delegate
func sendBackUpdatedNumber(){
let newestUpdate = UserInfo(whatYouSpentToday: runningNumber, oldDailyBudgetPassed: dailyBudgetPassedThrough)
amountPassedBackToUserInfo = dailyBudgetPassedThrough - Double(runningNumber)!
newestUpdate.goalToSaveDaily = amountPassedBackToUserInfo
print(amountPassedBackToUserInfo)
self.delegate.calculatedValue(value: amountPassedBackToUserInfo)
}
Now you need to implement this delegate method in class where delegate is set.
Here in UserInfoViewController class delegate is set so you need to implement its delegate method
extension UserInfoViewController: AddSubtractMoneyDelegate {
func calculatedValue(value: Double) {
//set label here
}
}
You could possibly also use an unwind segue to pass back the data.
If you don't under stand flow behind delegate(protocol oriented), you can simply go through below code. it only works if both class
But it is not a good practice
Learn about protocol, closure, or Notification Center broadcasting for mostly used, flexible and reusable coding methods.
UserInfoViewController
class UserInfoViewController : ViewController {
fun receiveBackUpdatedNumber(numberString:String){
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "addOrSubtractMoney"{
let addOrSubtractMoneyVC = segue.destination as! AddSubtractMoney
addOrSubtractMoneyVC.userInfoViewController = self
}
}
}
}
AddSubtractMoney
class AddSubtractMoney: UIViewController {
var userInfoViewController: UserInfoViewController!
var updatedNumber = ""
func sendBackUpdatedNumber(){
self.userInfoViewController.receiveBackUpdatedNumber(numberString: updatedNumber)
}
}
If you are confirtable you can go with protocols.. protocols insist a class to compulsory implement a method, which make code more reusable and independent.
In Above method we are passing instance of current viewcontroller(UserInfoViewController) to next viewcontroller(AddSubtractMoney) on performing segue, So by that we can access any properties of function in UserInfoViewController from AddSubtractMoney. So it make easy to pass data from AddSubtractMoney to -> UserInfoViewController

assing object with prepareForSegue becomes nil in Swift 3

Sir,
I am trying to implement a form and pass the Data object below
import UIKit
import GRDB
class Staff: Record {
var id: Int64?
var compId: Int64 = 0
var chiName: String = ""
var engName: String = ""
to the table view controller loading the child record. when it comes to implementation, it seems getting null and does not make sense. Would you please tell me how to ensure the second view controller does not receive null objects under this case ?
Below is the
Log :
Here is my code:
First UIView Controller
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
print("view salary X ")
print(dummy)
print(dummy.id ?? "0")
if let secondController = segue.destination as? ViewSalaryTableViewController {
secondController.dummyStaff = dummy
}
}
Second UITableView Controller :
public var dummyStaff : Staff?
override func viewDidLoad() {
super.viewDidLoad()
..
print("arrive dummyStaff")
print(dummyStaff ?? "njull")
}
Storyboard partial draft :
Storyboard setting
Make sure the type casting for secondController is working. If you have multiple segues, use segue identifier to distinguish. Below code worked fine for me:
class MyBook {
var name:String!
}
ViewController 1
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "Vc1ToVc2" {
let book = MyBook()
book.name = "Harry"
if let destinationVc = segue.destination as? ViewController2 {
destinationVc.book = book
}
}
}
ViewController 2
var book:MyBook?
override func viewDidLoad() {
super.viewDidLoad()
print(book?.name ?? "No name")
}
Prints: Harry

Swift sending Multiple Objects to View Controller

I am trying to send multiple objects from my initial view controller to my Username VC. Here is the segue code from my controllers: The issue comes when I add in the code to send the second object, termreport. If I delete the termsM and the assignment, it send the students as usually, but I also need to send the termReport object. How would I fix this?
ViewControler:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let students = sender as AnyObject as? [Student]
else { return }
guard let termsM = sender as AnyObject as? [TermReport] //How would I send both objects?
else { return }
if let secondVC = segue.destination as? UsernameVC {
secondVC.students = students
secondVC.userWebView = webView
secondVC.terms = termsM // not sending
}
let gradeResponse = try Parser(innerHTML)
self.performSegue(withIdentifier: "ShowStudents", sender: gradeResponse.students)
self.performSegue(withIdentifier: "ShowStudents", sender: gradeResponse.termReports) //how would I send both variables?
UsernameVC:
var terms: [TermReport]!
override func viewDidLoad() {
print("TERM \(terms[0].grades[3])")//returns found nil optional ERROR
}
You have to include all of the variables you want to send to another ViewController using a segue into a single object (which can be a collection as well). You either create a custom class/struct that has properties with type [Student] and [TermReport] or put these into a native collection (Tuple or Dictionary).
Create custom struct:
struct TermData {
var students = [Student]()
var termReports = [TermReport]()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let segueData = sender as? TermData
else { return }
if let secondVC = segue.destination as? UsernameVC {
secondVC.students = segueData.students
secondVC.userWebView = webView
secondVC.terms = segueData.termReports
}
}
let gradeResponse = try Parser(innerHTML)
let termData = TermData(students: gradeResponse.students, termReports: gradeResponse.termReports)
self.performSegue(withIdentifier: "ShowStudents", sender: termData)

Resources