Is prepareForSegue the correct way to pass values ​​between view controllers

I am trying to learn Swift and I am trying to develop a famous note taking app.

There is an array associated with the table view, and another view for adding notes. In the second view, the textfieldshouldreturn event fires segue and returns to the table.

I wanted to know if this is correct. Because by doing this, I am manipulating a variable in another view controller. I am not an MVC master, but I felt it was wrong. Here is my code snippet:

func textFieldShouldReturn(textField: UITextField) -> Bool { self.performSegueWithIdentifier("backSegue", sender: self) return true } override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { if(segue.identifier == "backSegue"){ let navController = segue.destinationViewController as UINavigationController; let myController = navController.topViewController as NotesTableViewController; if(self.ourTextField?.text != nil || self.ourTextField?.text != ""){ myController.notes.append(self.ourTextField?.text ?? ""); } } } 

Thanks.

+6
source share
4 answers

Your question is not about prepareForSegue , but about the relationship between controllers. The reason your design "feels wrong" is because it is there. The problem is that your note recording controller knows too much about the view controller, which uses it , because it directly manipulates the variable from the call controller. To manipulate a variable directly, it must know the class of the caller.

Why is this a problem? This makes your note pad less reusable. If you write the note controller correctly, you can reuse it in other applications. To make it reusable, you need to separate the note recording controller from the caller - it does not need to know who exactly calls it.

So the question is, how do I pass the data back to the caller if I don’t know who called me? The answer is delegation .

Delegation works as follows:

  • You create a protocol , which describes the method or methods that will be implemented by the implementer of this protocol. In your case, you can use a protocol such as NoteWriterDelegate , which implements the takeNote(note: String) method.

     protocol NoteWriterDelegate { func takeNote(note: String) } 

    Define this in the file with the note view controller.

  • Your post writer will have an optional delegate pointer:

     weak var delegate: NoteWriterDelegate? 
  • You need to declare your first view controller as NoteWriterDelegate :

     class ViewController: UITableViewController, NoteWriterDelegate 
  • And then we implement the required method in your first view controller:

     func takeNote(note: String) { notes.append(note) } 
  • When you call prepareForSegue in preparation for moving to the note view controller, you pass yourself as a delegate:

     destinationViewController.delegate = self 
  • In the view controller of a note entry, when you have a note to pass back to the caller, you call takeNote on the delegate:

     delegate?.takeNote(self.ourTextField?.text ?? "") 

This way, your writer will notice that he is talking to NoteWriterDelegate . If you want to reuse this in the future, you just release the class for writing notes to another project, implement the delegate, and it works without having to touch the code in the class for writing notes.

+7
source

I would recommend passing data through prepareForSegue in most cases. It is quite simple to configure and easy to understand.

However, I would recommend never updating interface elements (labels, text fields, etc.) directly in the destination view. In my opinion, this is a bad connection, which creates a lot of problems.

Instead, create a property or properties on the destination view controller, which the caller can set to prepareForSegue to pass data to it. These should be special target properties used exclusively for data transfer. The destination view controller is then responsible for using the data in these properties to update its user interface or internal state.

Delegation is a valid approach, but I find it too complicated for most situations. This requires more customization and is more abstract. This abstraction is not required in many ways by the controller. If you find that you need to reuse the view controller, you can always reorganize it later.

+2
source

I don't think prepareSegue is the perfect way to pass data between view controllers ... at least not directly.

I share your concern about using prepareForSegue to pass values ​​between view controllers. The source view controller should not know anything about the destination view controller (and vice versa, for that matter). Ideally , view controllers should be separate islands without being visible to each other .

To address the clutch that storyboards seem to encourage, Ive often used some form of intermediary pattern to pass data between controllers. Here is a pretty good blog post on how to implement a version of this template around the storyboard: http://coding.tabasoft.it/ios/mediator-pattern-in-swift/ . As always, this template may not be the most suitable for all situations, but I believe that it was a good solution in many of my past projects.

In principle, how a mediator template will work within the framework of the storyboard paradigm is that in each view the controllers prepareForSegue method, the segue object is passed to the mediator object. The view controller does not care about what is inside or where the navigation system is; he just knows that he is not visible. The broker that just passed the segue object (containing the source and destination view controllers) is then responsible for transferring data between the source and destination view controllers.

Using this template, each view controller is blissfully unaware of the existence of another. On the other hand, the mediator class should be aware of the relationships between the view controllers (and the view manager interfaces) in the navigation path. Obviously, if the navigation changes or the view controllers themselves change, the mediation class will need to be adjusted. Each view controller, however, should not have any dependency on each other and therefore does not need to be updated to make changes to the navigation path or changes to other view controllers along this navigation path.

+2
source

This is not the “right”, but the right way. Especially in storyboard applications.

Here is an alternative way to pass a value and call a view.

 var myNewVC = NewViewController() myNewVC.data = self navigationController?.presentViewController(myNewVC, animated: true, completion: nil) 
0
source

Source: https://habr.com/ru/post/979535/


All Articles