3

I want to separate the data from the implementation in several classes, for many reasons.

One reason, for example, is that I have a few different menu screens that display text. I want to have one class that lists all the text for all the menus in one place, and then have the different menu objects read from that class when they initialize.

That way, whenever I want to make changes, I know exactly where the text variables are, and if I want to, I can change a bunch of them all at once.

I want to use the same principle in a lot of different ways, for example, setting the color and alpha values of various UIViews; having them all in one place would enable me to coordinate their settings and make small adjustments very easily.

Added to these reasons is that I'm working with a small team of other developers, and if we all know we're storing this kind of information in one place it's easier to understand each other's code.

So basically I want one big UberData class that every other class can read from and write to.

As far as I can figure, the only way to do this is make each of the needed variables a property, so I'll basically have a big methodless class with a heck of a lot of properties. But to my understanding, that's kind of bending the OO rules, because as much as possible classes should hide their innards. Not to mention the whole things seems really kludgey.

So the question is: is there a better way to do this than having the class with a million properties, and is it even proper to do it, from an OO perspective, at all?

Le Mot Juiced
  • 3,761
  • 1
  • 26
  • 46

5 Answers5

2

One big UberData class (and really, if you are thinking properties, you mean one instance of that class) is the wrong approach.

What do menu strings and view colors have to do with each other? Nothing. Therefore they don't belong in the same class.

Strings

For your menu strings, look into the NSLocalizedString macro and creating a strings file. You could create a CommonStrings class that wraps all of your calls to NSLocalizedString:

@interface CommonStrings : NSObject

+ (NSString *)open;
+ (NSString *)save;
// etc.

@end

@implementation CommonStrings

+ (NSString *)open {
    return NSLocalizedString(@"open", @"menu item title for opening a file");
}

+ (NSString *)save {
    return NSLocalizedString(@"save", @"menu item title for saving a file");
}

// etc.

@end

This approach means you only write @"open" in one place, and then you refer to [CommonStrings open] when you need the (localized) string. The compiler checks that you've spelled [CommonStrings open] correctly, which is nice.

However, it's still probably better to break this into multiple helpers (one for each independent part of your app), rather than one giant helper for your entire app. If you use one giant catch-all class, then compiling your app takes longer because so much has to be recompiled every time you add or remove a method in this class.

UIView colors

First, watch the appearance customization videos from WWDC 2012 and WWDC 2013 and read up on UIAppearance. Maybe you can just use that to customize your app's colors.

If that doesn't suffice, create a category on UIColor for your app's colors:

@interface UIColor (MyApp)

+ (UIColor *)MyApp_menuBackgroundColor;
+ (UIColor *)MyApp_menuTextColor;
// etc.

@end

@implementation UIColor (MyApp)

+ (UIColor *)MyApp_menuBackgroundColor {
    return [self colorWithPatternImage:[UIImage imageNamed:@"menuBackgroundPattern"]];
}

+ (UIColor *)MyApp_menuTextColor {
    return [self colorWithWhite:0.0 alpha:1.0];
}

// etc.

@end

Again, it may be better to have multiple helper categories for different parts of your app, so you don't have to recompile as much when you add or remove a category method.

rob mayoff
  • 375,296
  • 67
  • 796
  • 848
  • "What do menu strings and view colors have to do with each other? Nothing." I disagree. What they have in common is they both fit the category "things I may want to fiddle with at the same time so I really don't want to go hunting them down lots of different places." – Le Mot Juiced Sep 13 '13 at 20:48
  • Further, if they don't belong together then just about every XIB written is an offense. – Hot Licks Sep 13 '13 at 20:57
  • A XIB is generally for defining a single scene in your app. Similarly, one class (or instance) per scene to hold all of the scene's appearance details (menu item labels, colors, fonts, etc.) makes sense. One giant class to hold all of the appearance details for all of the independent scenes in an app doesn't make sense. – rob mayoff Sep 13 '13 at 21:29
  • @robmayoff perhaps it's relevant that I have a relatively small project. So "all of the appearance details for all of the independent scenes" isn't as much as you might expect. – Le Mot Juiced Sep 18 '13 at 20:51
