pass value through another vc solution - ios

I pretty doubt that what is different between value by using prepare func
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "MainToTimer") {
let vc = segue.destination as! YourViewController
vc.var_name = "Your Data"
}
}
or declare global variable for example
in VC1
var justsimpleint:Int! = 0 //out side class
viewdidload(){
justsimpleint = justsimpleint + 2
}
in VC2
viewdidload(){
print(justsimpleint) // it will be 2
}

Using global variables to pass data between VCs is not a good idea. Global variables are things that should be used carefully. If you want to pass data from a View Controller A to View Controller B, which A presents, do it in the prepareForSegue method. If you want to pass data from View Controller B to View Controller A, which presents B, use the delegate pattern.
Global variables are bad for this purpose because:
They can be accessed from anywhere. This makes it easy for you to change them accidentally.
It is easier to spot errors. Say you have a global variable val that VC A and B will use. You pass some data from A to B. The first time A presented B, the data was passed successfully. The second time, the data was not sent due to some error in your logic. This would mean that B will receive the data you sent last time and everything will seem OK from the outside. If you pass the data in prepareForSegue, VC B will not get the data if no data was passed. A likely unexpectedly found nil while unwrapping optional error will occur. This makes it easy for you to tell what went wrong.
Global variables will hold references to objects, even when the VC that needs it is deallocated. This means that there will be some useless objects floating around in memory after the VC is dismissed if you don't clean it properly.

By default, assuming that you are working with storyboards, you should use the segues for passing data to the next view controller.
Global variable would be useful in case of you want it to be shared in the whole application, you could create a Singleton class for setting global properties.
Also, for passing back between view controllers, you could create a delegate to achieve it, you might want to check Passing data back from view controllers Xcode.
If you are unfamiliar with work with delegates, you might also want to check this answer.

Related

MVP: Can a view controller message a presenter of another view controller?

THere are 2 view controllers: master and detail view. They both have a presenter as I'm implementing the MVP pattern.
I need to update the data in the detail view controller.
I'm using this code in the master view controller.
detailVC.presenter?.set(data: presenter?.data[row])
I'm getting the data from the presenter of the master view controller and passing it to the presenter of the detailVC.
Is this good design?
If you create DetailVC before, you can use like that. Also u can use delegate patterns for that. MasterVC must have a delegate for DetailVC. Whenever you need to set your data you can use delegate.set(data: presenter?.data[row]) in your MasterVC class. But don't forget to set MasterVC delegate.
But if u want to create DetailVC and set parameters, u can use init method. Create an init function for your detailVC with the required parameters like that.. Write that function into your DetailVC class or create a DetailViewControllerInit class for your custom init functions with different parameters..
static func initDetailVC(data: DataType?) -> UIViewController {
let vc = UIStoryboard.... // create DetailVC here..
let presenter = viewController.presenter
presenter.set(data) // or presenter.data = data
return vc
}
After that u can create DetailVC in MasterVC like that
let detailVC = DetailVC.initDetailVC(data: presenter.data[row])
Though your implementation would certainly work, here are a couple of things to consider in order to improve it:
1) It hinges upon the master view controller knowing about a lot of different things (the detail view controller, the presenter, and the presenter's API). This can create coupling, which can make it more difficult to later refactor your code. Instead of calling detailVC.presenter?.someMethod(), I would consider adding a pass through method to detailVC that handles the calling of the presenter method (as well as anything else it needs to do at the same time) so that your architecture will be more modular and it will be easier to later swap out components.
2) I would consider decoupling the view and the model. Since MVP is really M<->P<->V in practice, ideally your model and view would not really communicate with or know about each other. Here, it seems like the view, or at least the object in which this line of code lives, knows about model when you call detailVC.presenter?.set(data: presenter?.data[row]). In order to do this decoupling, you could just have the view send an event / message prompting the presenter to do its thing, rather than manipulate the data directly. For example: detailVC.presenter?.newInputReceived(input: "hey!")
Hopefully that helps!

