Storyboard Segue Identifier naming conventions - naming-conventions

I'm building a large storyboard and I was wondering if anyone has come up with helpful naming conventions for segue identifiers.
It looks like Apple just uses 'ShowX' in their examples where X is the name of the view it is showing. So far I prefer to use 'PushX' or 'ModalX' to keep track of which type of transition it is. Anyone have any other tricks or tips?

As with most things in programming, you can use any name you like.
But, as with most things in programming, names matter and good names are hard.
Here's how I name segues…
Good segue names
Name segues as you would name action methods. Name segues by what they will do. Good segue name examples:
addUser
showReport
editAccount
composeMessage
reviewChanges
Bad segue names
Avoid segue names that just describe what the segued to thing is or how it works.
Some examples of bad segue names!!:
Bad name 1! – segueUserDetailViewController – avoid this!
Bad name 2! – segueImageViewController – avoid this!
Bad name 3! – RecipeViewControllerToIngredientViewController – avoid this!
Why are these names bad?
These names are bad because they explicitly state their implementation. Instead of naming what they do, they name how they do it. This is a form of coupling.
So, if what you are doing is "showing a shopping basket", the fact that this happens to be done by presenting a ZZBasketViewController today, is totally irrelevant to the caller, and just burdens them with detail they don't care about. Perhaps tomorrow it'll be done with a ZZShoppingItemsViewController or an STSuperConfigurableStuffList.
So:
Use a name such as showShopping.
Avoid a name such as showBasketViewController.
A naming rule for all your programming
Usually the point of an abstraction is that a user doesn't and shouldn't know how the abstraction works. All they know is what it will do for them – what it means to them.
It is bad for any abstraction's name to needlessly specify how something must be done because then the caller prescribes how the callee must work.
This coupling will:
Burden the caller with knowledge it does not need.
Arbitrarily constrain the callee's implementation in a needless way.
Couple the caller to the callee in a pointless and costly way requiring maintenance or future removal.
If someone subsequently ignores the coupling and changes the implementation, then the given name begins to lie and will mislead a future programmer who looks at the code, unless the name is also changed.
Segues are abstractions. Segue names should not refer to the implementation.
Follow Cocoa's identifier convention
Both Swift and Objective C use camelCase for identifiers.
Names never contain _ or - characters.
With the exception of types, classes & protocols, all names should have a lower case first letter.
Name your segues in camelCase and give them a lower case first letter.
Uniqueness is not necessary
Segue names do not need to be unique within a storyboard. The names only need to be unique within a particular scene (view controller).
Ilea's answer mentions this, quoting from Ray's site:
It only has to be unique in the source scene; different scenes can use the same identifier.
…in fact, it often makes a lot of sense to have the same segue name in many scenes of a storyboard because you might be able to addLocation from a number of scenes.
Further tips for naming and working with Segues…
Scenes can have >1 segue going to the same other scene
This is something that I use a bit. You might have a view controller that can display and also edit account information: AccountVC. Nothing stops you having two or more segues from a scene that go to this same other view controller's scene: editAccount and showAccount. Your prepareForSegue:sender: can then use the segue ID to set up the AccountVC appropriately to either edit or just show.
Use the sender
Because segues are called with a sender, they feel very much like action messages.
Make use of the segue's sender to configure the destination view controller in your prepareForSegue:sender: implementation. This saves polluting your view controller with transitory state for this.
Here's an example of a table view delegate handling a tap:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
Account *const account = [self accountAtIndexPath: indexPath];
[self performSegueWithIdentifier: showAccount sender: account];
}
Which lets your prepare… method look like this:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([showAccount isEqualToString: segue.identifier])
{
AccountViewController *accountVC = segue.destinationViewController;
accountVC.account = sender;
}
}
Avoid #"stringLiterals" in performSegue: calls
If you're asking "What's this showAccount? You mean #"showAccount", right?". A big tip is: Use file scope variables for your segue names. Do not use #"string literals". So, at the top of my view controllers there's often a block like this:
DEFINE_KEY(showReport);
DEFINE_KEY(showPDF);
DEFINE_KEY(shareReport);
The macro DEFINE_KEY is in my project's .pch and looks like this:
#define DEFINE_KEY(keyName) static NSString *const keyName = ##keyName
… it creates a const NSString* variable who's value is equal to its name. The static means that it's only available in this "compilation unit" and doesn't pollute the global name space at link time.
If you use a variable like this, you have the compiler on your side. You can't get a name wrong because then it won't build. Code completion will help you finish a name that you start. You can refactor a name as you would any other variable. Even the syntax highlighter is on your side!
Think of unwind segues as like exceptions
Think of the destination view controller as being an exception handler for the unwind segue. The unwind segue propagates up the navigation stack very much like an exception propagates up the call stack. The segue looks for an unwind handler like an exception looks for an exception handler (catch block). It's looking for an unwind handler that is suitable for the type of unwind segue – again, this is like an exception handler for the type of exception being searched for.
Importantly, you can have many view controllers implementing the same unwind handler. The unwind will bubble up like an exception to the first view controller that handles it.
The usual advice for unwind naming is something like unwindToMessageList. This can make sense, but following the "exception handler" metaphor, it can be very useful to name unwind handlers by what they are handling. So, unwindFromEventDetails, unwindFromReport, or unwindFromCloseupImage might be good names describing what is being caught. These handlers can be implemented at multiple possible catch sites. The appropriate handler will be automatically selected using the navigation hierarchy.

