Using non-secure web services in iOS

As of iOS7ish, if you try to access a normal web site with http:// instead of the more secure https://, you get an error at runtime:

App Transport Security has blocked a cleartext HTTP (http://) resource load since it is insecure. Temporary exceptions can be configured via your app’s Info.plist file. To allow any http:// to get through, add the following to your Info.plist file

	<key>NSAppTransportSecurity</key>
	<dict>
		<key>NSAllowsArbitraryLoads</key>
		<true/>
	</dict>

or if you want specific domains to be allowed (example.com in this case):

	<key>NSAppTransportSecurity</key>
	<dict>
		<key>NSExceptionDomains</key>
		<dict>
			<key>example.com</key>
			<dict>
				<key>NSIncludesSubdomains</key>
				<true/>
				<key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
				<true/>
				<key>NSTemporaryExceptionMinimumTLSVersion</key>
				<string>TLSv1.1</string>
			</dict>
		</dict>
	</dict>

That being said, its better to use secure web sites when possible.

Restricting Orientations in different view controllers

At some point you may want to restrict certain device orientations to certain view controllers. Its quite easy to do from your App Delegate.

First add a new variable to your AppDelegate.swift file:

   var allowableOrientations: UIInterfaceOrientationMask = .All

Next, implement the application:supportedInterfaceOrientationsForWindow function in the same file:

func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask {
    return allowableOrientations;
}

This will be called whenever you rotate the device to determine if that orientation is allowed. It doesn’t, however, force a rotation. We can implement another function in the same file to handle setting the allowableOrientations file and making sure the current orientation is one of those:

    func setAllowableOrientations(orientationMask:UIInterfaceOrientationMask,
                               defaultOrientation:UIInterfaceOrientation) {
        self.allowableOrientations=orientationMask;
        if ((orientationMask.rawValue & UInt(UIDevice.currentDevice().orientation.rawValue)) == 0) {
            UIDevice.currentDevice().setValue(defaultOrientation.rawValue, forKey: "orientation")
        }
    }

To use it, simply call this function from your View Controller’s viewDidLoad function, similar to this:

    (UIApplication.sharedApplication().delegate as! AppDelegate).setAllowableOrientations(.Landscape,defaultOrientation: .LandscapeRight)

That’s it!

ERROR iTMS-90022: Missing required icon file. (120×120)

If you get a iTMS-90022 error message and you have what looks like the proper 120×120 icon file in your Images.xcassets, you can try to get this working the old fashioned way by adding the appropriate .png files to your project and updating your Info*.plist file. The entries for the plist file for iPhone targets should be:

<key>CFBundleIconFiles</key>
<array>
    <string>Icon-Small</string>
    <string>Icon-Small-40</string>
    <string>Icon-Small-50</string>
    <string>Icon</string>
    <string>Icon-60</string>
    <string>Icon-72</string>
</array>

Make sure your png files (along with the @2, @3 versions) match this naming convention as well. For me, it was the ‘Spotlight’ files that were misnamed to ‘Icon-40.png’, etc instead of ‘Icon-Small-40.png’.

The entries for the plist file for iPad targets should be:

<key>CFBundleIconFiles~ipad</key>
<array>
    <string>Icon-Small</string>
    <string>Icon-Small-40</string>
    <string>Icon-Small-50</string>
    <string>Icon-72</string>
    <string>Icon-76</string>
</array>

If you have a universal app, add both to your plist file, and make sure the matching png files are all in your project as well.

More info here if you are a developer.

Saving UIImage to the photo album.

The simplest way to save a UIImage to the photo album is with the UIImageWriteToSavedPhotosAlbum() function. The simplest way is by specifying the UIImage, and none of the other parameters:

    UIImageWriteToSavedPhotosAlbum(image, nil,nil, nil);

However, this will save the image as a jpg, which is not always ideal. If you want to save it as PNG, you have to first encode the image as a PNG, then make a new image based on that PNG so that the PNG data is attached to it, and then save the new image:

    NSData *data=UIImagePNGRepresentation(image);
    image=[UIImage imageWithData:data];
    UIImageWriteToSavedPhotosAlbum(image, nil,nil, nil);

You can also force JPEG format (with your own compression quality) by doing the same thing, but with the UIImageJPEGRepresentation() call:

    NSData *data=UIImageJPEGRepresentation(image,1.0); // best quality
    image=[UIImage imageWithData:data];
    UIImageWriteToSavedPhotosAlbum(image, nil,nil, nil);

Making your iOS OpenGLES view transparent

At some point you may want to make your OpenGL ES view transparent, so you can display something else behind it. (Maybe a camera view?) For this article, I’m going to assume you are using a Game project template from XCode 6.

In your GameViewController.m’s viewDidLoad function, just before the ‘[self setupGL];’ call, add the following lines:

    view.backgroundColor=[UIColor clearColor];
    
    CAEAGLLayer *eaglLayer=(CAEAGLLayer*)self.view.layer;
    eaglLayer.opaque=NO;

Also, in the glkView:drawInRect function, change the glClearColor line to be all zeroes:

    glClearColor(0.00f, 0.00f, 0.00f, 0.0f);

That should be it.

Fixing the status bar visible after photo roll showing bug on iOS7/iPad

In iOS7 disabling the status bar is a bit more complex than it was in iOS6. (We have to add a row call it “View controller-based status bar appearance” and set it to boolean NO in the Target Info properties tab.)

Unfortunately, there’s also a bug on the iPad (up to 7.1 as of this writing) that makes the status bar visible again after the photo roll is shown. Luckily, there’s a pretty easy fix.

First, make sure your UIViewController subclass (the one that is already a UIImagePickerControllerDelegate) is a UINavigationControllerDelegate as well. (It should be anyway to get rid of the warnings in XCode.) Then add the following UINavigationControllerDelegate function to that subclass:

- (void)navigationController:(UINavigationController *)navigationController
      willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
    [[UIApplication sharedApplication] setStatusBarHidden:YES];
}

That’s it!

Debugging no-touch zones

When converting some iOS apps from iOS6 to iOS7, I came across a very strange bug. I had an app with tons of views and view controllers, and on the right side of the screen, touches wouldn’t be recognized. (For buttons, gestures, scrolling, etc.) It was quite obvious that one of the views frame’s wasn’t covering that part of the screen. The easy way to figure out which view it is is to add:

    self.view.clipsToBounds=true;

inside each viewDidLoad, verify that the dead zone is no longer visible, and then slowly remove the clipsToBounds calls one at a time until the dead zone is visible again, and you’ve found the offending view! Then take whatever steps you need to adjust that view properly.

In my case, a view had a frame (0,0+768,1024) for a landscape view. Changing this to (0,0+1024,768) resolved the issue. Oddly enough, either sized frame didn’t cause iOS6 to have issues, just iOS7.

Hiding the navigation bar shadows in iOS7

I had a doozy of a time trying to hide the tiny little one pixel high grey shadows in the navigation and tab bars. Google had a lot of suggestions, but none of them worked until I came up with a combination of a few that did the trick. In your application:didFinishLaunchingWithOptions: functions, add the following:

   [[UINavigationBar appearance] setShadowImage:[[UIImage alloc] init]];
   [[UINavigationBar appearance] setBackgroundImage:[[UIImage alloc] init] forBarMetrics:UIBarMetricsDefault];

This removes the shadow for all navigation bars and tab bars, so if you want a different behavior this probably won’t work.

Also make sure your navigation bar and tab bar don’t have translucency set. (There’s a checkbox in Interface Builder.)

Keeping your mobile screen active (iOS and Android)

There are times when you need to force your app to stay active. (For example, a routing app or something where its supposed to be hands free. You don’t want your user to have to keep tapping it to keep it from sleeping.) Its easy to do this in both Android and iOS.

For Android, insert this little bit of code in each Activity’s onResume() function that you want to prevent from sleeping:

	@Override
	public void onResume() {
		super.onResume();
		// stop screen from sleeping
		getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
	}

Android will re-enable sleep timeouts whenever you exit the app, so you don’t have to worry about turning it back on.

For iOS, its almost as simple, except you only have to do it once for your entire app. You enable it when your application becomes active, and disable it when it enters the background. Insert this bit of code in your app delegate:

- (void)applicationDidBecomeActive:(UIApplication *)application {
    // stop screen from sleeping
    [[UIApplication sharedApplication] setIdleTimerDisabled:YES];
}

- (void)applicationDidEnterBackground:(UIApplication *)application {
    // let screen sleep
    [[UIApplication sharedApplication] setIdleTimerDisabled:NO];
}

That’s it!