2

First off, forget "propriety". What is proper is what works, works reliably and efficiently, is easy to understand, and easy to maintain. Adhering to "object-oriented principles", while to some degree a worthwhile goal, should not cause you to do awkward, error-prone, and inefficient things. Just about any "programming rule" can be followed too literally.

Having dozens (or hundreds) of properties is clumsy and hard to maintain from several standpoints, so that's basically a non-starter.

More appropriate is having a few queryable interfaces that return the values you want. The "right" scheme often depends on the characteristics of the data, but one can, eg, have a method with a simple switch statement or "if ladder" that returns value Y given value X (where X is, eg, an enumeration value).

One can also have what is either actually or conceptually an NSDictionary which you query with a character "key" value.

A variation of that is a property list, which allows you to describe the data in a data file, vs having to code it into source. You can also put the data in a JSON file, if that suits your design and habits better.

And there are several other schemes that I've used in the past that aren't occurring to me just now.

Added:

OK, I'll give an example of something that is basically impossible with "macros" and "extern variables". In several cases, in some of the code I work on, there are objects that contain information about specific events. There may be 50 different categories of events, each with a different set of properties (sub-category, display color, text for various conditions, etc).

One could, of course, have several hundred declared constants, of the style "kXyzCategoryAbcSubCategory", "kXyzCategoryAbcColorMode", "kXyzCategoryAbcTitleText", etc, but maintaining is a PITA, and typos in use are routine. Plus it's not really usable, since you can't take "category" from the object and "index" the attributes.

Instead, I use one of two schemes:

  1. A set of callable interfaces where you pass in the constant "kXyzCategoryAbc" (or the category value dynamically extracted from the object) and call one of several methods -- xyzSubCategory, xyzColorMode, xyzTitleText -- and the method returns the required data.
  2. A callable interface where you pass in the category value and it returns an NSArray (with keys, eg, of "subCategory", "colorMode", and "titleText").

Both of these techniques work pretty well, though one or the other may be preferred depending on circumstances. Both allow you to maintain the data as a table of some sort, and allow you to use the same constant to fetch multiple values, vs having to introduce 50 new constants when you add one new attribute to one of your category objects.

Hot Licks
  • 47,103
  • 17
  • 93
  • 151
1

Yes, the common response to a single, shared configuration object is the Singleton pattern. Briefly, a class method knows how to make or return an instance of the class and the instance knows how to configure the needed things. I think you will find many results from stack overflow by searching although What should my Objective-C singleton look like? is one easy example.

Beyond that, I would encourage you to look into how localization works in iOS - the problem you initially reference of having a source for menu titles and things is one that the localization library has solved for you, and in an extensible way.

You may also want to look at property lists (aka, plists) which is a structured data file that can be easily read in iOS applications.

Community
  • 1
  • 1
James
  • 1,118
  • 7
  • 13
1

You shouldn't use a class for this at all. Use macros for code internal constants, and extern variables for values that can change.

Note: User visible strings should be done through localization files -- see the other answers.

When it comes to constants, I usually have a CPConstants.h (CP being my class prefix) file that looks something like this:

#define kCLConstant1 42
#define kCLColorConstant [UIColor blackColor]

And so on.

If you need values to be changeable, first create a CLConstants.m file, like this:

#import "CLConstants.h"

int some_global_var = 42;
UIColor* some_global_changable_color = [UIColor blackColor];

And so on. Then, in CLConstants.h, add a line like this for every variable you declared in CLConstants.m:

extern type varname;

Now all files that include/import CLConstants.h can use and change those variables, and changes will be visible to all other files in the project.

