Mar 30

Recently there have been some interesting developer news related to working with images on the iPhone.

  • First there is Chris Greening’s open source project simple-iphone-image-processing, that provides a set of common image processing tasks .
  • Today I listened to the Mobile Orchard’s podcast Interview with Paul Cantrell, and the discussion was about UIKit, views, layers, etc. This was the most enlightening information I’ve come across on this topic ever. Highly recommended.

So, I thought I’d contribute a few UIImage routines that I’ve found useful.

Combine two UIImages

To add two UIImages together you need to make use of Graphics Context.

- (UIImage *)addImage:(UIImage *)image1 toImage:(UIImage *)image2 {
	UIGraphicsBeginImageContext(image1.size);

	// Draw image1
	[image1 drawInRect:CGRectMake(0, 0, image1.size.width, image1.size.height)];

	// Draw image2
	[image2 drawInRect:CGRectMake(0, 0, image2.size.width, image2.size.height)];

	UIImage *resultingImage = UIGraphicsGetImageFromCurrentImageContext();

	UIGraphicsEndImageContext();

	return resultingImage;
}

Create a UIImage from a part of another UIImage

This requires a round-trip to Core Graphics land:

- (UIImage *)imageFromImage:(UIImage *)image inRect:(CGRect)rect {
	CGImageRef sourceImageRef = [image CGImage];
	CGImageRef newImageRef = CGImageCreateWithImageInRect(sourceImageRef, rect);
	UIImage *newImage = [UIImage imageWithCGImage:newImageRef];
	CGImageRelease(newImageRef);
	return newImage;
}

Save UIImage to Photo Album

This is just a one-liner:

UIImageWriteToSavedPhotosAlbum(image, self, @selector(imageSavedToPhotosAlbum: didFinishSavingWithError: contextInfo:), context);

And to know if the save was successful:

- (void)imageSavedToPhotosAlbum:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo {
	NSString *message;
	NSString *title;
	if (!error) {
		title = NSLocalizedString(@"SaveSuccessTitle", @"");
		message = NSLocalizedString(@"SaveSuccessMessage", @"");
	} else {
		title = NSLocalizedString(@"SaveFailedTitle", @"");
		message = [error description];
	}
	UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title
													message:message
												   delegate:nil
										  cancelButtonTitle:NSLocalizedString(@"ButtonOK", @"")
										  otherButtonTitles:nil];
	[alert show];
	[alert release];
}

written by Nick \\ tags: , , , , ,

Mar 17

The other smartphone vendors who have been working like crazy to catch up with Apple ever since the original introduction of the iPhone, were set back another year today with Apple’s preview of iPhone OS 3.0.

Some of the other touch-like phones currently available are actually not bad phones in themselves. But any smartphone today is compared with the iPhone, and that’s where they fall down. Palm who has recently gained some attention and interest for their Pre, will now face off with the iPhone 3.0 at their launch in June. You have probably already forgotten the shortcomings of the first iPhone software release. I can’t think of any smartphone vendor who has hit it out of the park with their 1.0 release. Now Apple has had two years and the help of 30 million customers to refine their software. “Not one of those people will still be using an iPhone a month later.” Right.

Other blogs have covered the 100 new features of 3.0, so I will not rehash that information. And the 1,000 new API:s are covered by the NDA, so I can’t discuss them either. But I would like to emphasize the new business models. These changes are HUGE.

The initial App Store model only allowed single apps to be purchased for a one-time fee. This led to the proliferation of books as apps, and apps where it would be natural to purchase additional content but that was verboten. (We actually implemented mobile content purchases for iWallpaper, but Apple rejected it.) When asked at the time, Apple plainly stated that they did not have a solution for other business models.

Now they do. In spades.

  • The ability to purchase content separately from the app. Suddenly ebook readers on the iPhone make a whole lot more sense.
  • Subscription billing. Get continued access to a service (games, enterprise services), or receive new content on a regular basis (magazines, videos).
  • Make purchases from within applications. This feature alone probably added another $1B to the App Store business for Apple.

You have already seen a lot of content made available in the form of iPhone apps for the sole reason that you can easily charge money for it; unlike the web in general where everything is expected to be free. With these changes Apple will become the largest seller of digital products of all types. If you can digitize it, it will soon be available for sale in the App Store. (And why limit the store to iPhone clients? Sell information products for any computer. Notice that it’s not called the iPhone Store…)

Competitors have been scrambling to launch App Store clones, with one of the larger alternatives having just barely put payments in place. (Although, handling micropayments from mobile devices in 77 countries should not be belittled.) Now Apple lapped them again.

I’ll let other people continue to whine that “there really wasn’t that much unexpected” and that this was “Really pretty minor stuff“. You’ll have to excuse me, I have a dozen new apps to write that just became possible. And a few new business models to explore. This is going to be just as much fun as last year when the SDK was brand new!

