how to deliver data to other ViewController? - ios

#IBAction func hallOfFame(_ sender: UIButton) {
guard let hof = self.storyboard?.instantiateViewController(withIdentifier: "HOF") as? HallOfFameTableViewController else { return }
hof.hallOfFames = self.hallOfFames.sorted(by: <)
}
Self-view has hallOfFame and it is Array.
And hof-view also has hallOfFame.
So I Send self-view's hallOfFame -> how-view's hallOfFame.
but hof-view do not receive data.
what's wrong?

if you want send the data forward in the view controller stack, you can use perform segue and prepare for segue method
https://www.youtube.com/watch?v=guSYMPaXLaw&t=595s
but if you send the data backward, you can use protocol and delegate pattern : https://www.youtube.com/watch?v=KhqXUi1SF4s&t=198s

Related

Swift: Need to move data stored using a button to another View Controller

Using a button to collect a phone number and then reading and storing the phone number under the button function.
#IBAction func viewRewardsButtonTapped(_ sender: UIButton) {
view.endEditing(true)
// Validate format
let phoneNumberInput = phoneInput.text
}
Is there a better way to store the phone number phoneNumberInput and get it to another UIViewController?
I currently can't get the other UIViewController to recognize the variables stored under the #IBAction function.
You could use UserDefaults:
// Set phone number
UserDefaults.standard.set(phoneInput,text, forKey: "phoneNumber")
// Get phone number
UserDefaults.standard.string(forKey: "phoneNumber")
Or if you want to segue at the same time that you´re clicking the button use this:
self.performSegue(withIdentifier: "showVC2", sender: phoneNumber.text)
override func prepare(for segue: UIStoryboardSegue, sender: Any!) {
if (segue.identifier == "showVC2") {
if let phoneNumber = sender as? String {
let vc = segue.destination as! YourSecondVC
vc.phoneNumber = phoneNumber
}
}
}
So you basically create the segue and declare a variable for phoneNumber in your second VC so that you can pass the value to it.
Your phoneNumberInput variable is only stored in the scope of your action handler method viewRewardsButtonTapped, so once this method is left the value is lost. You should add a member variable (var phoneNumber: String?) to your class and use this for storing the value. Then, when you open the next view controller, you can pass the value in as a parameter.
If you are using storyboard segues (which I assume), you have to implement performSegue withIdentifier, cast the destination view controller to the concrete type and set the value (e.g. targetVC.phoneNumber = self.phoneNumber).

viewDidLoad() is running before Alamofire request in prepareforsegue has a chance to finish

I have two viewControllers that are giving me trouble. One is called notificationAccessViewController.swift and the other is called classChooseViewController.swift. I have a button in the notificationAccessViewController that triggers a segue to the classChooseViewController. What I need to do is within the prepareForSegue function, perform an Alamofire request and then pass the response to the classChooseViewController. That works! BUT, not fast enough.
Below is my prepareForSegue function within the notificationAccessViewController
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let DestViewController = segue.destination as! classChooseViewController
Alamofire.request("MYURL.com").responseJSON { (response) in
if let JSON : NSDictionary = response.result.value as! NSDictionary?{
let classNameArray = JSON["className"] as! NSArray
print("---")
print(classNameArray)
DestViewController.classNameArray = classNameArray
}
}
}
I have it printing out classNameArray to the console, which it does SUCCESSFULLY. Although, in my classChooseViewController.swift, I also print out the classNameArray to the console and it prints with nothing inside of it. This is because the viewDidLoad() function of the classChooseViewController.swift is running before the prepareForSegue function has finished. I want to know what I can do to ensure that the classChooseViewController does not load until the prepareForSegue function has FINISHED running.
Below is my code for classChooseViewController
class classChooseViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var classNameArray: NSArray = []
override func viewDidLoad() {
super.viewDidLoad()
print(classNameArray)
}
}
I have attached a picture of what the console reads. You can see that the classNameArray prints from the classChooseViewControllers viewDidLoad() function BEFORE the prepareForSegue() function because the "---" prints out after.
Your design is wrong.
The Alamofire request function is asynchronous. It returns immediately, before the results are ready. Then it invokes the completion handler that you pass in.
I tend to agree with Paul that you should make the request in the destination view controller, not in the current view controller.
If you DO want to fetch the data in Alamofire before you segue to the other view controller then you'll need to write a method that starts the request, then invokes the segue to the other view controller from the Alamofire.request() call's completion handler.
If you're invoking the new view controller from a button press, you need to not link the button directly to a segue, but instead write an IBAction method that triggers the AlamoFire request call, then invokes the segue (or instantiates the view controller directly and pushes/presents it manually) from the completion handler.
Something like this:
#IBAction buttonAction(sender: UIButton) {
Alamofire.request("MYURL.com").responseJSON {
(response) in
if let JSON : NSDictionary = response.result.value as! NSDictionary? {
let classNameArray = JSON["className"] as! NSArray
print("---")
print(classNameArray)
let theClassChooseViewController = storyboard.instantiateViewController(withIdentifier:"ClassChooseViewController" as ClassChooseViewController
theClassChooseViewController.classNameArray = classNameArray
presentViewController(theClassChooseViewController,
animated: true,
completion: nil)
}
}
}
By the way, class names and types should start with an upper case letter, and variable names should start with a lower-case letter. This is a very strong convention in both Swift and Objective-C, and you'll confuse the hell out of other iOS/Mac developers unless you follow the convention.

