Tell me more ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

EDIT: One of the bigger questions I have is that, is using switch-case more efficient then using this?

Right now in my application (a weather app), I have one section of code in my View Controllers to set up the condition image, and the background image. It's about 400 lines of if-else statement at the end. The app's performance is fine and works fast, but is it bad to have this? Would it be something that say Apple would be looking at and would reject? The code is very easy to read, and makes perfect sense in my opinion. Here's a snippet:

NOTE THIS IS BAD CODE, I DID NOT USE ELSE-IF's AND HAD NESTED STATEMENTS, AND IT IS NOT EASILY READABLE, AND HAS DUPLICATED CODE

- (void)updateImages:(ICB_WeatherConditions *)weather {
    if ([condition isEqualToString:@"113"]) {
        conditionsImageView.image = [UIImage imageNamed:@"Sun.png"];
        BGView.image = [UIImage imageNamed:@"Sun.jpg"];
    } else {
        if ([condition isEqualToString:@"116"]) {
            conditionsImageView.image = [UIImage imageNamed:@"Mostly_Sunny.png"];
            BGView.image = [UIImage imageNamed:@"Partly_Cloudy.jpg"];

        } else {
            if ([condition isEqualToString:@"119"]) {
                conditionsImageView.image = [UIImage imageNamed:@"Overcast.png"];
                BGView.image = [UIImage imageNamed:@"Overcast.jpg"];
            }
            else {
                if ([condition isEqualToString:@"122"]) {
                    conditionsImageView.image = [UIImage imageNamed:@"Overcast.png"];
                    BGView.image = [UIImage imageNamed:@"Overcast.jpg"];
                }
                else {
                    if ([condition isEqualToString:@"143"]) {
                        conditionsImageView.image = [UIImage imageNamed:@"Mist.png"];
                        BGView.image = [UIImage imageNamed:@"Foggy.jpg"];
                    }
                    else {
                        if ([condition isEqualToString:@"176"]) {
                            conditionsImageView.image = [UIImage imageNamed:@"Scattered_Thunderstorms.png"];
                            BGView.image = [UIImage imageNamed:@"Scat_Tstorms.jpg"];
                    }

It keeps going on after that. As you can see, the statements are almost identical, and the only reason I have a lot is because I have a lot of conditions. Is it bad practice to do this? and if it is, is there any way that is more efficient then this? Thanks!

share|improve this question
4  
Does objective C allow switch-case with strings like the newer versions of Java? If it does I would use switch-case. – Jon Taylor Jul 12 '12 at 14:12
@JonTaylor I'm pretty sure it does, I think I've seen it in other code, but is this any less efficient? – Programmer20005 Jul 12 '12 at 14:13
I have no idea about the efficiency of switch-case vs if/else in objective c however it will be more readable. – Jon Taylor Jul 12 '12 at 14:14
3  
Objective-C doesn't support switch on NSString. But your code would be more readable if you write your conditions like this: Can Objective-C switch on NSString? – Florent Jul 12 '12 at 14:15
2  
@Programmer20005 use [NSString integerValue] for a switch statement. – Christoph Winkler Jul 12 '12 at 14:18
show 7 more comments

migrated from stackoverflow.com Jul 12 '12 at 17:15

13 Answers

up vote 26 down vote accepted
- (void)updateImages:(ICB_WeatherConditions *)weather {

    static NSDictionary *map = nil;
    if (map == nil) {
        map = [[NSDictionary dictionaryWithObjectsAndKeys:
            @"Sun,Sun", @"113",
            @"Mostly_Sunny,Partly_Cloudy", @"116",
            @"Overcast,Overcast", @"119",
            ...
            nil] retain];
    }

    NSString *pair = [map objectForKey:condition];
    NSArray *images = [pair componentsSeparatedByString:@","];

    if ([images count] != 2) {

        // handle failed configuration

    } else {

        conditionsImageView.image = [UIImage imageNamed:[images objectAtIndex:0]];
        BGView.image = [UIImage imageNamed:[images objectAtIndex:1]];

    }
}
share|improve this answer
This worked! For everyone else, the reason I chose this answer, (which is the same thing that @Mark had too, except this had a better example) instead of using switch statements, is that after I implement this, it's much easier for me to change an image or background image without much hassle at all, not only that, but after I put this in the App Delegate, I was able to share the dictionary with all the view controllers, making my life 5x easier. All the methods below are also fantastic for people with similar needs. Thanks everyone! – Programmer20005 Jul 12 '12 at 15:35
4  
This is a great solution--really the only one. anyone who isn't physically hurt by the original shouldn't be a programmer! You MUST learn to recognize patterns like this as repeated code and therefore completely unacceptable--serously NEVER give into the temptation to cut/paste/edit like the original, it's sinful--it is the cause of just about every serious coding hassle I've ever encountered. – Bill K Jul 12 '12 at 15:43
I find it sad that this answer has so few upvotes. Did no one else notice all the duplicated code in each if statement...? – Izkata Jul 12 '12 at 16:07
@BillK Why is duplicated code bad? (I changed to this, but just for informational purposes) – Programmer20005 Jul 12 '12 at 16:09
2  
@Programmer20005 - Duplicated code is a nightmare for maintenance and reading. If things change, you have to change every single spot--good luck finding them all, reliably, every time! If someone has made an alteration in one spot, good luck noticing it after reading 20 versions which are all the same! – Rex Kerr Jul 12 '12 at 16:53

In this case your indentation makes the code drift over to the right so does make the code difficult to read so does need some change .

First I would note that a lot of code is repeated and also as noted a switch after converting the condition to an int is possible.

However in this case your code is effectively doing multiple lookups so I would look at using NSDictionaies (or NSArrays if the conditions are 0 to a number) and then do a straignt lookup

e.g. setup

NSDictionary* conditionsImages = [NSDictionary dictionaryWithObjectsAndKeys: 
                @"113", @"Sun.png",
                @"116", @"Mostly_Sunny.png",
     ...
                nil];

Or read the dictionaries from a file

Access them by

- (void)updateImages:(ICB_WeatherConditions *)weather {
    conditionsImageView.image = [UIImage imageNamed:
                   [conditionsImages objectForKey:condition]];
    BGView.image = [UIImage imageNamed:[otherImages objectForKey:condition]];

  ...
}
share|improve this answer
This seems like the best way so far, I'll try it out and come back! Thanks! – Programmer20005 Jul 12 '12 at 14:29
A dictionary is definitely the DRYest method. Perhaps also, since there is a limited amount of pictures, make a second dictionary with image description and image path, so that if the path changes, the english name remains: pictograms = dictionary("sun" => "/path/to/lage-sun.png); conds = dictionary("113" => "sun"); – jurgemaister Jul 12 '12 at 14:45
It's not just the indentation. The code really does have nested ifs, as if the programmer was not aware of the "else if" construct. – Alex Feinman Jul 12 '12 at 15:46

There is a lot of overhead in calling the string methods on the objects + strings can not be used as the condition in switch statements. You are lucky that the values you are driving your logic by are ints. So just convert the value to an int value and run your code in a switch statement like so:

 - (void)updateImages:(ICB_WeatherConditions *)weather {

    //convert string to int value
    NSString *conditionS = [weather condition];
    int var = [conditionS intValue];

    switch(var){
        case 113:
            conditionsImageView.image = [UIImage imageNamed:@"Sun.png"];
            BGView.image = [UIImage imageNamed:@"Sun.jpg"];
        break;
        case 116:
            conditionsImageView.image = [UIImage imageNamed:@"Mostly_Sunny.png"];
            BGView.image = [UIImage imageNamed:@"Partly_Cloudy.jpg"];
            break;
        case 119:
            conditionsImageView.image = [UIImage imageNamed:@"Overcast.png"];
            BGView.image = [UIImage imageNamed:@"Overcast.jpg"];            
            break;

            (etc)
            .
            .
            .
        default:
            (default code)
    }


}
share|improve this answer

In my opinion (if a switch is not an option), there is nothing wrong with multiple ifs, I just would rewrite in the following manner for better readability and less indentations.

- (void)updateImages:(ICB_WeatherConditions *)weather
{
    if ([condition isEqualToString:@"113"])
    {
        conditionsImageView.image = [UIImage imageNamed:@"Sun.png"];
        BGView.image = [UIImage imageNamed:@"Sun.jpg"];
    }
    else if ([condition isEqualToString:@"116"])
    {
        conditionsImageView.image = [UIImage imageNamed:@"Mostly_Sunny.png"];
        BGView.image = [UIImage imageNamed:@"Partly_Cloudy.jpg"];
    }
    else if ([condition isEqualToString:@"119"])
    {
        conditionsImageView.image = [UIImage imageNamed:@"Overcast.png"];
        BGView.image = [UIImage imageNamed:@"Overcast.jpg"];
    }
    else if ([condition isEqualToString:@"122"])
    {
        conditionsImageView.image = [UIImage imageNamed:@"Overcast.png"];
        BGView.image = [UIImage imageNamed:@"Overcast.jpg"];
    }
    else if ([condition isEqualToString:@"143"])
    {
        conditionsImageView.image = [UIImage imageNamed:@"Mist.png"];
        BGView.image = [UIImage imageNamed:@"Foggy.jpg"];
    }
    else if ([condition isEqualToString:@"176"])
    {
        conditionsImageView.image = [UIImage imageNamed:@"Scattered_Thunderstorms.png"];
        BGView.image = [UIImage imageNamed:@"Scat_Tstorms.jpg"];
    }
    ...
share|improve this answer

Put your image mapping in a property list:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>113</key>
  <dict>
    <key>condition</key>
    <string>Sun.png</string>
    <key>background</key>
    <string>Sun.jpg</string>
  </dict>
  <key>116</key>
  <dict>
    <key>condition</key>
    <string>Mostly_Sunny.png</string>
    <key>background</key>
    <string>Partly_Cloudy.jpg</string>
  </dict>
</dict>
</plist>

And rewrite your method:

- (void)updateImages:(ICB_WeatherConditions *)weather {
  NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:@"your.plist"];
  NSDictionary *images = [dict objectForKey:condition];

  if (images) {
    conditionsImageView.image = [UIImage imageNamed:[images objectForKey:@"condition"]];
    BGView.image = [UIImage imageNamed:[images objectForKey:@"background"]];    
  }
  else {
    // fallback
  }
}
share|improve this answer

Why don't you just rename your Files to the matching Number?

conditionsImageView.image = [UIImage imageNamed:[NSString stringWithFormat:@"%@.png", condition]]; 
BGView.image = [UIImage imageNamed:[NSString stringWithFormat:@"%@.jpg", condition]];
share|improve this answer
Probably because one file e.g. Overcast.jpg is used for more than one number - so only works if you can use hard links in the file system – Mark Jul 12 '12 at 14:15
This one is actually a very good response. Since the images are static, this is the best approach ever possible and saves from redundancy of switch and if-else statements. +1 – Eugene Jul 12 '12 at 14:15
@Mark of course this could happen. Nevertheless the example states nothing like this. Exceptions could be hardcoded. – Christoph Winkler Jul 12 '12 at 14:16

I'd say a switch statment would be preferable here, especially as you are performing the If on the same expression each time.

Were all the If conditions different variables then I'd say keep it as it is, but a switch is ideal here.

share|improve this answer

You should combine your else and if so you don't over nest since it's all at the same level.

    if (condition) {
        // Do something.
    } else if (someOtherCondition) {
        // Something else.
    }

Another thing you could do (it would just have a long piece of code else where) is to set up a Dictionary, I think you could even use an XML file or something to load and it would be a bit cleaner and shorter.

share|improve this answer

If you can convert the strings to integers, it would be more readable if you use the switch statement.

Otherwise, here's a similar question, with several different solutions offered. I'd suggest shipping as is, since you've already measured performance and it's fine.

Alternative solutions include

  • using enums (which you can switch over)
  • having the strings be values in a dictionary (with ints as keys that you can switch over).
  • using elseif to make the indentation look nicer:

    if { 
        ...
    else if{
        ...
    }
    
share|improve this answer

The main readability problem here has to do with the indentation.

Don't open a bracket after the else and put the if statement directly:

if (...) {
} else if (...) {
} else if (...) {
}

instead of:

if (...) {
} else {
    if (...) {
    } else {
        if (...) {
        }
    }
}
share|improve this answer

In theory there is nothing against such a thing but you are repaeting a lot of code for setting the image therefore i would recomment to use a switch statement instead of the if-elses and make a own method for the inner part of the if-elses. And to make it even more readable use a enum:

The enum:

enum {
    WeatherTypeSun = 115,
    WeatherTypeMostlySunny = 116;
}   WeatherType;

and here the switch statement with use of the enums:

- (void)setWeatherImages:(NSString *)weather {
    conditionsImageView.image = [UIImage imageNamed:[weather stringByAppendingString:@".png"]];
    BGView.image = [UIImage imageNamed:[weather stringByAppendingString:@".jpg"]];
}

- (void)updateImages:(ICB_WeatherConditions *)weather {

    int weatherCondition = [condition intValue];

    switch (weatherCondition) {
        case WeatherTypeSun: 
            [self setWeatherImages:@"sun"];
            break;
        case WeatherTypeMostlySunny:
            [self setWeatherImages:@"mostly_sunny"];
            break;
    }
}
share|improve this answer

Use else if!

if (foo) {
    //do something
}
else if (bar) {
    //do something else
}

Apple will definitely not reject you for what you have, but using else if will make it much easier to read and get rid of all that indenting. A switch statement is really no faster, and plus you usually have to have a break; for every case, which is kind of silly to me.

share|improve this answer
else if is what he has already the { } make no difference since its a single line executed after the else. – Jon Taylor Jul 12 '12 at 14:18
@JonTaylor It's logically the same, but so much more readable. – woz Jul 12 '12 at 14:19
True, you didn't really explain this in your post though, it seemed more like you were giving it as an alternative when actually its essentially a code style difference. – Jon Taylor Jul 12 '12 at 14:21
@JonTaylor Ok, I improved my answer. – woz Jul 12 '12 at 14:33
@ woz: In your comment, you have mentioned that: "A switch statement is really no faster"...Can you please explain? – Vikram Jul 12 '12 at 16:46
show 1 more comment

You should look at switch statements for this. The template code in Xcode looks something like this:

switch (condition) {
    case 113:
        // do something
        break;
    case 116:
        // do something
        break;
    /* etc. */
    default:
        // when all else fails, do something
        break;
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.