There is no correct answer for this question. It depends on taste. I mitigate for readability. Don't be shy to give a long name for your segue identifier; give long and expressive names because Objective-C is a very verbose language leveraging us to write very readable code.
I have searched for an official convention, but I couldn't find any. Here's what Apple has to say:
You assign identifiers to your segues in Interface Builder. An
identifier is a string that your application uses to distinguish one
segue from another. For example, if you have a source view controller
that can segue to two or more different destination view controllers,
you would assign different identifiers to each segue so that the
source view controller’s prepareForSegue:sender: method could tell
them apart and prepare each segue appropriately.
Another quote from Ray Wenderlich's site:
Give the segue a unique Identifier. (It only has to be unique in the
source scene; different scenes can use the same identifier.)
An interesting approach for picking the identifier name (see above link for more):
First write the code that verifies for the segue identifier name BEFORE you even set the name in the interface builder. I'm talking about this code: if ([segue.identifier isEqualToString:#"SegueIdentifierName"])
Build & Run! Don't fill in the identifier name in the Interface Builder yet. You do this because you may have multiple outgoing segues from one view controller and you’ll need to be able to distinguish between them. If nothing happens while you run and trigger the segue you're working on, then your segue name identifier is unique and good to be used. If instead the code performs a segue that you haven't intended, you have a conflict for the sague name identifier.
Fix the conflict - if any.
Fill in the segue identifier in the Interface Builder and test that it does what you want.
I like this because it's like a TDD approach: write a failing test, write some code to pass the failing test, refactor, repeat.

"ViewControllerAToViewControllerB"
For example, if you have a MasterViewController and a DetailViewController, the segue identifier could be:
"MasterToDetail"

Personally I would not use the animation-type in front, if you change the animation, you'll need to go back to the code. However, if you declare the segue identifier as a constant in the source controller, you can more easily change the name at a later stage, without digging through the code.
I generally use the name I gave the controller, without the "ViewController". So RatingViewController would be "Rating" as storyboard. One exception are the unwind segues, I name those starting with "returnTo" ending in the name of the destination ("returnToRating").

Related

How to create a reusable subview with controls, without using xib file