Linuxios
  • 34,849
  • 13
  • 91
  • 116
  • Awesome! And simple. Thanks! Ignore the down voter--you got my vote! – Le Mot Juiced Sep 13 '13 at 20:47
  • +1: To cancel out the down vote. Also, this is a reasonable solution, especially if most values are indeed constant. – JRG-Developer Sep 13 '13 at 20:48
  • It's simply wrong. Using a property list, eg, is an excellent approach in many cases. – Hot Licks Sep 13 '13 at 20:48
  • @JRG-Developer: Thanks. Sometimes doing things the C way is just better. – Linuxios Sep 13 '13 at 20:48
  • 1
    @HotLicks: Using a property list is a heavyweight solution for some internal strings or magic numbers. Anyway, what's wrong with macros? Writing `kCPConstant1` is a lot more convenient than writing `[myPropertyDict objectWithKey:@"someKey"]`. And then you've just introduced one more constant string in the property list key. Property lists have plenty of uses -- just not this. – Linuxios Sep 13 '13 at 20:50
  • (And using macro variables for constants is discouraged by numerous coding standards.) – Hot Licks Sep 13 '13 at 20:50
  • @HotLicks: It's better than hard coding them. What are you supposed to do then? Apple's own code does it. It might be discoraged in C++ and other languages, but not Obj-C. – Linuxios Sep 13 '13 at 20:51
  • @Linuxios - The OP talked about a bunch of different things, such strings. Using macro variables for strings is a lousy approach -- it gets unmanageable pretty quickly. – Hot Licks Sep 13 '13 at 20:52
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/37367/discussion-between-linuxios-and-hot-licks) – Linuxios Sep 13 '13 at 20:53
  • @Linuxios - There is (contrary to this answer's assertion) no single approach appropriate to all cases. – Hot Licks Sep 13 '13 at 20:54
  • @HotLicks: Where do I assert that? I assert that using classes for this is a bad idea. Also, fixed to mention user visible strings. – Linuxios Sep 13 '13 at 20:56
  • You said "You shouldn't use a class for this at all. Use macros for code internal constants, and extern variables for values that can change." – Hot Licks Sep 13 '13 at 20:59
  • @HotLicks: Did you see my edit about localization and user visible strings? Anyway, let's stop this rather pointless debate. BTW, +1 to your answer. They all work for different cases. – Linuxios Sep 13 '13 at 21:01
  • Yeah, unfortunately Apple's localization scheme kinda sucks, but that's another topic entirely. – Hot Licks Sep 13 '13 at 21:05
0

You basically have two choices in sharing data across several controllers:

1) Use a singleton

In this instance, a singleton data model sounds appropriate. (A singleton data model is a single, shared instance of a data model used by several controllers.)

As you mentioned, however, you may indeed wind up with a class that has lots of properties by doing this... there's nothing wrong per se about this, however. Just be choosy in the properties that you have on the singleton... if it's not shared by several controllers, it probably shouldn't be there.

See Apple's documentation on how to create such here.

Here's another SO post about Singletons in iOS.

2) Use a header (.h) file in which you define all your shared variables, then import this wherever you need them (or put into the .pch file to be automatically imported everywhere in your project).

Typically, this is done using static constant variables. I'd imagine you could possibly do this via just static variables so that you can edit them, but at least the last I checked, the compiler may give incorrect unused variable warnings.

Community
  • 1
  • 1
JRG-Developer
  • 12,454
  • 8
  • 55
  • 81
  • Why -1? A singleton is appropriate here. Give a reason or suggest something else. – JRG-Developer Sep 13 '13 at 20:33
  • Why'd you get down voted? Just singleton hate? I hate it when people downvote without explaining. – Le Mot Juiced Sep 13 '13 at 20:33
  • It does nothing to answer his question. Singleton obsession? – Hot Licks Sep 13 '13 at 20:34
  • (And how do you like it when people serially downvote you because they didn't like that you downvoted their answer? This is a very good reason for not responding to "why was I downvoted" queries.) – Hot Licks Sep 13 '13 at 20:35
  • It answers the question: a shared model used by several controllers. It's either that or a flat `.h` header file, full of constants. Based on the vignette provided, he wants to be able to change the variables, so constant variables is out. – JRG-Developer Sep 13 '13 at 20:35
  • No, his question is about having thousands of properties, or some other scheme. – Hot Licks Sep 13 '13 at 20:36