I have three Xibs (A, B, C). From A I am going to B. So If I have to pass data to A and get back to A, I have written a delegate and I am dismissing the Controller. This is fine.
But now my requirements are that I have to go from A to B and from B to C. Now from C I have to pass data to A and come back to A.
How to do this?
Note: I am not using StoryBoard or Navigation Controller. And Controller A is not root View Controller
Add a notification observer with a name in controller A, Then from where you want to send data (in your case B, C), From there post a notification with the name you are observing in controller A.
You can use NSNotificationCenter for passing data from viewcontroller to another view controllers.
You can try NSNotificationCenter as shown below,
Example:
In your ViewControllerA.m
-(void)viewDidLoad
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(dataReceived:) name:#"passData" object:nil];
}
-(void)dataReceived:(NSNotification *)noti
{
NSLog(#"dataReceived :%#", noti.object);
}
In your ViewControllerC.m
-(void)gotoHome
{
[[NSNotificationCenter defaultCenter] postNotificationName:#"passData" object:[NSDictionary dictionaryWithObject:#"Sample Data" forKey:#"dataDic"]];
[self.navigationController popToRootViewControllerAnimated:YES];
}
Check this example in below link:
https://stackoverflow.com/a/22501709/5349267
I extend the answer of Sommm as it is my wish to underline how important
MVC, the model-view-controller pattern,
as a design pattern in iOS as well as macOS applications really is and how useful it is if one obeys to it.
Good solution: Use NSNotificationCenter in order to let parts of your application communicate that don't know about each other usually. You register controllers that are waiting for information as observers for a specific message type and then post from other parts of your code what should be sent to the observers (using the specific message type the observers are waiting for).
Much better solution: Don't use NSNotificationCenter and instead design your application better and obey to the MVC design pattern.
The OP's initial question somehow shows (at least to my mind) a common misunderstanding: Don't try to create a specific UI flow and write your code accordingly but think of your application in a more general way first. As you don't give enough information on the question what you really want to do, I will assume you are generating an instance of a class User in your three XIBs called A, B and C.
Model: Write a class User that has the needed properties like firstname, surname and email
Controller: Write a controller UserController that holds an array of every instance of the class User that exists in your application.
View: Now (and only now!) think about your View. First question: Do you really need three XIBs or could you use one with three UIViews? The views could be IBOutlets of a class say UserViewController. Make this class the delegate of your single XIB file and connect the IBOutlets. Your UserViewController could then have a property user of the instance of User it represents and set the properties of user according to what has been inserted in the UITextFields for example.
If this is too complicated to you, you can use the NSNotificationCenter in order to send messages (with content if needed) between different parts of your application. If you find yourself using it very often: increased usage of this technique usually is a sign of a badly constructed application that needs some refactoring.
Related
I was using this code to pass data between controllers.
InterfaceController.reloadRootControllersWithNames(["1","2","3"], contexts: ["adf","asd","asd"])
I called this code in willActivate() method, that you can see here.
override func willActivate() {
// This method is called when watch view controller is about to be visible to user
super.willActivate()
InterfaceController.reloadRootControllersWithNames(["1","2","3"], contexts: ["adf","asd","asd"])
}
I have no idea about whats wrong with this code, in apple watch, the app is just refreshing again and again. Is there any other method to pass data between interface controllers while we use page based navigation.??
Please Find the below way of passing data in paged-based navigation in watch application (Watch os 2 and later).
Note :
An array of strings, each of which contains the name of an interface controller you want to display in the page-based interface. In your storyboard, the name of an interface controller is stored in the object’s Identifier property, which is located in the attributes inspector. The order of the strings in the array is used to set the order of the corresponding interface controllers. This parameter must not be nil or an empty array.
Objectiv C
[self presentControllerWithNames:#[#"FirstViewController",#"SecondViewController",#"ThirdViewController"] contexts:#[#"adf",#"asd",#"asd"]];
Swift
let controller = ["FirstViewController","SecondViewController","ThirdViewController"]
let pageContexts:[AnyObject]? = ["adf","asd","asd"]
presentControllerWithNames(controller, contexts: pageContexts)
If you call reloadRootControllersWithNames(NSArray, contexts: NSArray) onto the view controller, it will do as the function is titled: reload. Therefore, you have a view controller that refreshes all 3 of the items in the first Array you give, and then, since your current view controller calls willActivate() upon loading, it infinitely refreshes.
I don't have enough reputation to make this a comment, but my suggestion here is to find a method that you can call on an instance of the watch interface you want to in order to pass data.
What I mean is, if you have each interface as its own class, then you can make a method in the interface you want to get to that sets a property to whatever data you want to transfer before you present the interface.
I also use Objective-C more than Swift, but here's what my guess is as to what the Obj-C code would come to:
SecondWatchInterface *secondWatchInterface = [SecondWatchInterface new];
[SecondWatchInterface setSomeDataWithAStringValue:#"Foo"];
[self presentWatchInterface:SecondWatchInterface]
In Swift, I'm thinking this would equate to:
SecondWatchInterface *secondWatchInterface = SecondWatchInterface.new()
secondWatchInterface.setSomeDataWithAStringFile:"Foo"
presentWatchInterface(secondWatchInterface)
I'm unfamiliar with WatchKit myself; I typically use SpriteKit. I hope I helped identify what the problem was, at least!
To pass data between interface controllers you need to go for this method:
presentControllerWithName(“Second Watch Controller”, context: [“segue”:“pagebased”,“data”: "Data Passed Successfully”]);
Also, you can refer to this example: http://www.informit.com/articles/article.aspx?p=2272657&seqNum=3
I have two or even more view controllers (A and B) which uses the same calculation method. I would guess the best way is to put the calculation method in its own class (lets call it C), define a protocol and thats it. If this is right, how do I know how to address the delegate?
If I alloc/init an object of the class C (the one with the calculatormethod) e.g. in class B I have the object pointer in class B - thats ok. But how do I get the object pointer known in class A or even other classes (i.e. how do I tell those controllers which want to use the delegate (i.e the same calculation method), how to address the delegate once it is alloc/init by class B?
Any help is very much appreciated!
I have two or even more view controllers (A and B) which uses the same calculation method.
Unless this is for calculating view layouts, it probably indicates you've have an MVC violation. View Controllers typically should not calculate anything. Their job is to manage user interaction. Data and calculations belong in the model.
If it is a proper view controller calculation (like managing layout), then you're correct that you want a delegate. "Delegation" is what Cocoa tends to call the Strategy pattern. You move your algorithm into another object and that lets you vary the algorithm without varying the rest of the code.
So in one case you need access to some model object, and in the other you need access to some delegate. In either case, the solutions can be similar. I'll call either situation "C" as you have.
One solution, particularly is you're using a storyboard, is to create "C" in the storyboard and wire it with an IBOutlet. You can create any object you like in a storyboard. Just drag out an "Object" and set its class to the appropriate class. Wire it up just like anything else. (This is a technique that is commonly used for multi-view nib files on OS X, and I had remembered translating over to Storyboards, but it doesn't work for sharing objects across scenes, only within scenes; so it's not as useful on iOS.)
Another solution, particularly for the model, is to implement it as a singleton, or to have a separate singleton (a "model controller") that returns it. You should not use the app delegate for this; use a singleton made expressly for this purpose.
You can create "C" in the application delegate and pass it into the root view controller (this is a proper use of the app delegate, because it's part of global program initialization). The view controllers can pass the object as part of their segues. This is my preferred solutions for passing model objects around.
If it really is a layout calculation delegate, this is probably part of some kind of configuration system (assuming it can change). The current configuration can be treated as a piece of the model, and all the above techniques still work.
If it really is just shared algorithms and doesn't vary, don't forget C functions. There is no rule that you must put all code into methods. Functions are ideal for stateless calculation. Categories can be used this way to, but simple functions have fewer complexities.
What you are saying is that both classes A and B have a common dependency (could be class C or simply a protocol C).
A and B don't need to know anything about how they are instantiated, they just need to know that they will be eventually provided with an instance implementing (protocol) C.
Another important thing is that you probably don't want C to be hold with a strong reference by either A or B.
I would look at which class F could have the responsibility to instantiate A and B.
The responsibility of this class (which could be described as a Factory) could also be to provide instances of A and B with a C instance.
So what I would do: Define a "factory" class that has methods to build instances of A and B.
Each of these methods would also provide with a C instance. This C instance could be a property of the factory class if you want it to be shared (or this factory class could also pick the C instances from a pool of available C instances).
UPDATE: not practical if you are using storyboards to instantiate your controllers. In this case you probably want to go with other given answer or implement your shared computational functions as methods of a singleton class C (see How to pass object between several views in storyboard, iOS Dev for example)
Use a superclass for A and B (and any number of additional controllers) that contains the calculation method. By using a superclass, you don't have to alloc init another class or use delegates, all the subclasses will have access to the method.
Another approach that would be more general would be to implement a category on UIViewController to add the calculation method. This way, any controller that descends from UIViewController (UITableViewController, UICollectionViewController, etc.) would have access to that method.
After Edit:
I see in your comments that your calculations have nothing to do with the controllers, just some sort of algorithm, so a category or subclass of UIViewController is probably not the best way to go. If you want to do it in another class, any controller that needs to use it, can instantiate an instance of that class, set itself as delegate, and get the result back through the delegate method (that is, if you even need a delegate -- if the calculation is fast, then you can just return a result from the method rather than using a delegate). After your controller gets the result back, the instance should be deallocated. You don't have to worry about which controller set the delegate, since each controller creates its own instance of the calculation class, and sets itself as delegate. I use this kind of structure for apps that need to do downloads from a server from multiple controllers. The download class is instantiated, does its work, sends back the result in a delegate method, and then gets deallocated. It only sticks around for as long as it needs to to do its work.
So in my app I have the following situation:
BackendCommunicatingClass -> (owned by) -> ModelClass -> (owned by) -> HomescreenViewController
HomescreenViewController is a delegate for ModelClass.
ModelClass is a delegate for BackendCommunicatingClass.
Also on when the app launches for the first time, I have this:
WelcomeViewController -> (owned by) -> HomescreenViewController
HomescreenViewController is delegate for WelcomeViewController.
When the user types a username and password in the WelcomeViewController, this information needs to get all the way to BackendCommunicatingClass, and then all the way back to WelcomeViewController to display error if needed. Right now I have implemented this by passing information to each class in the communication chain, until it gets to BackendCommunicatingClass. The result is a lot of duplication of protocol methods and I feel like I'm doing it wrong.
What do you think?
Well I understand it is not the clearest solution, but without seing the code, and the purpose of your program, this what I suggest.
Implement Key Value Observing (KVO) in Back End view controller, observing change in the Home View Controller. As soon as Back end controller detect change in the text field, trough a dedicated variable in Home View controller, it fires all the operation it has to do.
When back end finish, it sends a NSNotification with the result of the operation. Home view controller which you have made listening to such custom notification, react to that and display error message or other staff.
It may sounds complicated, but KVO and notification are easy to implement, and there are plenty of docs and tutorial on the net.
If there is a clear 1:1 mapping of what those delegate protocols provide AND the delegate does not deal in UI stuff that nothing except the directly owning view controller should be concerned with, you could pass the delegate along to the end of the chain and set it directly as a delegate. This is what delegates are for - being able to allow an otherwise unconcerned object to communicate with another object.
That said, depending on how strict your layering policy is, you may prefer to encapsulate the information at every step by having different delegates.
I have a few questions about code design. This might be long. I'll shorten it whereever possible.
Q1) Dependant or Independant?
Creating a class and adding required functionality so as to allow the object to change its own state vs state being changed by a controller (aka viewcontroller)
I find code examples are best when trying to communicate:
Note: I reduced some lines of code. Original code from http://www.techotopia.com/index.php/An_Overview_of_Objective-C_Object_Oriented_Programming.
Anyways if I had to set the Account balance for an account it is suggesting I do this:
#interface BankAccount: NSObject
{
double accountBalance;
long accountNumber;
}
-(double) getAccountBalance;
-(void) setAccountBalance: (double) x;
#end
// Implementation Section Starts Here
#implementation BankAccount
-(void) setAccountBalance: (double) x
{
accountBalance = x;
}
-(double) getAccountBalance
{
return accountBalance;
}
#end
//usage
BankAccount *account1 = [BankAccount alloc] init];
[account1 setAccountBalance: 1500.53];
----HOWEVER I believe I would have wrote it in a controller like this:------
#interface BankAccount: NSObject
{
double accountBalance;
long accountNumber;
}
import "BankAccount.h"
#interface MeViewController : UIViewController
-(void)setAccountBalance:(double)x toAccount:(BankAccount *)tempBankAcc;
#end
#implementation myViewController
-(void) setAccountBalance:(double)x toAccount:(BankAccount *)tempBankAcc
{
tempBankAcc.accountBalance = x;
}
//USAGE
BankAccount *account1 = [BankAccount alloc] init];
[self setAccountBalance(Account1,1500.53)]
#end
because I believe it is Me (meViewController) setting the AccountBalance, not the account itself as an account is just an account.
Is this a very bad idea? I can see with their example that the object can look after itself (independent) meaning changing its own state whereas my approach says that the BankAccount can only be modified through/with the controller (dependant)
???
Q2) What should/should not be in a controller?
I have also read somewhere that code written inside the controller should be only for:
Responding to user interaction & Updating the views
so does this means I should never do the following in the controllers:
READ or WRITE to and from NSUserDefaults. Since its a singleton, I thought it would be easier to write to it regardless of which controller is currently active? bad idea?
I understand that I should save data (includes NSUserDefaults) in applicationDidEnterBackground and in applicationWillTerminate so does that its a bad idea to save elsewhere (eg. in another controller).
Q3) Which design patterns do you most commonly use and which ones do you abuse wrongly?
Singletons:
My understanding is that [UIApplication sharedApplication],[NSNotification defaultCenter] and other singletons are accessible via all controllers, how about NSObject subclasses? or UIView subclasses?
And apparently you can use NSNotification to notify other controllers when the model has been updated.
Can someone tell me an example of when to use and how to use that?
Maybe when you import some data, that has different attributes, than was originally intended, then the observers could be notified of an upgraded data model, is that an example of when you would within your data model notify a controller?
Thats enough questions for now.
Sorry but I had to get them all out of my head:-)
Ben
First things first: don't use this
#interface BankAccount: NSObject
{
double accountBalance;
long accountNumber;
}
-(double) getAccountBalance;
-(void) setAccountBalance: (double) x;
#end
Here, the accountBalance ivar is public, while it should be private.
Instead, use a #property:
#interface BankAccount: NSObject
#property (assign, nonatomic) double accountBalance;
#end
That'll define a setter:
- (void)setAccountBalance:(double)accountBalance { ... }
and a getter:
- (double)accountBalance { ... }
and a private instance variable (ivar) named _accountBalance.
You can then, in your implementation, use:
#synthesize accountBalance;
which will automatically create the setter/getter like this:
- (void)setAccountBalance:(double)accountBalance {
_accountBalance = accountBalance;
}
- (double)accountBalance {
return _accountBalance;
}
Q1
If you take away all the business logic from the object itself, it's nothing more than a shallow container object. Further more, you need to replicate that logic in every controller you use. Business logic goes into business objects.
To your concrete example, the method of the controller does nothing but delegates the call down to the very object itself (infact, calling the same method if you use properties here). So you gained nothing at all. It may be fine to have that method in the controller, if the controller does more than just calling down on the object, maybe update some more data. One of the good hints here is that the controller does not use any of its own instance variables. It could easily be a class method. It's a code smell.
Q2
Basically, you have three choices for accessing NSUserDefaults: application level, view controller level, or view level.
Worst case is view level - a view should work in any context and should be configurable any way you like, and it certainly should not depend on some magic outside things. This would make reusability (not only for other apps, but also within the same app) a pain.
Accessing the defaults on the view controller level is often ok, especially if they take some singleton role. I.e. if you only ever have one playing screen and one setting screen, each of them might easily talk to the defaults. If there's the chance that you may have several instances of the same class differently configured, this gets a pain and bad smelling work arounds to "individualize" those.
This is where you want to place the access of the defaults outside of the view controllers, which is the most elegant and flexible approcach, but sometimes just not necessary.
Reusability may come earlier than expected, i.e. opening up a second text editor window (which might or might not show a different font), a second settings screen (i.e. a popover), a second playing screen for board games, etc.
Q3
I don't think Singletons are bad per se. They do come with a hefty price tag, though. They come convenient at early stages, just like global variables do.
They get very expensive, soon. There's no ownership, everything is shared, no chance to change a single bit without effecting the other users of that object. Reusability for objects depending on singletons is poor, as you always need those singletons as well. I tend to avoid them most of the time. And whenever I had one of them in my code, getting rid of them was a good decision.
As for the notifications: they are really designed for communicating on a system level. They shouldn't be abused for normal application logic and communication. It's a broadcast for system level events. If you use them as a substitute for object communication, this is a sure way to hell. This takes every logic out of the code, and there's no way build up responsible code.
Q1) Model-layer objects, especially ones that will be interacted with and mutated, should be responsible for their own internal representation as well as representing the knowledge of the rules of the interactions. For a bank account, I think both the approaches you present are wrong. The second in particular because it is actually taking even the responsibility of updating the state away from the account. But the first also because it only provides a setBalance method, which doesn't make sense in the real world. Whoever uses that account has to now handle all the logic of transfers, withdrawals, deposits, interest, and so on. Really, a bank account should have the methods deposit withdraw balance and so on, that way it can represent the rules and logic by which these interactions happen. In this case drawing from how this would work in real life can be helpful.
Q2) A lot of this depends on how your app is set up. A simple app's model layer may be just a plist or NSUserDefaults and some NSDictionary instances. In that case you might not want to have a view controller interact with it, but maybe a data controller object. But you can probably get away with having view controllers talk to that kind of model. When things get more complex though, you definitely need to separate concerns, and have controller objects closer to the data model or have more sophisticated models providing the rules of their interactions. Personally I'd avoid having all the view controllers loading NSUserDefaults, you could just make a single data controller class that could handle that and instantiate it as needed.
Q3) I see two questions here. Not sure what you mean by "accessible to all controllers". But NSNotification is incredibly useful when used right. Say you have 3 view controllers, all coordinating a different view of the bank account - one is a ledger, one is a pie chart, one is a budget calculator. When the app makes a request to refresh the bank account's information from the internet, when that request comes back it can notify all three of these view controllers at once and they can refresh their views accordingly. It's really used any time you have more than one object interested in knowing that something happened.
Hope this helps and let me know if you have any questions.
Read Stanford's lecture about MVC. I think you will find many answers there http://www.stanford.edu/class/cs193p/cgi-bin/drupal/node/205
I am making an object that goes to download stuff for all of my view controllers. The object is singleton instance and has a callback method with received data once the download is completed. It also has a delegate property so that it knows which object to call back to after the download is done.
There are multiple controllers that use this shared instance, and my question is how to call back to the correct view controller that requested the download.
My approach is to use delegation, but the problem is that since other view controllers are also its delegate, the download object could call back to every object and this will be hard to track.
I've worked on projects where people have attempted to use multiple delegates and it's basically a bad idea. The delegate pattern is about a 1 to 1 relationship between a class and it's delegate. Whilst it is possible to achieve some level of multiple delegation through switching the delegates in and out, it's more likely to lead to unpredictable behaviour and bugs.
My recommendation would be to change how you are thinking about this. You have two options as I see it:
Switch to an Observer pattern where you can register multiple observers which your main class can interact with. This is useful where your observers all implement the same protocol and where your main class wants to be aware of the observers and interaction with them.
Broadcast NSNotifications to indicate state changes and events. Here is a more decoupled approach because the main class does not need to know who is listening and does not directly interact with them. Other can start and stop being notified at their leisure. It also has the advantage that you do not need to create or implement a separate protocol. Instead you register the classes that need to know about changes with the NSNotificationCenter which in turns handles all the routing of notifications for you.
It actually sounds like the delegate pattern might not be the best approach here.
I would look into NSNotificationCenter instead.
The basic idea is that your singleton doing the net connection posts a notification (with something like postNotificationName:object:userInfo:) , saying that new data is available. Within this notification, you can pass a dictionary object (userInfo) that holds the data you've fetched, or info on what parts of your Model contain updated data.
Then, your other view controllers can register themselves to 'observe' these notifications by calling addObserver:selector:name:object:. Generally speaking, when a vc becomes visible I call addObserver, and removeObserver when it's being hidden or transitioned out.
Good luck!
Delegation doesn't seem like the right solution to this problem. How about requiring the requesting view controller to provide an object (its self) and a selector for you to call as a completion notification? Of course, you'll need a place to store that object and selector until the download completes. Hopefully you have (or could create) an object for this.
i recommend to use one of these ways
observer:
when use data that you want to inform other object are near to primitive ones.for example when you are using 'NSMutableArray' you can not inform the change in one of object by the standard implemented pattern at least you need to implement one for your self that is not reusable that much
Notification
when your interaction with destination object (those need to be inform) is in one-way.it means you don't need any acknowledge or other data back from them.
delegate
when there is one object to inform at each time step.
note:block use for success and fail is not a pattern to broadcast data its about to queue task when you don't know when they are finishing or failing like network operations
EDIT:
how to create notification | multi delegate issues and implementation
While I agree with most of the answers here, if you did actually want to achieve multiple delegates you could potentially declare an array of delegates and send messages to all delegates within that array. If your protocol has optional delegate methods you safely check using responds(to aSelector: Selector!) -> Bool before invoking (being mindful of memory management, as those delegates would be strongly referenced in the array). Again I do agree that multiple delegates is likely a bad architectural idea and using blocks or notification center would suit your needs better.
One approach, which works for me if you only have one other object to forward messages to is to create a forwardingDelegate This does not end up with issues of hard to debug ordering of delegates and it does not unnecessarily create a dependency on the other object. Keep in mind, if you have many objects then this might not be the best approach, it is mainly for one additional object but this could be extended to support an array of objects so long as there is one that receives the SDK and forwards it to the other objects [1]. Note that every method that is needed for the forwarded object needs to pass it along, even if it is not used by the forwarding object.
For example, if I need to forward the messages coming from the mapView delegate:
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
{
// handle this object here.
if ([self.forwardingDelegate respondsToSelector:#selector(mapView:regionDidChangeAnimated:)])
{
[self.forwardingDelegate mapView:mapView regionDidChangeAnimated:animated];
}
// or handle this object here.
}
[self.forwardingDelegate mapView:mapView regionDidChangeAnimated:animated];
The forwarding property would be declared like this.
#property (nonatomic) id<MKMapViewDelegate> forwardingDelegate;
And the other object would adopt the protocol as if it were receiving the original message.
[1] The array approach for multiple delegates may get tricky because then you don't have as much control over what order the delegates get called, as was mentioned in other posts.