1

Recently when developing an iOS app, I encountered a problem that i need to have two view controllers to share some data, or even retrieve UI status from another view controller.

To honor this feature, i defined and implemented delegate for view controllers.

@protocol ViewControllerADelegate <NSObject>

-(id)dataForSomePurposeForViewController:(ViewController *)viewController

@end

and then when pushing a new viewController(in ViewControllerA) class, I will set the currentViewController as delegate of the new controller if it is needed.

viewControllerA.delegate = currentViewController
[currentViewController.navigationController pushViewController:viewControllerA animated:YES]

After that I can call the delegate selector and get data from last view controller like:

//in viewControllerA
self.someData = [self.delegate dataForSomePurposeForViewController:self]

The code above is just an example showing what i have tried, there may be some typo in the code and do not pay too much attention to it.

In fact the above way works fine and never causes any problem up to now. But when i talked about this with one of my colleague, who is an Android developer, I was told that what i have done in iOS will not work in Android. In the task stack model in Android, the view controller (called Activity in Android) will not execute any code or respond to any function call when it is not at the top of the navigation stack.

This somehow makes me worry about my usage of delegate in this way. I googled a lot and now I know that my implementation is little bit against the MVC design pattern and the best way for communication between view controllers may be setting a shared model.

However changing the current code is a time consuming task. So My question is:

  1. What is the risk of using delegate for view controller communication?
  2. Is that as I understood that having a shared model to is the best practice? If yes, who should i assign this model to? by using singleton?

Any help or thinking is appreciated and thanks in Advance.

MMhunter
  • 1,431
  • 1
  • 11
  • 18
  • it sounds like the android version should be pushing and popping fragments instead of different activities (then they can use this delegate pattern without a problem) but if they have already built the app in such a way, then it will be a lot of work for either of you to change at this point. maybe just have different implementations that work for each platform, not the end of the world. although could have implications if designing new features on top of this... hard to tell without knowing exactly what you are doing – Fonix Jul 14 '16 at 02:51
  • 1
    1)The risk is that top controller on stack can be not deallocated if u hold strong reference on it. Define delegate property as weak. Delegate pattern its not only one way to communicate between controllers. Also you can use blocks or notification center. Its matter of style.2)It depends on application architecture, sometimes its better to have shared model but in most cases delegates are good for communication. – Bohdan Savych Jul 14 '16 at 03:08
  • @BohdanSavych thank u for your reply. In fact i am using weak reference then maybe delegate is not a bad idea, at least for iOS. If u could post your comment as an answer then maybe i could mark it as accepted. – MMhunter Jul 14 '16 at 03:31
  • @Fonix thx and in fact i know little about Android but it is not my responsibility to implement the communication in Android, but just in case xD. – MMhunter Jul 14 '16 at 03:34
  • @MMhunter if you want i can add example with blocks in answer. – Bohdan Savych Jul 14 '16 at 03:43
  • @BohdanSavych sure that will be very helpful for me thx. – MMhunter Jul 14 '16 at 03:44

2 Answers2

1

Without knowing what type of data dataForSomePurposeForViewController returns and how it generates it, as a general sort of design choice for the controller relationship you described, I'd go for a type of Dependency Injection implementation where you pass the data required by the new controller. Use delegation for control, such as for popping the new controller off the stack and saving changes made by the new controller on the model that was passed to it.

Some Apple examples of Core Data apps involve the creation of a scratch managed object context (MOC) passed into an 'add new record' controller containing data to be manipulated by the new controller. This MOC is created and owned by the controller that launches the 'add' controller, and also is responsible for tearing it down and saving changes after the 'add' controller is popped off. The add controller's delegate is the controller that launched it.

jp2g
  • 682
  • 4
  • 8
  • Thanks for your reply. I will learn about the MOC. But i am still wondering if there is any risk if i use the delegate for communication. `dataForSomePurposeForViewController` is just an example, in my real implementation it could be function that takes parameters and calculates result together with some properties assigned to the old controller. It basically assumes that the old controller in stack will complete any thing just like when it is at the top of stack. – MMhunter Jul 14 '16 at 02:49
  • Any controller in the navigation stack is "alive" and accessible like any object. Because the UINavigationController that the view controllers are contained in owns those controllers, they're guaranteed to be in memory until they're popped off the navigation stack. – jp2g Jul 14 '16 at 03:50
1

1)The risk is that top controller on stack can be not deallocated if u hold strong reference on it. Define delegate property as weak. Delegate pattern its not only one way to communicate between controllers. Also you can use blocks or notification center. Its matter of style.2)It depends on application architecture, sometimes its better to have shared model but in most cases delegates are good for communication. Below block example. Create property with type of block with copy attribute (why copy)

typedef void(^ApproveAction)(NSString *name);//return type(^name)(arguments)

@interface SecondViewController : UIViewController

@property (nonatomic, copy) ApproveAction onApproveTap;

+ (NSString *)storyboardID;

@end

Then in firstViewController controller on some action we do next:

#pragma mark - Actions

- (IBAction)presentAction:(id)sender
{
    SecondViewController *secondVC = [self.storyboard instantiateViewControllerWithIdentifier:[SecondViewController storyboardID]];
    secondVC.onApproveTap = ^(NSString *name)
    {
        NSLog(@"%@",name);
    };

    [self presentViewController:secondVC animated:YES completion:nil];
}

To prevent retain cycle with block read this

Then again in SecondViewController call block when you should(for example on some button tap):

if (self.onApproveTap)//check if block not equal nil
    self.onApproveTap(@"Name");

!self.onApproveTap?:self.onApproveTap(@"name");//the same but with syntax sugar

Hope this helps.

Community
  • 1
  • 1
Bohdan Savych
  • 3,310
  • 4
  • 28
  • 47