I'm coding an app, and have several views that are reused (f. ex. containing a textfield, a label and a button)
Now i would like to just create it in a "globalviews.m" file once and add it as a subview to the mainview. (I really don't like the interface builder)
I could easily create a function that returns a UIView, with the components in them, but i would like to access the controls of course.
I was hoping something like (making "searchview" global in the viewcontroller in use)
// making searchview a global thingy
UIView *seachview ;
// rest of code here and then in viewdidload:
UIView *seachview = [[UIView alloc] init] ;
searchview = [[globaviews alloc] thesearchviews_name] ;
[self addsubview:searchview] ;
But how could I make controls inside easily accessible. Like:
NSString *something = searchview.textviewname.text ;
Or would this be a terrible idea to begin with?
Its just the way I would prefer to code...
You can create a custom class that is a subclass of UIView. You could then add properties for each control (the same way you would add NSString, NSNumber etc). Or you could create public methods to modify / get data.
e.g.
- (void)setTextFiledColour:(UIColor *)color;
- (NSString *)getTextFieldText;
My personal opinion (from a lot of experience) is to learn interface builder and deal with it. It is perfectly possible to do what you want and many people agree with you and choose to do it that way. Personally I've never seen it done "right". Meaning that its all done custom to create their own patterns and methodologies, avoiding years of experience and testing that has gone into the patterns provided by interface builder.
I find that storyboards in particular force a very specific pattern and style of coding. I think moving away form that is a huge mistake, as if used correctly it has great potential to abstract away UI / Code, prevents taking shortcuts that come back later on and most importantly when someone else needs to change it, there is no ambiguity, you can't make a mistake with class names or variable names etc.
I've used storyboards a lot and have yet to have an issue with them, on the flip side i've worked with developers who insist on doing it all by hand and have never had so many issues, and shocked at how long it takes to develop applications in this manner.
In the end its up to you.
Note
You mentioned wanting to create a view and reuse it. Its also possible to do this in a .xib file and reuse it that way. Losing some of the storyboard abilities but might give you a little of both worlds.

Objective-C / Cocoa Touch: Is it good design to set label texts in the setter of the UILabel object?

Let's say I have a two subclasses of UIViewController called MasterViewController and DetailViewController.
DetailViewController has a property of type NSNumber called level and a UILabel called levelLabel.
MasterViewController has a segue to DetailViewController called ToDetail. MasterViewController's prepareForSegue is like so
- (void)prepareForSegue:(UIStoryboardSegue)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"ToDetail"]) {
DetailViewController *detailVC = (DetailViewController *)segue.destinationViewController;
detailVC.level = [NSNumber numberWithInt:10]; // never mind the literal...pretend there was some algorithm for it
}
}
So then, in DetailViewController we implement the setter for levelLabel like so:
- (void)setLevelLabel:(UILabel *)levelLabel
{
if (levelLabel) {
_levelLabel = levelLabel;
_levelLabel.text = level.stringValue;
}
}
Is this good code design? Also, could you critique my code writing style? I pretty much wrote all this code on the fly so this is pretty much how I write code for the most part.
I thought of this question while showering because this is how I implement the setting of almost all the label texts that depend on a segue.
What follows is my own way of thinking about such relationships. Italics applies to your question.
You have the thing being controlled (the label) the controller (destination view controller) and the context it is being controlled within (the source view controller). This can also be expressed as model-view-controller, but I think thinking about a context can apply to much more specific and localised situations.
You should generally try to keep information flow going in one direction, from the context downwards. Objects should not have to be aware of the context in which they exist, ie they shouldn't have to ask for any information, they should be told everything they need to operate. So the source view controller should push the level to the destination view controller, the destination view controller should push this information to the label. This is what you already have, sort-of.
To build upon the above, not only should information flow in one direction, but I also try to ensure the relationships are causal, ie pushing information from one object to another should cause it to subsequently be pushed to the next object. Your code is not doing this which is probably why you have a bad feeling about it.
A more appropriate thing to do is set the text property of the label within the level setter, so that when you set or change the level, the label will update subsequently. The label may or may not be loaded so you will have to check whether it is using -isViewLoaded; -viewDidLoad is the appropriate place to set the text property upon first load.
(When I say 'push' that's just my way of thinking about setting properties or passing arguments because it implies directionality. It is really dependency injection. An example of pulling information would be delegates and data sources. But note here still the object isn't aware of any context, delegates and data sources are clearly defined as protocols, not classes, and usually within the same header file, and are themselves pushed onto the object from a surrounding context. So yes the object is asking for information, but on its own terms and from a system it has no knowledge of.)
Re coding style:
That's exactly how I write code but note Apple reserves the use of underscore prefixes

Separate Delegate and Controller

As many other developers new to Cocoa, I struggle with delegate and controller concept. I get the basics, but one thing bugs me. Practically every explanation says that "usually" or "in simple cases" (which are the only ones they give as examples) controller and delegate tend to be the same object.
That leads to a question: when would you want to separate controller and delegate for the same interface object?
Two general cases when using a separate class for your delegate is needed are
When you need to perform unrelated actions in response to the same delegate message, or
When you would like to share the logic of the delegate among multiple views or controllers.
An example of the first situation would be a page with two unrelated tables. Each UITableView would need its own delegate, so using the controller as the delegate would require an ugly if statement in each delegate method; defining and using separate delegates is clearly preferred in this case.
An example of the second situation would be a group of similar pages that show DB data from tables of similar structure. Pages themselves are sufficiently dissimilar, so you cannot reuse the controller in its entirety. If you choose to put the delegate into the controller, most of the logic behind the table view's data source would be identical. You can put the code into a shared delegate implementation, and have each controller instantiate that delegate with the configuration parameters specific to the table associated with this controller.
One note to keep in mind when using another object besides the controller as your delegate: the controller should retain / keep a strong reference to the delegate, since the view will only keep a weak / assign reference to it. See property "assign" and "retain" for delegate for more information on this.

