[vc_row row_type=”row” use_row_as_full_screen_section=”no” type=”full_width” text_align=”left” box_shadow_on_row=”no”][vc_column][vc_column_text]

With the introduction of storyboards and segues, it became less necessary to create new view controllers in code, but every now and then, you still need code to create a new view controller. You can download the code for this article at https://github.com/Five-Pack-Creative/ArticleFactoryMethods

[/vc_column_text][/vc_column][/vc_row][vc_row row_type=”row” use_row_as_full_screen_section=”no” type=”full_width” text_align=”left” box_shadow_on_row=”no”][vc_column][vc_empty_space height=”20″ image_repeat=”no-repeat”][vc_column_text]

Attempt #1

[/vc_column_text][vc_empty_space height=”20″ image_repeat=”no-repeat”][/vc_column][/vc_row][vc_row row_type=”row” use_row_as_full_screen_section=”no” type=”full_width” text_align=”left” box_shadow_on_row=”no”][vc_column][vc_column_text]Our sample app for this article has two view controllers, HomeViewController wants to display DetailViewController. Easy peasy, just do something like this:

1

2

3

4

5

6

 

– (IBAction)showDetailTapped:(id)sender {

DetailViewController *detailController = [[UIStoryboard storyboardWithName:@”Main” bundle:nil] instantiateViewControllerWithIdentifier:@”DetailViewController”];

[self presentViewController:detailController animated:YES completion:nil];

}

 

Ok, that will work, but there’s a problem. Now HomeViewController has to know the name of the storyboard and the identifier used to create DetailViewController. With this simple example, it’s not much of a problem, but if multiple places in code are using DetailViewController, they all have to know this information and use the correct strings. Let’s make it better.[/vc_column_text][/vc_column][/vc_row][vc_row row_type=”row” use_row_as_full_screen_section=”no” type=”full_width” text_align=”left” box_shadow_on_row=”no”][vc_column][vc_empty_space height=”20″ image_repeat=”no-repeat”][vc_column_text]

Attempt #2

[/vc_column_text][vc_empty_space height=”20″ image_repeat=”no-repeat”][/vc_column][/vc_row][vc_row row_type=”row” use_row_as_full_screen_section=”no” type=”full_width” text_align=”left” box_shadow_on_row=”no”][vc_column][vc_column_text]We should put the call to instantiateViewControllerWithIdentifier inside DetailViewController and then expose that so callers can create a new instance easily. How about this, inside DetailViewController, add an init method:

1

2

3

4

5

6

7

 

– (instancetype)init {

// Spoiler alert: don’t do this!

self = [[UIStoryboard storyboardWithName:@”Main” bundle:nil] instantiateViewControllerWithIdentifier:@”DetailViewController”];

return self;

}

 

And then the HomeViewController code changes to this:

1

2

3

4

5

6

 

– (IBAction)showDetailTapped:(id)sender {

DetailViewController *detailController = [[DetailViewController alloc] init];

[self presentViewController:detailController animated:YES completion:nil];

}

 

Much better! Now any class that wants to instantiate a new DetailViewController doesn’t have to know anything about what storyboard, or even that there is a storyboard. You don’t have to remember the identifier or make sure you type it correctly. So, we’re done, right? No, there’s a problem lurking…[/vc_column_text][/vc_column][/vc_row][vc_row row_type=”row” use_row_as_full_screen_section=”no” type=”full_width” text_align=”left” box_shadow_on_row=”no”][vc_column][vc_empty_space height=”20″ image_repeat=”no-repeat”][vc_column_text]

The Problem

[/vc_column_text][vc_empty_space height=”20″ image_repeat=”no-repeat”][/vc_column][/vc_row][vc_row row_type=”row” use_row_as_full_screen_section=”no” type=”full_width” text_align=”left” box_shadow_on_row=”no”][vc_column][vc_column_text]

In order to see the problem, let’s put a breakpoint in DetailViewController at the top of our new init method and run.

[/vc_column_text][/vc_column][/vc_row][vc_row row_type=”row” use_row_as_full_screen_section=”no” type=”full_width” text_align=”left” box_shadow_on_row=”no”][vc_column][no_image_with_text alignment=”center” image=”13170″]
[/no_image_with_text][/vc_column][/vc_row][vc_row row_type=”row” use_row_as_full_screen_section=”no” type=”full_width” text_align=”left” box_shadow_on_row=”no”][vc_column][vc_column_text]

You can see, even before the first line of the init method has run, self has a value. Ok, that makes sense, the call to alloc reserved a memory location for the new variable, but now let’s step over to the next line and look at self again.

[/vc_column_text][/vc_column][/vc_row][vc_row row_type=”row” use_row_as_full_screen_section=”no” type=”full_width” text_align=”left” box_shadow_on_row=”no”][vc_column][no_image_with_text alignment=”center” image=”13171″]
[/no_image_with_text][/vc_column][/vc_row][vc_row row_type=”row” use_row_as_full_screen_section=”no” type=”full_width” text_align=”left” box_shadow_on_row=”no”][vc_column][vc_column_text]

Woah! The value is different. Well, that makes sense too because the call to storyboardWithName is going to create and return its own value. What this means is that an instance of DetailViewController will get created with alloc and then immediately thrown away (and dealloc’ed) in the first line of the init method.

[/vc_column_text][/vc_column][/vc_row][vc_row row_type=”row” use_row_as_full_screen_section=”no” type=”full_width” text_align=”left” box_shadow_on_row=”no”][vc_column][vc_empty_space height=”20″ image_repeat=”no-repeat”][vc_column_text]

Final Attempt

[/vc_column_text][vc_empty_space height=”20″ image_repeat=”no-repeat”][/vc_column][/vc_row][vc_row row_type=”row” use_row_as_full_screen_section=”no” type=”full_width” text_align=”left” box_shadow_on_row=”no”][vc_column][vc_column_text]So the principle of moving the call to instantiateViewControllerWithIdentifier inside DetailViewController was right, we just picked the wrong way to do it. Rather than overriding the init method, we want a factory method to create and return an instance.

1

2

3

4

5

 

+ (instancetype)controller {

return [[UIStoryboard storyboardWithName:@”Main” bundle:nil] instantiateViewControllerWithIdentifier:@”DetailViewController”];

}

 

We expose that method in the header and then use it in HomeViewController:

1

2

3

4

5

6

 

– (IBAction)showDetailTapped:(id)sender {

DetailViewController *detailController = [DetailViewController controller];

[self presentViewController:detailController animated:YES completion:nil];

}

 

Voila! The code that knows how to create a DetailViewController is now encapsulated inside that class, callers can easily create a new instance, and our problem of creating extraneous instances is solved. It’s a thing of beauty.[/vc_column_text][vc_empty_space height=”100″ image_repeat=”no-repeat”][/vc_column][/vc_row]

© Five Pack Creative 2008 - 2019

Let’s Talk

info@fivepackcreative.com

(972) 377-0023

Frisco, TX  |  Tulsa, OK  |  Wayne City, IL

© Five Pack Creative 2008 - 2019

Let’s Talk

info@fivepackcreative.com

(972) 377-0023

Frisco, TX  |  Tulsa, OK  |  Wayne City, IL