Appside Down: Should my App Work Upside Down?

When creating an iPhone app, many app developers lean towards disallowing their app being used upside down. Reasons given are usually:

  • The big companies (Apple, Google) have most of their apps only in portrait mode, not in upside down portrait mode
  • When allowing upside down and a phone call comes in, the user is confused, which would not have happened, if the user had to use the phone in portrait mode only

While I do see the reasons behind this, there are situations, where this portrait mode only is just not convenient:

  • When the app is used on a bike, in a rain-shelter but a cable needs to go outside (either phone or charging / lightning). If you are lucky, your bike app shelter knows where the cable is. Else you wish, you could use your navigation app upside down.
  • When the app is used in a car and you want to place the iPhone holder on the windshield in a phone holder as low as possible – then you either are 2-3 cm (or 1 inch) over the windshield due to the charging / lightning cable going out below, or you can turn your iPhone upside down and put the cable in on top, allowing you to lower the phone a little more.
  • When you are charging your phone in a car where there is no phone holder (e.g. rental car), then the cup holders are helpful (sometimes). Being able to put a phone into the cup holder upside down and still being able to see the content of your app helps you save the money for the car phone holder.

So when designing an app that is used over a longer time, e.g. could be used with the lightning cable being plugged in, then the option of using the app upside down suddenly may make sense for your customers.

What do you think?

ToDo before publishing an Android App

Assume your app is ready. What do you need to do to get it into the Google play store?

  1. Generate a signed Apk
    1. Check that the version has a unique version code
    2. Check the applicationId to be unique and starts with your company name, e.g. “com.smallapps.”
  2. Create a new entry in your Google Play Store
    1. Decide which language is the default language
    2. Give it a title up to 30 char long
    3. Give it a short description up to 80 char long
    4. Give it a long description up to 4000 char long
    5. Upload at least 2 screen shots, maximum 8 Screen shots per Type of each: Telephone, Tablet, Android TV and Android Wear – of course only if it is applicable
    6. Upload a “high resolution” symbol, e.g. the icon if your app in 512×512 pixels. But could also be a nicer version. Will be shown in the google play store entry.
    7. Upload a “Funktionsgrafik” (sorry, need to check the english name): Another image of size 1024×500 pixels. Will also be shown in the play store entry.
    8. Optional: Upload an ad picture 180×120 pixels, a TV banner or a video
    9. Select the App-type: App or Game
    10. Select the Category: Learning, Work, Comics, …
    11. Choose a content rating (age rating)
    12. Answer some questions to result in an age rating
    13. Enter an email Address, where you can be contacted
    14. Enter a link to your privacy policy (or click “will be supplied later”)
    15. Enter price or “for free”. This decision is forever!
    16. Decide in which countries to distribute the app
    17. Answer if the app contains ads
    18. Answer if you and your app agree to the android content policy
    19. Answer if you know about export control
    20. Upload the Apk into Alpha or Beta test or upload it to the final store
    21. Decide if it is a open or closed test phase
  3. Then wait until the App is published
    1. If it is a alpha or beta test, you may share the link to find the Apk in the  App store

That’s it already, have fun!

boolForKey:

When storing objects in the NSUserDefaults or in some other key-value store, convenience methods are often used to store and retrieve keys.

boolForKey: is such a method, but since the BOOL data types have only two values, the the information, if the key was in the store or not, is lost in this method. Because it returns NO if the value NO was stored and it also returns NO if no information was stored at all.
This is the reason, why I prefer to store “default-off” values in the user defaults. I rather check for disclaimerWasRead than for shouldShowDisclaimer. I rather check for values which can be off or non-existing by default and will be YES for exactly one decision than having distinguish all three states.

[Edit:] A similar situation arises if you use integerForKey:. The value 0 does not inform you, if the key existed at all, or if it existed as 0. If you use objectForKey: first and then transform the result to a integer value, than you still can distinguish if you received nil from the first call.[End Edit]

[object compare: weHaveAProblem]

When using Objective-C Methods like compare: and similar methods like caseInsensitiveCompare: you should be aware of one possible problem:

  • Compare returns NSOrderedSame when two objects are considered equal
  • NSOrderedSame is also returned, if you call compare: on a nil-pointer!
  • Why is that?

    Because any method being called on a nil pointer returns nil.
    Which is the same as 0 (zero).
    Which is the same as NSOrderedSame.
    Ouch!

    Assume we have:
    NSString *stringa = @"huhu";
    NSString *stringb = @"huhu";
    BOOL result = [stringa compare:stringb] == NSOrderedSame;

    And print it out like this:
    NSLog(@"[@\"%@\" compare:@\"%@\"] == NSOrderedSame is %@ ",
    stringa, stringb, result ? @"yes":@"no");

    Then we get:
    [@"huhu" compare:@"huhu"] == NSOrderedSame is yes
    If we call instead
    stringb = @"not huhu";
    result = [stringa compare:stringb] == NSOrderedSame;

    We will get:
    [@"huhu" compare:@"not huhu"] == NSOrderedSame is no
    Of course, nobody warns us when we use a nil pointer instead:
    stringa = nil;
    result = [stringa compare:stringb] == NSOrderedSame;

    Which of course results to:
    [(null) compare:@"not huhu"] == NSOrderedSame is yes
    Luckily, writing the code this way:
    result = [stringb compare:stringa] == NSOrderedSame;
    Results in the correct value again:
    [not huhu compare:@"(null)"] == NSOrderedSame is no

    Learnings?
    So we learned: We need to check the left object, the method receiver, for not being nil to prevent strange results. And in cases, where we compare against a constant object, we might as well put the constant on the left hand side, resulting in e.g.
    result = [@"huhu" compare:objectb] == NSOrderedSame;
    to prevent stumbling over nil pointers.

    Xcode Asset Catalog

    When using the asset catalog, I usually name the image set in the same way as the image file. E.g. I create an image set leftIcon with files leftIcon@2x.png and leftIcon@3x.png in it.

    Today I decided different.

    I created an image set named button_pressed_background and used a file called grey_background.png.

    When fetching the icon with UIImageNamed:@"...", I was not sure which name to use. It turned out, that I need to use the asset catalog image set name, e.g.
    UIImageNamed:@button_pressed_background", not the filename (which returns nil when used).

    Last question I had, was: Since I used a small grey icon to fill the complete button, is the grey on a 3x-Device the same as on the 2x device? Or do I need a 2x and 3x version of the image file, although it only contains grey pixels? I assumed no, since it looked OK. So hopefully the customers will agree…