iOS: Uniquely identify ViewControllers from Storyboard

I have a custom ViewController that is meant to be reusable, and an arbitrary number of instances will be chained together in a NavigationController in Storyboard, all sharing the same model as a delegate.
The ViewControllers need to tell the model which instance they are. Currently, they have an int property that they get from the segue, but it doesn't seem very idiomatic and doesn't lend itself to having multiple instances onscreen (for iPad). I figure there's got to be a cleaner way to do this, so does anyone know what it is? Thanks.
RESULT: self.view.tag
A UIViewController's UIView has a tag property which you can set from anywhere you want. You could also simply identify the type of the controller by using [self class]. Or simply use the memory location by referencing the controller directly.
Update You could simply implement a unique identifier for a UIViewController using a category.
I guess the "cleanest" way in terms of design architecture would perhaps be an array of ViewControllers. (It could be managed in the app delegate.) However, there are memory considerations - on the iPhone you would likely want to create and the destroy the view controllers as needed. The array could contain the identifier and perhaps some other model-related information in order to recreated the controllers as needed.
Too bad there is no property storyboardIdentifier of UIViewController. They can be instantiated with this id but it would be helpful if the viewcontroller can query its id.
I recently ran into this. I figured out you can add a "Restoration ID" in the storyboard. Then you can access it perhaps like this (depending on your use case)
navigationController?.viewControllers.first?.restorationIdentifier

Traversing the ViewController hierarchy properly?

I'm having trouble referencing one view controller from another. The code works but I get warnings which makes me think I'm going about it wrong. I'm trying to reload the data in a tableView whose controller is in a NavigationController.
What's wrong with a message like this:
From the AppDelegate:
[self.tabBarController.selectedViewController.topViewController.tableView reloadData];
Although this works, I get the warning request for member 'topViewController' in something not a structure or union because Xcode doesn't know that the selectedViewController will return a navigationController. So I could do the following:
UINavigationController *myNavigationController = self.tabBarController.selectedViewController;
[myNavigationController.topViewController.tableView reloadData];
But then I get this warning: incompatible Objective-C types initializing 'struct UIViewController *', expected 'struct UINavigationController *'
How far do I have to go with this? The first line works. To get to the "right way" is it gonna take 8 lines of code?
A major code smell here, IMO. You're trying to do action at a (great) distance. It's not exactly clear what you're trying to accomplish, nor why you need to do this action from the app delegate. I have seen some developers treat the app delegate like a giant catch-all global lump of mud, and I think this is an anti-pattern that should be eliminated from iOS development.
Back to your question: you're trying to force a table view controller, inside a tab view controller, to reload its data. I'm assuming this is in response to something happening. Why not have the view controller in charge of that table watching for that event instead of the app delegate? That way, the thing that owns the table view is directly controlling it -- which is the entire point of the MVC pattern. This is a much better approach than having the app delegate drill down through a hierarchy to find a table view... in terms of complexity, readability, and brittleness.
If, for some reason, you can't or won't have that view controller observing for the event directly (hard to fathom why offhand), you could always have the app delegate post an NSNotification and let the view controller in charge of the table register as an observer for it. Not as good as direct observation, but definitely better than your current approach.
You can't use dot-notation unless the compiler knows what type of object you are using it on, and that that object type can receive a message with that name.
You can use dot-notation with a bunch of type-casts (which in this case, is hideously ugly):
[((UITableViewController *) ((UINavigationController *) self.tabBarController.selectedViewController).topViewController).tableView reloadData];
Or you can break it up into discrete steps:
UINavigationController *navController = (UINavigationController *) self.tabBarController.selectedViewController;
UITableViewController *tableViewController = (UITableViewController *) navController.topViewController;
[tableViewController.tableView reloadData];
Note that I'm assuming that your top VC is a sub-class of UITableViewController.
You really shouldn't be accessing the .tableView property externally - you should encapsulate that behaviour with a reloadData method on the View Controller itself. Even if all it does is call reloadData on its .tableView, you should encapsulate it. This will make your code more modular (which makes it easier to understand for you and others), and make it easier to expand on and add complexity to your View Controller down the track.
Without knowing exactly how this app is structured, I would guess that you're probably better off using notifications or observers to get your VC to reload its data. If you have some global event that requires a UI refresh, an NSNotification is a good way to make the UI layer get the message while keeping your code nice and modular.