Pages

Saturday, July 18, 2009

iPhone OS - drawing image and stupid thing from Apple

Just because of a piece of code, a team in Singapore gave up an iPhone project. Actually, they do not know how to play tricks with Apple. ... and I also faced to a similar situation when I play as a technical consultant to fix this stupid bug.

The need of client is: drawing some effects on an image and save it as a photo in Photo library. So, if anyone has experience with graphic programming may think this is a very simple task. However, that's not true.

Many forums and online articles just shows us a real problem on iPhone graphics programming: the root position (0,0) of the image to draw is on the bottom left and the root position of the graphic context is on the top-left corner. So, we must do some transformation to keep the image in the correct position.

And the simple code to fix this issue is as below:
int width = image.size.width;
int height = image.size.height;
CGSize size = CGSizeMake(width, height);
//create the rect zone that we draw from the image
CGRect imageRect = CGRectMake(0, 0, width, height);
UIGraphicsBeginImageContext(size);
CGContextRef context = UIGraphicsGetCurrentContext();
//Save current status of graphics context
CGContextSaveGState(context);
//Do stupid stuff to draw the image correctly
CGContextTranslateCTM(context, 0, height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextDrawImage(context, imageRect, image.CGImage);
//After drawing the image, roll back all transformation by restoring the 
//old context
CGContextRestoreGState(context);
DO OTHER EFFECTS HERE
//get the image from the graphic context
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
//commit all drawing effects
UIGraphicsEndImageContext();
Above piece of code is thing that you see in all online technical articles related to iPhone graphic programming. Anyway, that's not enough. If you deploy that piece of code into iPhone OS 3.0 on real device, it does not run correctly: saved images are always scaled in horizontal and inverse. However, there is a very strange thing: this code is run perfectly on simulator for iPhone OS 3.0.

After 1 day to detect the problem, I found that: when an image is shown on iPhone, it has a direction. Direction is included: UP, DOWN, LEFT, RIGHT. So, we must to fix the code to satisfy when they're in those cases. The above code is run correctly in case image direction is UP.

Here is the fixed version:
int width = image.size.width;
int height = image.size.height;
CGSize size = CGSizeMake(width, height);
//create the rect zone that we draw from the image
CGRect imageRect;

if(image.imageOrientation==UIImageOrientationUp 
|| image.imageOrientation==UIImageOrientationDown) 
{
    imageRect = CGRectMake(0, 0, width, height); 
}
else 
{
    imageRect = CGRectMake(0, 0, height, width); 
}

UIGraphicsBeginImageContext(size);
CGContextRef context = UIGraphicsGetCurrentContext();
//Save current status of graphics context
CGContextSaveGState(context);

//Do stupid stuff to draw the image correctly
CGContextTranslateCTM(context, 0, height);
CGContextScaleCTM(context, 1.0, -1.0);

if(image.imageOrientation==UIImageOrientationLeft) 
{
    CGContextRotateCTM(context, M_PI / 2);
    CGContextTranslateCTM(context, 0, -width);
}
else if(image.imageOrientation==UIImageOrientationRight) 
{
    CGContextRotateCTM(context, - M_PI / 2);
    CGContextTranslateCTM(context, -height, 0);
} 
else if(image.imageOrientation==UIImageOrientationUp) 
{

//DO NOTHING

}
else if(image.imageOrientation==UIImageOrientationDown) 
{
    CGContextTranslateCTM(context, width, height);
    CGContextRotateCTM(context, M_PI);
}

CGContextDrawImage(context, imageRect, image.CGImage);
//After drawing the image, roll back all transformation by restoring the 
//old context
CGContextRestoreGState(context);
DO OTHER EFFECTS HERE
//get the image from the graphic context
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
//commit all drawing effects
UIGraphicsEndImageContext();
After fixing this bug, I can say only one thing: why Apple always do something stupid and make developer do the stupid thing?

Thursday, July 9, 2009

Build and deploy iPhone application with offical and unofficial ways

I always call myself a 2 faces man because I accept both 2 things at the same time for everything: black and white, the good and the bad, angel and evil. In my point of view: nothing is only quite 100% good or bad and we must accept that truth in the real life.
As the title of this article, I will share you 1 trick and 1 official way to build your iPhone application. Trick is the bad, and the official way is good (if you accept to pay 99$/month for iPhone developer account).

Section 1: the good - how to build your iPhone application on XCode as official way of Apple

Below is the process to build and distribute your application on the real iPhone
If you have the iPhone developer account, please go to the iPhone developer program portal and access to below link:
http://developer.apple.com/iphone/manage/distribution/index.action

It has very detail guideline to help you in step by step. Just follow them.

Section 2 (the bad) build your iPhone application on XCode without IDP membership

This trick is only used in case you don't want to pay money (99$/month) for the developer account. However, I must warn you about some limitations as below:
1. You can not upload your iPhone application into AppStore. (sure. because it is an unofficial way)
2. You can only deploy and run your build app in iPhone OS <= 2.2. Currently, I can not run it in iPhone OS 3.0.
3. The iPhone SDK you can use for this trick is only from: 2.0 - 2.2

And ... now, below is the link that guide you about the trick:
http://www.vinodlive.com/2009/01/16/how-to-deploy-an-iphone-application-without-an-idp-membership/

Actually, the above trick does below things:


1. Create a Signing Identify on your Mac by following this simple guide from Apple.
In this step, we create a certificate (actually, it is not approved by Apple) in our computer and use it to sign for our code.
2. TextEdit the /Developer/Platforms/iPhoneOS.platform/Info.plist file with some special keys and values to skip checking mapping between the certificate and the provisioning profile.

3. Change the Project Settings . In the Properties List, find the Code-Signing-Identity and Select the Certificate Name(iPhone Developer) you created earlier. This step helps you sign your code with the created certificate.
4.  Add the a snippet on to your projects info.plist using TextEdit to cheat the compiler


If you succeed with above article, you're a lucky man. If you're failed, just do a small step in addition:
Go to Project/Edit project settings - check true for Generate profiling code.

I succeed with both ways.  So I write this article to share with you my experience. If you have any other "bad" or "good" ways, please share me.

Monday, July 6, 2009

Deploy app on iPhone - no wifi - no appstore with full explanation

I received an iPhone (OS 3.0) 2 days ago. My new task is: learning how to deploy our iPhone applications on this device. There is no wifi at my home. Our application is also not uploaded into Apple App Store. So, I need a way that:
1. Only need to copy our application into iPhone and run.
2. No need wifi to copy and setup

Is that difficult?

I read tons of articles and search on many forums but when I follow their steps (no understanding) I got different failed results.

Finally, I find a way after analyzing from many sources. Below is the list of step that I will guide you with my explanation. Anyway, I write this article just for iPhone developers (not for end user). However, in case you are an end user, you can also find some helpful information to solve your problem here.

The solution is:
Connect to your iPhone by using SSH with root account. With the console on SSH, you have the full access right to run any commands (supported by the kernel system behind) to copy/set access right/install/remove your software. However, if you have no wifi - you need a software to bind your an available port on your computer with port 22 (of SSH) on iPhone. The soft to do that is called iTunnel.
iTunnel is worked based on iTune. You should choose to install latest version of iTune to make sure this works ok.
Below is the list of step you need to follow:

Step 0: install latest version of iTune in your computer


This is very important. Without iTune, iTunnel can be run on command line ok. However, when you use SSH to connect to the available port - it will throw error.
In the past, I think this trick does not need installing iTune anymore but I found a problem when I help my team member to do this. At last, we found that the difference between our computer is the iTune version. You must download the latest version of iTune and install it on your computer to make sure this trick works.

Step 1: Install OpenSSH on your iPhone

You need wifi only in this step. To do this, you just need to connect to wifi - go to Cydia - search OpenSSH and install this package. The final result of this step is: OpenSSH will be run as a service in background and open the port 22 to connect. Anyway, you need to check whether the service is turned on or off. To do this, you need to install another software to check. I recommend you should search BossPref (also using Cydia to install).

Step 2: Download and run iTunnel

You can search on Internet to download this software into your PC. I recommend a link that you can download as below:
http://www.makkiaweb.net/blog/2008/09/iphone-tunnel-suite-27

After downloading this, you only need to unzip into a folder. Open the console and go to this directory. Type into the console:

iTunnel.exe 22 [any available port on your computer]
The final result of this step is: iTunnel binds port 22 on your iPhone with a port on your computer.

Step 3: Use any SSH client to connect to the port (on step 2) with root account.


I recommend that you should use WinSCP. The password of root account is: alpine

Example: if I use the port on my computer to bind is: 9000. I will login to SSH on my computer with below information:
HOST: 127.0.0.1 (localhost)
Account: root
Pass: alpine
Port: 9000

After this step, you can see a window (if you use WinSCP) with 2 panels. Left is for your computer, right is folder structure on iPhone. You can copy your app and set execute rights on this. You can also go to menu, open terminal to run any commands you like with root account on iPhone.

Good luck! If you have any problem, leave comments here and I will help.

Update for common questions:
Question 1: I meet "bind error" on the command line when I run iTunnel.
Answer: The port you want to bind was used by another program. So, please try with another port. See step 2 with the command line

iTunnel.exe 22 [any available port on your computer]
After you run the iTunnel successfully, use above port to connect with WinSCP.