written by Nick

Mar 06

A simple method to debug your iPhone application code is to use NSLog() statements at strategic points. I often find myself printing out the response from server calls using NSLog. Even after the iPhone client development is done, these log statements can be useful to debug server issues. But you obviously don’t want to fill the console log in your released code.

To skip lines of code you don’t want to include in the release build you can use a preprocessor instruction:

#ifdef DEBUG
NSLog(@"Server Response: %@", response);
#endif

Unfortunately Xcode does not automagically define DEBUG when you do a debug build. So you have to add a definition to your project target.

1. Be sure you select Debug in the Configuration drop down.

2. Select Settings Defined at This Level in the second drop down to reduce the number of fields shown.

3. Now click the tool button in the bottom left corner and select Add User-Defined Setting from the menu.

4. Enter the following name value pair:
OTHER_CFLAGS    -DDEBUG=1

Now when you build and run in debug configuration you will see the log messages, but those lines of code will not be included in your release builds.

Update: Use GCC_PREPROCESSOR_DEFINITIONS instead of OTHER_CFLAGS. See comments below.

Update 2: See this post for a vastly improved method: The Evolution of a Replacement for NSLog

Update 3: See this post if you’re working with an SDK 3.0 project: How To Add DEBUG Flag In A 3.0 Project In Xcode

written by Nick \\ tags:

Mar 03

Happy Square Root Day!

Playing a video (of a supported file format) on the iPhone is very easy using the MPMoviePlayerController class. You just create an instance of the class and initialize it with the URL of the video. The controller plays the video in full screen mode and returns back to your application when it’s done.

However, if the URL of the video is recognized by the iPhone as a YouTube URL then the Apple URL Scheme mechanism kicks in and launches the YouTube app. In this scenario control will not return to your app after the video has played. (The behavior is equivalent of calling [UIApplication openURL:] with the video URL.)

One workaround is to use a UIWebView and load it with the video URL. The drawback with this approach is that the user will see the rather ugly YouTube mobile web site and has to find and tap on the link of the video to play it.

YouTube Mobile

 

Another, visually more appealing, option is to create a small UIWebView on your screen and load it with the YouTube embed code. The result is a small button like image that shows the YouTube play button above a screen image from the video. When the user taps the image, the iPhone video player opens in full screen mode as usual, and when the video is done control is returned back to this screen.

YouTube Video Embedded in iPhone App

 

Here’s the code:

- (void)embedYouTube:(NSString*)url frame:(CGRect)frame {
 NSString* embedHTML = @"\
    <html><head>\
 <style type=\"text/css\">\
 body {\
 background-color: transparent;\
 color: white;\
 }\
 </style>\
 </head><body style=\"margin:0\">\
    <embed id=\"yt\" src=\"%@\" type=\"application/x-shockwave-flash\" \
 width=\"%0.0f\" height=\"%0.0f\"></embed>\
    </body></html>";
 NSString* html = [NSString stringWithFormat:embedHTML, url, frame.size.width, frame.size.height];
 if(videoView == nil) {
 	videoView = [[UIWebView alloc] initWithFrame:frame];
 	[self.view addSubview:videoView];
 }
 [videoView loadHTMLString:html baseURL:nil];
}

Tip of the hat to joehewitt.

Update: A complete working Xcode project has been posted here.

written by Nick \\ tags:

Feb 19

The iPhone media player is very easy to use, but it only works in landscape mode and it seems designed more for video than audio.  If you wish to play an onboard MP3 or an MP3 file as it’s downloading (not really streaming, but progressive download) you have another option!  Use the audio player embedded with Safari running in a UIWebView.

Here’s how to do it without having to create a special window for it.  Instantiate an UIWebView object using a 1×1 pixel sized frame (here self.playerView is an instance variable on my controller and is NOT added as a subview)

UIWebView *webView = [[UIWebView alloc] initWithFrame: CGRectMake(0.0, 0.0, 1.0, 1.0)];
webView.delegate = self;
self.playerView = webView;
[webView release];

Then load your NSURLRequest.

NSURLRequest *request = [[NSURLRequest alloc] initWithURL: [NSURL URLWithString: myMP3URL] cachePolicy: NSURLRequestUseProtocolCachePolicy timeoutInterval: myTimeoutValue];
[self.playerView loadRequest: request];
[request release];

That’s it!  When the request starts downloading the player with full controls will launch and your screen will look the the picture below.

Audio player launched from a UIWebView

Check out the web view delegate methods to learn more about controlling the behavior of the web view to suite your specific needs.

written by Jess \\ tags: ,

Feb 12

In a previous post I listed a line of code that looked like this:

NSString *credentials = [NSString stringWithFormat:@”%c%s%@%c%c%s%@”, ‘u’, “ser:”, @”pas”, ’s’, ‘w’, “ord”, @”@”];