Swift - Passing data coming from an API to another view controller

Please check the below code:
#IBAction func sendActivationCode(_ sender: UIButton) {
service.Register(phoneNumber: self.mobileNumberTxt.text!, callback: { (response) in
self.setCustomerValues(response: response)
})
}
func setCustomerValues(response: [String:Any]) {
registrationToken = (response["token"]! as! String)
code = response["code"] as! Int
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "toStep2" {
let vc = segue.destination as! Step2ViewController
vc.registrationToken = registrationToken
}
}
The problem is: prepare function is executed before setCustomerValues and I cannot use registrationToken variable in Step2ViewController.swift because it's nil.
Instead of connecting your segue from the button to Step2ViewController, connect it from the view controller. This way the segue will not automatically be performed when the button is touched.
Then call performSegue from within your setCustomerValues callback to perform the segue explicitly after getting the registration token. Note that if the callback is not on the main thread, you will need to dispatch_async to the main thread before calling performSegue.
You should push viewcontroller after self.setCustomerValues(response: response). Don't push viewcontroller when sendActivationCode
The best way to come out of this problem is to create an IBAction method from your button on a Touch Up Inside Event and not create any Segues on 'action' of your button.
Use the following code:
#IBAction func sendActivationCode(_ sender: UIButton) {
service.Register(phoneNumber: self.mobileNumberTxt.text!, callback: {
(response) in
let vc = self.storyboard?.instantiateViewControllerWithIdentifier("Step2ViewController") as! Step2ViewController
vc.registrationToken = (response["token"]! as! String)
vc.code = response["code"] as! Int
self.navigationController?.pushViewController(vc!, animated: true)
})
}

Who came first? IBAction or ViewDidLoad

I have a Button on First VC which is directed to two active states.
1) SecondVC
override func viewDidLoad() {
super.viewDidLoad()
subjectPickerView.dataSource = self
subjectPickerView.delegate = self
SwiftyRequest()
// Used the text from the First View Controller to set the label
}
func SwiftyRequest(){
print("SecondViewController METHOD BEGINS")
let jsonobj = UserDefaults.standard.object(forKey: "PostData")
let json = JSON(jsonobj as Any)
for i in 0 ..< json.count{
let arrayValue = json[i]["name"].stringValue
print(arrayValue)
self.subjects.append(arrayValue)
self.subjectPickerView.reloadAllComponents()
}
print(self.subjects)
}
2) IBAction of FirstVC
#IBAction func buttonPressed(_ sender: Any) {
Alamofire.request("http://localhost/AIT/attempt3.php",method: .post, parameters: ["something": semValue, "branch" : streamValue])
.responseJSON { response in
print(response.result)
if let JSON1 = response.result.value {
print("Did receive JSON data: \(JSON1)")
// JSONData.someData = JSON1 as AnyObject?
UserDefaults.standard.set(JSON1, forKey: "PostData")
UserDefaults.standard.synchronize()
}
else {
print("JSON data is nil.")
}
}
}
NOW, Whenever i pressed the button it calls the viewDidLoad of SecondVC before IBAction of FirstVC which is a bit problematic for my app! How can i decide the priority between these two function.
You have to think about what you want to happen. Clearly the Alamofire call is going to take some time. What do you want to do with the 2nd VC while that time elapses? What do you want to do if the call does not return at all?
This is a common problem when dependent on external resources. How do you manage the UI? Do you present the UI in a partial state? Do you put a popover saying something like "loading". Or do you wait for the resource to complete before presenting the 2nd VC at all?
We cannot make that decision for you, since it depends on your requirement. There are ways to implement each one, though. If the resource usually responds quickly you could show the VC in a partial state and then populate it on some kind of callback. Typically call backs are either (1) blocks (2) delegate methods or (3) notifications. There is also (less commonly) (4) KVO. You should probably research the pros and cons of each.

Pass Data using Closures

I know that there are multiple approaches to pass data back from one controller to another like Delegates, NSNotifications. I am using another way using Closures to pass data data back. I just want to know is it safe way how I pass any data using blocks like below or should I avoid this approach.
First ViewController (where I make object of Second ViewController)
#IBAction func push(sender: UIButton) {
let v2Obj = storyboard?.instantiateViewControllerWithIdentifier("v2ViewController") as! v2ViewController
v2Obj.completionBlock = {(dataReturned) -> ()in
//Data is returned **Do anything with it **
print(dataReturned)
}
navigationController?.pushViewController(v2Obj, animated: true)
}
Second ViewController (where data is passed back to First VC)
import UIKit
typealias v2CB = (infoToReturn :NSString) ->()
class v2ViewController: UIViewController {
var completionBlock:v2CB?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func returnFirstValue(sender: UIButton) {
guard let cb = completionBlock else {return}
cb(infoToReturn: returnFirstValue)
}
#IBAction func returnSecondValue(sender: UIButton) {
guard let cb = completionBlock else {return}
cb(infoToReturn: returnSecondValue)
}
}
That's a very good and reasonable approach and much better than notifications.
Looking at the evolution of Cocoa API you will notice that Apple has replaced more and more delegate API with blocks / closures over the years.

Resources