Issue in Delegate while passing data back to ViewController - ios

I'm having a problem passing array data back from one view controller ("VC2") to another ("VC1"). I do everything by the rules. I made a proper protocol in VC1.
But unfortunately I could not get the data back.
This is my code:
VC2
protocol RecivedData {
func dataRecived(nameArray: [String] , priceArray: [String])
}
var popUpdelegate : RecivedData?
#IBAction func nextBtnTapped(_ sender: UIButton) {
print("Hello")
let namedata = itemNameArr
let namePrice = itemPriceArr
self.popUpdelegate?.dataRecived(nameArray: namedata, priceArray: namePrice)
print(namedata)
print(namePrice)
self.view.removeFromSuperview()
}
VC1
class HomeVC: UIViewController , RecivedData {
func dataRecived(nameArray: [String], priceArray: [String]) {
itemNameArr += nameArray
itemPriceArr += priceArray
print(itemNameArr, itemPriceArr)
print ("This is HomeVC")
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "sendSegue"{
let secondVC: AddOnItemPopUpVC = segue.destination as! AddOnItemPopUpVC
secondVC.popUpdelegate = self
}
}
}

Replace your code with this
protocol RecivedData : class {
func dataRecived(nameArray: [String] , priceArray: [String])
}
And
weak var popUpdelegate : RecivedData?
Now it will start working.
Make sure there will be no typo in segue name.

Related

How can I pass dictionary from one view controller class to another?SWIFT

I am trying to make a list of users and their passwords in one view controller, save that information in a dictionary, and send that dictionary to another view controller which asks the user to input their username/password combination to authorize the log in. (the key is the username and the value is the password). Is there a way I can send the dictionary from SecondVC to the FirstVC?
First View Controller
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var Username: UITextField!
#IBOutlet weak var Verification: UILabel!
#IBOutlet weak var Password: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
Username.delegate = self
Password.delegate = self
}
var usersDict = [String : String]()
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let des = segue.destination as? AccountViewController {
des.usersDict = usersDict
}
}
#IBAction func Authorization(_ sender: Any) {
for ( key , value ) in usersDict{
let v = key.count
var start = 0
if start <= v{
if Username.text == key{
if Password.text == value{
Verification.text = "Looks Good"
}
}
else{
start += 1
}
}
else{
Verification.text = "Yikes"
}
}
}
}
Second View Controller
class AccountViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var CreateUsername: UITextField!
#IBOutlet weak var CreatePassword: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
CreateUsername.delegate = self
CreatePassword.delegate = self
// Do any additional setup after loading the view.
}
var usersDict = [ String : String ]()
#IBAction func MakeANewAccount(_ sender: Any) {
usersDict[CreateUsername.text!] = CreatePassword.text!
}
}
I have made there dictionary, but it will only send in the beginning and won't update after creating the original account. (dictionary it is sending is empty)
With a segue add this method inside ViewController
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let des = segue.destination as? AccountViewController {
des.usersDict = yourDicHere
}
}
Here's a general pattern for making a controller work with data from some object it creates, in this case a second controller.
Try applying it to your situation and let me know if you run into problems.
protocol Processor {
func process(_ dict: [String : String])
}
class FirstController: UIViewController, Processor {
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let controller = segue.destination as? SecondController {
controller.delegate = self
} else {
print("Unexpected view controller \(segue.destination)")
}
}
func process(_ dict: [String : String]) {
}
}
class SecondController: UIViewController {
var delegate: Processor?
func someWork() {
if let processor = delegate {
processor.process(["Name" : "Pwd"])
} else {
print("Delegate not assigned")
}
}
}

Can not pass Data To ContainerView

I have tried 2 methods to pass the data from ViewController to ContainerView, with and without segue
Here is without segue method
ViewController
class DetailPostBookReviewVC: UIViewController {
var postid: String!
#IBAction func Menubutton(_ sender: Any) {
print(postid!)
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "MenuBookReviewVC") as! MenuBookReviewVC
vc.favpostid = postid
}
ContainerView
class MenuBookReviewVC: UIViewController {
var favpostid = String()
#IBAction func Deletepost(_ sender: Any) {
print(favpostid)
}
}
result: favposid has Nill Value
UPDATE this is with segue method
class DetailPostBookReviewVC: UIViewController {
var postid: String!
#IBAction func Menubutton(_ sender: Any) {
print(postid!)
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?){
if (segue.identifier == "toMenuBookReviewVC") { //"toMenuBookReviewVC" is identifier
let vc = segue.destination as! MenuBookReviewVC
vc.favpostid = postid!
}
}
Pass your data like. User prepare(for:sender:)
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if
segue.identifier == "MyIdentifierInStorybiard", // Set that
let controller = segue.destination as? MenuBookReviewVC {
controller.favpostid = postid
}
}
I think you postid is not String type so print the null value
In this way, you can't pass data for the container view. if in this way without presenting controller and push controller you can use the global variable then direct pass data and use any controller you want to use.
Example
import UIKit
class ViewController: UIViewController {
var postid: String!
override func viewDidLoad() {
super.viewDidLoad()
postid = "23" // You change your post ID
}
#IBAction func Menubutton(_ sender: Any) {
favpostid = postid
}
}
var favpostid : String!
class MenuBookReviewVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
print(favpostid)
}
}
For Passing Data To Container View you can use this
UserDefaults.standard.set(value, forKey: "SomeKey")
after your data is used you can clear that default value.
UserDefaults.standard.set("", forKey: "SomeKey")

Protocol function sends the value back to the first VC but does not set it to the variables