Getting data from another view controller (not via passing the data forward or backward) in Swift 4

Say I have View Controller A, where I have an array with data.
Now I also have a couple of other View Controllers (B,C,D, etc...)where I need that array.
I know I could pass the data forward using segues to each of the other View Controllers. But It seems not the perfect solution for me as I have to pass the data each time through a lot of different View Controllers
I would rather have a method where I define in View controller B "Get the data from View Controller A"
(As I understand passing data backwards is not what I want to achieve because I don't want to change the array in View controller B and pass it back to View Controller A. I only want to read/get the data within View Controller B from View Controller A.
Can somebody point me to a solution for this? Or do I have to pass the data forward from VC A to VC B?
I guess it is a matter of taste but I would rather write my code in VC B/C/D in case I need the data from VC A than passing each time data from A to every other VC...
Create a singleton class. let's say DataManager.
class DataManager: NSObject {
// A Singleton instance
static let shared = DataManager()
// your array
var dataArray = [String]()
}
now access this array from anywhere in the app.
Print(DataManager.shared.dataArray)
Do I have to pass the data forward from VC A to VC B?
You don't have to, but you should.
The suggested singleton pattern is considered an anti-pattern nowadays, you can read more about why here: What is so bad about singletons?.
It's not just a matter of taste, your code will be more maintainable if you avoid singletons.
Even if you use CoreData instead of an array in ViewController A, you will/should pass references from A to B, be it the Managed Object Context or an array of objects that you retrieved from CoreData in ViewController A. You can see this happening in the default template provided by XCode/Apple for a MasterDetail app with CoreData (just create a new project for that).

iOS object or delegate between two controllers?

Evening, my question is full about theory.
I understood reading from Apple developer documentation that is better to use the Delegates Pattern to keep track of some object attributes. In this way we can access the delegate without access to the object. (I really didn't get the reason of this choice)
I also understood that is better to define: protocolDelegate: class
and when we are declaring the delegate inside the class it's better to use the weak word to prevent some "kind of problem cycle". (??)
So, while I was playing a bit with code, I've discovered that you can't pass a weak delegate between two view controllers, because of course, when you change the controller, the weak delegate is going to be deleted because is a weak thing (or at least this is what I understood).
So, I have to choose between 2 options:
make the delegate "strong" deleting the weak key.
or pass the object in the segue and keep the delegate as weak.
I have a lot of confusion, can you clear my mind? :D
The cycle you're referring to is called a retain cycle.
Let's use a concrete example to clear this up: say you've got a UIViewController which has a UITableView. The view controller has a strong reference to the table view. The view controller now wants to act as the delegate to the table view.
Now, if the table view would have a strong reference to its delegate, we would have the following situation: the view controller has a strong reference to the table view, and the table view in turn would have a strong reference back to the view controller. Thus neither can ever get deallocated.
To break this cycle, references to delegates are usually weak. This allows the retain count of the view controller to drop to 0 eventually, which can in turn release the table view.
Your classes that want to use delegates should also follow this pattern and use weak references to their delegates. You should thus pass the required references via your segue.
I will concentrate on the first part of your question, since the previous answers have covered the rest pretty well.
Consider the following situation: you have a class that handles some kind of network connection - it sends a request to a server and gets a response. Outside of this class there is a viewController that has a button that triggers the request and a view which presents the response to the user.
Basically, the network handling class should be able to get some message from the viewController (button pressed) on one hand and pass the viewController the response on the other. So there should be bidirectional communication between the two classes. While the passing of the buttonPressed message to the network handling class is pretty obvious, the reverse part (passing the response) is a bit more tricky, because the network handling class should not be aware of who created it and who calls it (good OO practices and memory leaks prevention).
That's the point where the delegate pattern comes in. It allows an object to pass data to whoever is interested in it without knowing anything about the recipient. The class that passes the response only knows some 'delegate' and not another class. In addition you can take out the network handling class as is and put it in another project. Because it isn't supposed to know any other class from its original project, only some 'delegate', it can be put into another project without any modifications.
I hope it can help you to get the reason of the choice.
I think pass the object with segue, Segues are a very important part of using Storyboards in Xcode. We can go over the different types of seguesanother time, but this one shows you how to use the “Show” segue, as well as how to pass data between the two view controllers to customize the second one with whatever custom data is required.
You can easily use segues example; Under below you can send currentstring to destinationViewController inside sentstring , also ShowSegue is your segue identifier
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "ShowSegue" {
if let destinationVC = segue.destinationViewController as? OtherViewController {
destinationVC.sentstring = currentstring
}
}
}
Navigation between viewcontrollers maintain stack of viewcontrollers.
For example aVC is firstviewcontroller then top of stack will be aVC,
now when you push or show another viewcontroller say bVC then now top of statck is bVC. So stack looks like,
aVC -> bVC(top)
now you push another cVC then,
aVC -> bVC -> cVC(top).
So top of stack is always visible to user.
at current situation, aVC and bVC and cVC are not deallocate. they are in memory. But if you pop or dismiss cVC, then it will deallocate from memory and now your top of stack looka like,
aVC -> bVC(top).
So viewcontrollers live in stack till they are not popped or removed. So, they are strog reference by default.
Segue is nothing but you can say that they are graphical representation of push or pop operation.
another thing is that delegate should be weak that because it can create retain cycle if they are strong.
you can called delegate as representative in general sense.
Now, if you are using segue, send your object in prepareForsegue and it will manage everything else.

iOS UICollectionView navigation

I'm trying to figure out how to navigate around my app. But i'm a little lost.
I have a UIViewController that loads some data, then displays the data in a CollectionView. Then I have another UIViewController for the detailed view. I then trigger a segue to go to it, I pass the data etc.
self.performSegueWithIdentifier("detailViewSeque", sender: nil)
But the part i'm lost on is getting back to my main view, if I just trigger another segue then it loads all the data / view again. The data has already been loaded once, I really don't want to keep loading it.
I feel like I'm doing things wrong, that theres some super obvious way to handle this scenario.
Could someone point me in the right direction?
This is good situation to use an unwind segue (for more information: What are Unwind segues for and how do you use them?). Here's how to setup one up:
Firstly, create an #IBAction in the view controller you want to segue to, that takes a UIStoryboardSegue as its only argument. For example:
#IBAction func unwindToHere(segue: UIStoryboardSegue) {
// If you need you have access to the previous view controller
// through the segue object.
}
Secondly, you need to create the unwind segue in IB. To do this ctrl-drag from the view controller you want to segue from, to Exit and select the unwindToHere method:
Thirdly, you need to give your segue and identifier. To do this select your segue (see below - your segue will not be visible like normal segues); then use the Attribute Editor to give your segue an identifier.
Now you can use your segue. On the view controller you want to segue from, call:
self.performSegueWithIdentifier("YourID", sender: self)
To rephrase your needs "I have data that I need to keep around somewhere that isn't associated with a view controller".
You have a few options here. Your goal is basically to store it somewhere that isn't going to go out of memory.
The AppDelegate gets used for this purpose a lot but Singleton variable works as well.
I would personally create a singleton, say CatPictureRetriever with
private let _CatPictureRetriever SharedInstance = CatPictureRetriever()
class CatPictureRetriever {
static let sharedInstance = CatPictureRetriever()
var catPictures : NSArray?;
func gimmeCatPictures -> NSArray? {
return catPictures
}
}
Now you can get your pictures though your CatPictureRetriever anywhere
var pictures = CatPictureRetriever.sharedInstance.gimmeCatPictures()

Is prepareForSegue right way of passing value between view controllers

I'm trying to learn Swift and I'm trying to develop the famous note application.
There is an array bound to a tableview and another view for adding notes.
At second view textfieldshouldreturn event triggers a segue and goes back to tableview.
I wanted to learn if this is the right way. Because by doing this way I'm manipulating a variable in another view controller. I'm not a MVC master but I felt like it is 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 ?? "");
}
}
}
Thank you.
Your question is not really about prepareForSegue but the relationship between view controllers. The reason that your design "feels wrong" is that it is. The problem is that your note writing view controller knows too much about the view controller that is using it because it is directly manipulating a variable from the calling view controller. In order to directly manipulate the variable, it must know the class of the caller.
Why is this a problem? It makes your note writing view controller less reusable. If you write the note writing view controller correctly, then you could reuse it in other apps. To make it reusable, you need to decouple the note writing view controller from the caller - it must not know who exactly is calling it.
So the question becomes, how do I pass data back to the caller if I don't know who called me? The answer is delegation.
Delegation works like this:
You create a protocol which describes a method or methods that the implementor of that protocol will implement. In your case, you could use a protocol like NoteWriterDelegate that implements the method takeNote(note: String).
protocol NoteWriterDelegate {
func takeNote(note: String)
}
Define this in the file along with your note writing view controller.
Your note writer will have an optional pointer to the delegate:
weak var delegate: NoteWriterDelegate?
You need to declare your first view controller as a NoteWriterDelegate:
class ViewController: UITableViewController, NoteWriterDelegate
And then 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 writing view controller, you pass yourself as the delegate:
destinationViewController.delegate = self
In the note writing view controller, when you have a note to pass back to the caller, you call takeNote on the delegate:
delegate?.takeNote(self.ourTextField?.text ?? "")
By doing it this way, your note writer only knows that it is talking to a NoteWriterDelegate. If you want to reuse this in the future, you just drop your note writer class into another project, implement the delegate, and it works without you having to touch the code in the note writer class.
I would recommend passing data via prepareForSegue in most cases. It's pretty simple to set up and easy to understand.
However, I would recommend never updating UI elements (labels, text fields, etc.) on the destination view directly. In my opinion, this is bad coupling that creates a lot of problems.
Instead, create a property or properties on the destination view controller that the caller can set in prepareForSegue to pass data to it. These should be special purpose properties used exclusively for passing data. The destination view controller is then in charge of using the data in these properties to update its UI or internal state.
Delegation is a valid approach, but I find it to be overkill for most situations. It requires more setup and is more abstract. This abstraction isn't needed in a lot of view controller relationships. If you discover you need to reuse a view controller, you can always refactor to use delegation later.
I do not believe that the prepareSegue is the ideal way for passing data between view controllers...at least not directly.
I share your concerns about using prepareForSegue to pass values between view controllers. The source view controller shouldn’t know anything about the destination view controller (and the other way around, for that matter). Ideally view controllers should be separate islands with no visibility into one another.
To address the coupling that storyboards seem to encourage, I’ve often used some form of the mediator pattern to pass data between view controllers. Here is a pretty good blog post on how to implement a version of this pattern around storyboards: http://coding.tabasoft.it/ios/mediator-pattern-in-swift/ . As always, this pattern may not be the best fit for all situations, but I feel it has been a good solution in a lot of my past projects.
Basically, how the mediator pattern would work within the storyboard paradigm is that in each view controller’s prepareForSegue method, the the segue object is passed to the mediator object. The view controller doesn’t care what’s inside or where the navigation is going next; it just knows it’s about to not be visible. The mediator, which has just been passed the segue object (containing the source and destination view controllers), is then responsible for passing data between the source and destination view controllers.
Using this pattern, each view controller is blissfully unaware of the existence of the other. The mediator class, on the other hand, must know about the relationships between the view controllers (and the view controllers' interfaces) in the navigation path. Obviously if the navigation changes, or the view controllers themselves change, the mediator class will need to adjust. Each view controller, however, need not have any dependence on each other, and therefore need not be updated to to accommodate changes in the navigation path or changes to the other view controllers along that navigation path.
It is not 'the' right way, but it is a right way. Especially in storyboard applications.
Here is an alternative way of passing value and calling the view.
var myNewVC = NewViewController()
myNewVC.data = self
navigationController?.presentViewController(myNewVC, animated: true, completion: nil)

Resources