CoreData with NSOrderedSet does not work well on insertion

Usually, we generate an entity class with Editor->Create NSManagedOBject Subclass... For example we created a subclass of NSManagedObject

Xcode generates the .h file like this:

@interface MyEntity : NSManagedObject

@property (nonatomic, retain) NSOrderedSet manySubs;

//... some methods generated by Xcode
- (void)addManySubsObject:(MySubEntity*)value;
//... some other methods generated by Xcode

@end

and the .m file like this:

@implement MyEntity

@dynamic manySubs;

@end

If you use MyEntity just like this, you will get an error when you try to insert a new MySubEntity class by calling [myEntityObj addManySubsObject:aSubEntityObj];.

The error reads:

*** -[NSSet intersectsSet:]: set argument is not an NSSet

This is a bug Apple hasn’t resolved yet, but you can work around this bug by adding the following method to .m file.

- (void)addManySubsObject:(MySubEntity*)value{
    [self willChangeValueForKey:@"manySubs"];
    NSMutableOrderedSet *tempSet = [NSMutableOrderedSet orderedSetWithOrderedSet:self.manySubs];
    [tempSet addObject: value];
    self.manySubs = tempSet;
    [self didChangeValueForKey:@"manySubs"];
}

Be careful

Transparent view vs. hidden view in iOS

Rise the question

Here comes the question,

how to create a transparent view without any contents?

Wow, the answer looks so easy to get! Almost every iOS developer has created one or even more ‘transparent view’s. Let’s take a look at those solutions. We have 2 solutions.

Solutions #1

Create a view with alpha=0.f. take a look at the code below

UIView *transparentView1 = [[UIView alloc] initWithFrame:CGRectMake(0.f, 0.f, 320.f, 460.f)];
transparentView1.alpha = 0.f;
[self.view addSubview:transparentView1];
[transparentView1 release];

It’s simple and straight forward. Hum? Let’s take a look at another solution.

Solution #2

Create a view with backgroundColor=[UIColor clearColor].

UIView *transparentView2 = [[UIView alloc] initWithFrame:CGRectMake(0.f, 0.f, 320.f, 460.f)];
transparentView2.backgroundColor = [UIColor clearColor];
[self.view addSubview:transparentView2];
[transparentView2 release];

This is also very simple.

So, what’s the difference between these 2 solutions?

The difference on surface

Before we talking about the difference, we need to review some basic yet key properties provided by iOS

  1. hidden: set this property to YES will cause the view invisible. default is NO;
  2. alpha: this property affect all the contents of the view, including drawing and subviews. animatable;
  3. backgroundColor: this only affect the background color, not affect the drawing and subviews of the view. animatable;
  4. opaque: set this property to YES, will gain some performance boost on UI rendering. But need to full fill the rectangle of the view. If opaque is set to YES and the view has full or partial transparent content, the behavior is not define.

Visit apple’s document for UIView for detail information.

With these basic knowledge, we can easily tell the difference between these 2 solutions.

  • Solution #1 uses alpha property to make the view transparent, in another word, it’s invisible because the view is hidden.
  • Solution #2 sets backgroundColor to transparent color to make it invisible.
  • Solution #2 only set the background to transparent, if you draw anything on the view, or add some subviews to the view, they are still visible, and solution #1 will make everything in the view invisible, including the custom drawings and all the subviews.
  • If the view is a clean view without any custom drawings and subviews, the result of these 2 solutions look the same.

They look the same? But actually not the same? Yes, they just look the same, but not exactly the same, so what’s the real difference?

Look the same, but…

There is always a BUT.

These 2 solutions look the same, but act differently on receiving touch event.

  • Solution #1: alpha=0.f;, according to Apple’s document:
    > To hide a view visually, you can either set its hidden property to YES or change its alpha property to 0.0. A hidden view does not receive touch events from the system. However, hidden views do participate in autoresizing and other layout operations associated with the view hierarchy. Thus, hiding a view is often a convenient alternative to removing views from your view hierarchy, especially if you plan to show the views again at some point soon.
    >
    > See Apple’s document
  • Solution #2: when background color is set to clear color, the alpha is still 1.0, so this view still can receive touch event.

Finally, we have the answer

Before we get the correct answer, we need to use the correct terminologies to clarify the answer:

  • Hidden View: when hidden=YES or alpha=0.f
  • Transparent background View: when backgroundColor is set to clear color

The answer

  • a hidden view will not accept touch event, while a transparent background view will.
  • a hidden view may look like a transparent background view if add no subviews and draw nothing on the view.

With this answer, I think we can use hidden view and transparent background view correctly.

Best Practice on Derive an iPhone project from existed project

After I finished an iPhone project, I decided to create a similar project based on that one. So, I copied the previous project directory to an new directory and build it. Everything goes fine, the new project was correctly signed and installed on the machine, the icon and Default.png was correctly displayed.  But, only one thing was out of my expectation.

I installed the debug version of the previous project on the simulator and want to compare the look and feel, but after I install the new project, which was derived from the old project, on the iPhone simulator, shit happened! The old project was disappeared! Only the new project was there. I checked the project setting over and over again and got no clue completely. This drove me mad! Was it because I did not rename the project? I simply copied to a new directly, without changing the project name. Nope, it wasn’t. Was it because I set the wrong Product Name? Nope, it was not. What made the iPhone thinks this 2 apps were the same?

After I googled and tried a lot, just before I want to give up, I suddenly saw a key in info.plist file. the key is “Bundle Identifier“. Oh My GOD! It should be the reason! My instinct told me! I added some suffix to the identifier to make it unique, then try again on the simulator, it WORKED! It’s the reason!

Yes, iPhone Programming Guide told us every project need an unique identifier, thus Apple recommend us use the Java Package Style to identifier each app. But if you are copying project from here to there, you might miss this because without modify it, the app also worked fine. Dude….

I hope this would help you if you are facing similar problem as me.

The Best practice to protect your iPhone application from pirates

iPhone app developers are facing their hard worked app appears on the pirate app web site the day after it appears on iTunes. So you must want to protect your app as I do, right? But how? What’s the best way to protect your apps?

Here is the best practice by far I know. And it’s really simple and easy to do with. As far as Doskoi know, every hacked iPhone app need to add a key to the info.plist file. They need to add this:

SignerIdentity
Apple iPhone OS Application Signing

So it would be easy for us to detect whether your app was pirated or not. Simply using the following code to protect your apps.

 NSBundle *bundle = [NSBundle mainBundle];
 NSDictionary *info = [bundle infoDictionary];
 if ([info objectForKey: @"SignerIdentity"] != nil)
 {
 // Add your anti-pirate code here.
 }

Hope it will help you.

Doskoi’s Lounge » iPhone软件的破解保护.

Damn it, change the project name cost me a whole day!

Yes, I knew, I am SO stupid, I should Google it before I started to change the XCode project name. It cost me a whole day! What a joke.

All right, if you are looking for the correct way to change your application’s name show on the iPhone main screen. You need follow my steps.

  1. Scroll the left “Groups & Files” panel to the top(Where you put it, I putted it in the left.), and double-click your project name. This will lead us to step 2.
  2. In the Build tab, find the configuration dropdown list, select “All configurations”(Usually, on the top), and then type “Product Name”(It’s not case sensitive, remember the space) in the search box next to the dropdown list.
  3. Type whatever name you like in the right side field, and then close it and go to step 4.
  4. Choose Build->Clean all targets, then rebuild your project, It’s done!

Remember! Do NOT change your target or add your own target! That’s the wrong way, and will cause code sign failure, and it’s hard to repair!

God, Damn you Apple, you should post a tutorial for the developer. Now I did it for you, and you should pay me!

“AAC” is “evil”

Problem

Few days ago, I wrote a post on which is the best audio format we should choose when developing an iPhone app. The conclusion is using IMA4, it’s the best choice if you want to record audio on the device.

Today, I was trying to use the System Sound service to play a short audio and in order to save the spaces, I chose the “AAC” format to encode my sound effects. I just thought it’s would be capable for a system sound and didn’t searched the documentation for which format is suit for System Sound. Everything went fine in the simulator. But, when I load the app into the device, and, BOOM~~, it failed to play the sound effect! My first reaction was somewhere programmatic mistake in my code, and after a careful review, I thought the reason could  be the format. So I changed the format back to IMA4, and this time, it went well!

Apparently, System sound did not support AAC format, and I could not find the appropriate document where described it. Apple just pushes us to use IMA4 and other linear PCM format to reduce the CPU and power consume. Yeah, I understand that, but please, place such kind of important information some where we can find it easily! The API documentation could be a good place.

If I am the director of the documentation team, I would promote such information and place it in a notable place! Damn it.

Why I’m angry?

Because if you using AAC for recording or System Sound, it works fine on the simulator, but will be failed on the device! If the device does not support AAC for recording and System Sound, please disable it on the simulator!

Conclusion

The conclusion is if you want to playback a song or something like this, you can choose AAC as your primary format for it is well supported by hardware codec, but only for this purpose please. If you want to record audio or play system sound effect, please choose IMA4, it’s is the second best format for iPhone.

“AAC” is a good compressed format, but it’s also has its cost.

How to enable autorotation when combining UITabBarController and UINavigationController and your own views

My app is still not online now, because I have to fix some autorotation problems. Before today, I really didn’t know how to enable autorotation when my app is combined with UITabBarController and UINavigationController and my own views. After doing a little search, The solution is I need to enable autorotation for every view controller that will be presented in the UITabBarController.

Let’s assume we have a UITabBarController, theTabBarController and 3 UINavigationController named A, B, C. We need to set all the three controllers(A, B, C) to return YES in this method:

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    // Return YES for supported orientations
    return YES;
}

OK, now your tab bar and navigation bar and your views will be automatically rotated.

June 8th, is the iPhone Day!

Yesterday, technically, today, June 8th, is the iPhone Day! WWDC was opened about 14H ago, and almost all the main IT news channel are full of iPhone, We really need to celebrate it, 8th June, the iPhone Day.

OK, Let me tell you what’s my interest in iPhone Day.

  • The Rumor was real!
  • The new iPhone release date, June 19, was a little bit earlier than I expected.iPhone3GS, Release Date
  • Will Apple unlock the CPU limitation of current iPhone 3G? Still unknown.
  • In App Purchase is really, really what I need. But I could not catch the iPhone 3GS release time.Picture 5
  • I must work much more hard to get my app released.

Damn it, I like iPhone!

Don’t use [AVAudioPlayer pause]

AVAudioPlayer is providing a very convenient way to play iPhone supported audio file. Especially, if you want to implement fast forward and rewind, AVAudioPlayer is the best choice.

But I just found an odd problem. If I use [AVAudioPlayer pause] method, next time if I invoke [AVAudioPlayer play], it had no responde. But after I changed pause to stop, it worked fine. The problem looks like it somewhere inside AVAudioPlayer to control the AudioQueue Object to resume playing. Whatever, by using [AVAudioPlayer stop], you can archive the same goal as [AVAudioPlayer pause] do, I mean FFW and RWD.

BTW, the document said, pause will return YES/NO, but clearly, it is a no return value instance method. The guy who is responsible for this method might copied the same comment from play!

Picture 1

What a joke. This line was there since iPhone 2.2, and is still there in 3.0b5. Didn’t there anybody reported this problem? Should I fire a bug?

AVAudioPlayer could not play after recording via AudioQueue Object.

Problem

I’ve been fighting for AVAudioPlayer and AudioQueue object for 2 days,

Here is my Dev environment:
iPhone: 3.0Beta 5
OSX 10.5.7

Here is my scenario:
1. I need to use AVAudioPlayer to play a CAF with IMA4 data format. Including fast forward and rewind.
2. I need to use AudioQueue Object to record audio, because 3.0 has not go public yet.
3. A common scenario is record an audio A, then play the original audio A’(Not the one recorded) or vice versa.

The problem is I could NOT play the A’ file  after I record file A.
[theAVAudioPlayer play] always return NO, but [theAVAudioPlayer prepareToPlay] return YES.
This situation only happened after I used the AudioQueue Object for recording. If I did not record, it always worked fine.

About the clean up.
1. I released the theAVAudioPlayer object and set to nil each time after I finished playing.
2. I closed the audio file and deposed the AudioQueue Object each time after I finished recording.

Solution

Thanks to Vitali Molodtsov[Open the original post Needs login], the solution is reset the AudioSessionCategory to kAudioSessionCategory_MediaPlayback just after finish recording. Then every thing goes fine.

Conclusion

I think this is a bug for AVAudioPlayer. Each time, when we initialize an AVAudioPlayer object, it should set the correct AudioSessionCategory for us silently, but it doesn’t. So I’ve fired a bug for Apple.