I am learning IOS, and I have three view controllers A, B and C, and I can access C from B and B from A, then I send back data using this delegate method from C to A then I want to use these received data in A, and finally update the text in the textView of VC A, but it always updates the textView using the default values not the received ones.
class A
protocol isAbleToReceiveData{
func pass(book: String, chapter: Int)
}
class AViewController: UIViewController, isAbleToReceiveData{
var verses: [DBTVerse] = []
var text: String = ""
var currentBook: String = "Test"
var currentChapter: Int = 1
#IBOutlet weak var TextView: UITextView!
func pass(book: String, chapter: Int) {
self.currentBook = book
self.currentChapter = chapter
print(currentBook, currentChapter)
// current output is ok "the received data"
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
getVerses(book: self.currentBook, Chapter: NSNumber(value: self.currentChapter))
print(currentBook, currentChapter)
// current output "test" "1" while it should be the received data
}
func data(verses: [DBTVerse]) {
for verse in verses{
if let chapter: Int = verse.verseId?.intValue{
text.append(String(chapter))
text.append(verse.verseText)
}
}
updateData(text: text)
}
func updateData(text: String){
if let textView = self.versesTextView {
textView.text = text
textView.setNeedsDisplay()
}
}
func getVerses(book: String, Chapter: NSNumber) {
DBT.getTextVerse(withDamId: "ARBWTCO1ET", book: book, chapter: Chapter, verseStart: nil, verseEnd: nil, success: { (verse) in
if let verse = verse {
self.verses = verse as! [DBTVerse]
self.data(verses: self.verses)
}
}) { (error) in
if let error = error {
print("Error \(error)")
}
}
}
}
class C
class CTableViewController: UITableViewController {
var currentBook: String = ""
var chapters: [DBTChapter] = []
var AVC = AViewController()
var delegate: isAbleToReceiveData?
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = AVC
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let selectedChapter = Int(chapters[indexPath.row].chapterId) {
doDismiss(book: currentBook, chapter: selectedChapter)
}
}
func doDismiss(book: String, chapter: Int) {
if let delegate = self.delegate{
delegate.pass(book: book, chapter: chapter)
}
// Use presentingViewController twice to go back two levels and call
// dismissViewController to dismiss both viewControllers.
self.presentingViewController?.presentingViewController?.dismiss(animated: true, completion: nil)
}
So in general A receives the data but it does not override it to the variable so I can use it later or in the viewWillAppear.
You have to tell to next Viewcontroller who is it delegate like so if you are in A VC ande B goes trhow a segue then add in A viewController
class AViewController: UIViewController, isAbleToReceiveData{
//all class details
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToClassB"{
let vc = segue.destination as! bViewController
vc.delegate = self
}
}
in class B then you have a new property like c with
class b: UIVIewcontroller{
var delegate: isAbleToReceiveData?
So in the func to segue to next viewController c VC add this func
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToClassC"{
let vc = segue.destination as! CTableViewController
vc.delegate = self.delegate
}
}

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

How to transfer data between view controller using segues

I'm trying to pass data using a prepare(for segue:) function but it's showing nil in the second VC. Am I doing anything wrong?
class ViewController: UIViewController {
var first : [String] = []
#IBOutlet weak var passField: UITextField!
#IBOutlet weak var userID: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func login(_ sender: Any) {
let user : String = self.userID.text!
let password : String = self.passField.text!
if user != "" && password != "" {
let postString = ["username":user, “password”: password]
var request = URLRequest(url:URL(string:"http://mydomainhere.com/api/login")!)
request.httpMethod = "POST"
request.httpBody = try! JSONSerialization.data(withJSONObject: postString, options:.prettyPrinted)
let task = URLSession.shared.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in
if error != nil {
print("error=\(error)")
return
}
do {
if let json = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String: Any] {
let firstName = json["first_name"] as? String
let lastName = json["last_name"] as? String
self.first.append(firstName!) //putting into Array
self.performSegue(withIdentifier: "loginSegue", sender: self)
}
} catch {
print(error)
}
}
}
}
// data transfer to another controller
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "loginSegue" {
let secondController = segue.destination as? SecondVC
secondController?.name = first //passing to next VC //* here having the issue its not passing the data to next VC
print(first) // here first is printing perfectly
}
}
}
// second View Controller
class SecondVC: UIViewController {
var menu_vc : MenuViewController!
var name : [String]? // passing to this Array
override func viewDidLoad() {
super.viewDidLoad()
print(name) // here printing nil
}
}
As suggested by #Sweeper, it could very well be that your destination view controller is embedded in a UINavigationViewController, hence your segue.destination is in fact a UINavigationViewController, not a SecondVC.
You can try this code:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
var destinationViewController = segue.destination
if let navigationController = destinationViewController as? UINavigationController {
destinationViewController = navigationController.visibleViewController ?? destinationViewController
}
if let secondController = destinationViewController as? SecondVC {
secondController?.name = first
}
}
Of course the first four lines of code could be refactored in an appropriate function (even better if in an extension of UIViewController).
If that solves the problem, you can watch cs193p Stanford iOS course for further details.
In particular watch https://www.youtube.com/watch?v=HQrXM2zUPvY&index=6&list=PLPA-ayBrweUz32NSgNZdl0_QISw-f12Ai starting from the 30:20 mark.
Everything seems perfect with the below snippet
var first : [String] = []
#IBAction func btnTapped(_ sender: Any) {
let firstName = "iOS Geek"
self.first.append(firstName)
self.performSegue(withIdentifier: "MovetoSecVC", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "MovetoSecVC"{
let secVC = segue.destination as! SecondVC
secVC.name = first
print(first) // it'll print here ["iOS Geek"]
}
}
// Your Second View Controller
class SecondVC: UIViewController {
var menu_vc : MenuViewController!
var name : [String]? // passing to this Array
override func viewDidLoad() {
super.viewDidLoad()
print(name!) // it'll print here ["iOS Geek"]
}
}

Resources