Reader Matt asked me to expand on this in his comment. (Hint: Don’t be afraid to suggest topics for this blog.)

The reason for the curious looking line above is to avoid the whole string to be easily visible in the binary application file. Had I just done this:

NSString *credentials = @"user:password";

it is trivial to search the binary file and find this information in clear text in the file. But by breaking up the string into mixed parts of characters, C strings and Objective-C strings, the content is dispersed in the binary file making it more difficult to find. 

Other ways you can obscure strings is to manipulate the string before it’s used. An easy example is to add 1 to each byte in the string so that “HAL” becomes “IBM”. Of course you can make that function as complex as you want.

Keep in mind that security by obscurity is simply hiding information, which is very different from employing encryption using a mathematically proven algorithm. While it may take a long time to find a needle in a haystack, it just requires luck or patience. Whereas cracking a good safe is really difficult. When you think about security for your application you need to decide when a haystack is good enough for the information you’re trying to protect. And when that is the case, feel free to use a variation of the techniques described here.

written by Nick \\ tags:

Feb 10

Apptism just reported that they are now tracking 20,000 apps on the App Store. I thought I’d take inventory of the number of apps that I’ve written for clients that are available on the App Store now. The total tally comes to 164. Granted 132 of those are books powered by the Iceberg book reader. (They are not not public domain books, but current best sellers from big name publishers.)

That gives me about a 0.8% share of the number of apps, which is the same as when the App Store launched. So I’m keeping pace with the growth of the App Store!

After this self-congratulatory post, back to our regular scheduled program…

written by Nick \\ tags:

Feb 06

Using Multiple OpenGL Views And UIKit
In the Mobile Zodiac application for Chicago Tribune I started out using standard views for all animations but that turned out to be too choppy on the device, so I used OpenGL to animate the zodiac, but kept the rest of the views intact. I wish I could have read this article before I embarked on that project…

9 iPhone Memory Management Links and Resources
You can’t learn too much about memory management on the iPhone.

14 Essential Xcode Tips, Tricks and Resources for iPhone Devs
When you spend so many hours per day living in Xcode, every little trick that can save me some time is welcome. This article has lots of them.

iPhone dev: Retrieving user phone numbers
I have to admit that I did not know it was possible to retrieve the phone number of the device. And it turns out to be so simple. Nice detective work Erica!
 
Speed Matters
This is not directly iPhone related, but it brings up an important point that a 0.5 second delay has a great impact on usability. And it’s very easy to end up with 0.5 second delays in your iPhone app if you’re not careful.

written by Nick

Feb 05

I was seeing this error message when testing an app on a device:

Mashable(1870,0x3940f7e0) malloc: *** mmap(size=3802099712) failed (error code=12)
*** error: can't allocate region
*** set a breakpoint in malloc_error_break to debug

Trying to allocate 3.8 GB of memory on an iPhone isn’t likely to succeed, although the simulator is all to happy to comply.

After some debugging I found this code:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    NSInteger numRows;
	
    NSArray *headlines = [[BlogService sharedInstance] headlines];
    if (headlines) {
        numRows = [headlines count];
    }
	
    return numRows;
}

Ouch! Returning a variable that has not been initialized and is therefore pointing to a random location in memory is not going to lead to anything good.

Moral of the story: Always initialize your variables. Test early and often on a device.

written by Nick \\ tags:

Feb 03

To download a small amount of data from a URL it’s very convenient to use:

NSData *downloadData = [NSData dataWithContentsOfURL:url];

To get data from a URL that requires authentication there several classes that specifically deal with this, e.g. NSURLAuthenticationChallengeSender and NSURLCredential, which require that you use NSURLConnection instead. 

NSURLConnection has several other advantages such as asynchronous downloading, but if you just want a one-liner, you can still use NSData dataWithContentsOfURL with basic authentication using the following method.

Normally the URL that you pass to dataWithContentsOfURL looks something like https://www.mysite.com/getmydata

You can add a username and password directly in the URL like this https://username:password@www.mysite.com/getmydata and this type of URL works just fine with NSData dataWithContentsOfURL.

Security Implications

  • When you include the username and password in the URL, they may be stored in the web server’s log file.
  • If you don’t use SSL, the username and password are sent in clear text.
  • Don’t store the URL including the username and password in a property file or plist. These files can easily be viewed by someone looking inside your app bundle.
  • Don’t store the credentials like this: NSString *credentials = @”user:password@”; This string is very easy to find in the executable file. If your security requirements are low then you can apply some mild obfuscation: NSString *credentials = [NSString stringWithFormat:@”%c%s%@%c%c%s%@”, ‘u’, “ser:”, @”pas”, ‘s’, ‘w’, “ord”, @”@”]; If you have real security requirements, use real encryption.

 

written by Nick \\ tags: