May 22, 2009
I currently implement global variables by defining them within the AppDelegate.h file (outside of class definition) and then include this file in each ViewController.m that needs access to these variables.
This works just fine but I’ve noticed that after saving/restoring these variables as persistent data (NSUserDefaults), their values seem to change over time.
My question: is this approach the right way to handle globals without encapsulation? I use a lot of ‘C’ data types — int, char, etc. for my global variables (rather than NSInterger, etc.) so I’m not sure if there’s a better approach.
My short answer: DON’T. Globals are EEEEVILLLLLL. Why not create an engine class for your app that you use as shared instance. Define all your “globals” there.
May 14, 2009
Let’s imagine I have this great MyBookshelf App and now with OS 3.0 coming I am planning to include Storekit so that my users can also purchase new content from within my app. So far, that’s a great idea. Additional features for my customers, additional income through in-app sales.
But maybe I have hundreds of thousands of users who are not immediately willing to upgrade to the new software. The reasons for this are outside the scope of this article but as an active developer you are risking loss of customers if you only concentrate on the new.
Your choices are:
- make a final build for 2.x putting in all the bugfixes you can and then move on. Existing 2.x customers will only receive the latest 2.x version, new customers will have to have a 3.0 iPhone to purchase the new app.
- create a new app from the same codebase adding only the 3.0 features there and then continuing to push updates to both the 2.x version and the 3.0 version.
- create magic code to dynamically load 3.0 frameworks if they are present. This is highly advanced and only for true pros.
For this article I am focussing on option 2. I want one project to hold my source code and have two targets, one for 2.x and one for 3.0. Only the 3.0 version can include the appropriate 3.0-frameworks and I don’t want to see any compiler problems. Also I don’t want to resort to some fancy art of dynamic loading.
May 12, 2009
In the past week I received an email by Apple, like my dear developer colleagues, stating that I have to consider whether the code of my apps is compatible to iPhone SDK 3.0. Gosh, the guys from Cupertino got me scared!
And that they can’t take a joke occurred this night: The update of my app Super Trumps, submitted about 8 days ago, has been rejected this night. The friendly associate of Apple stated that my app crashes when using it with SDK 3.0. That means: They’re testing apps already now on compatibility!
Without delay I checked the compatibility of all my existing apps and – lo and behold – in two applications problems occurred.
May 03, 2009
With Device | Debug and Code Sign “iPhone Developer” I have no problems getting my app onto the device, but with all other configurations I get this strange error message in XCode Organizer.
That’s a weird error that most of us have encountered one time or the other. Here are my hints how to get it fixed.
Apr 26, 2009
3 Months ago, when I started developing MyAppSales I felt confused by multiple XIB files, so I decided to put all view controllers into the MainWindow.xib. Not only does this not really gain you more overview, but this practise slows down application launch. On the iPhone you should defer work whenever possible.
That’s why I revisited my XIB files for version 1.0.1 and moved parts to external files wherever possible. Here are all the steps.
Apr 23, 2009
For a project I am working on I needed to shuffle the contents of an NSArray without harming the items themselves. NSArray is a convenient container because it does not care about what you put inside. This is because you don’t (put objects into arrays), you only pretend.
You cannot add an object itself into an array but instead you always insert pointers to class instances. NSArray and its bigger cousin NSMutableArray will keep track of the pointers and memory management of the items so you don’t have to. This is a custom category for NSArray that is useful for shuffling the contents, regardless of their class type.
Apr 09, 2009
You are listening to user feedback, especially those in Italy. You solve all their problems with a new version, in my case LuckyWheel 1.0.3. You polish it, test it (you think) and submit it to Apple for review. After a week you get this message back:
Your applications, LuckyWheel and LuckWheel Lite, cannot be posted to the App Store at this time because they do not achieve the core functionality described in your marketing materials, or release notes. Applications must adhere to the iPhone Human Interface Guidelines as outlined in iPhone SDK Agreement section 3.3.5.
The release notes for both applications state, “Italian UI and Instructions added”. However, in our review, when we put the device into Italian language mode and launched the applications, the application UI was still in English. Only the instructions were changed to Italian. See attached screenshots.
In order for your applications to be reconsidered for the App Store, please resolve this issue and upload your new binaries to iTunes Connect.
That’s a very long way to say: “Hey buddy, your Italian is English!”
When I got this message I was stumped. I though I had tested it. Thieves! Who has stolen my Italian UI?! But then I remembered something I had found out some months ago.
Mar 29, 2009
When switching to or beginning with Objective C you might be tempted to try to use the old c-style arrays, but that’s better left to the hard core C-enthusiasts. For programming Cocoa Touch we always use the NSArray class because of the additional intelligence it provides for us, not to mention integration with memory management.
The first thing I ever added into NSArray was string objects. And so will probably everybody who starts with Objective C.
NSString *someText = @"Static Text"; // static allocation
NSString *someMoreText = [[NSString alloc] initWithString:@"More Static Text"]; // manually allocated
NSArray *myArray = [NSArray arrayWithObjects:someText, someMoreText, nil]; // note the nil
[someMoreText release]; // don't forget to release
NSString *retrievedText = [myArray objectAtIndex:1]; // first index = 0
How about numbers? Generally you can only add instances of objects into NSArray. But luckily Apple has created the NSNumber class which provides a container object for any kind of number, i.e. int, float or even BOOL.
With the methods above you are able to add strings and numbers into arrays. There is yet another wrapper class that allows to put even more complex data types and structs into arrays: NSValue. Most usefully are the UIKit additions to NSValue which give you the possibility of packaging CGRect, CGPoint, CGAffineTransform or CGSize structs into objects. And those are just as easy to put into an array.
CGRect aRect = CGRectMake(0, 0, 100.0, 100.0);
CGSize aSize = CGSizeMake(10.0, 20.0);
NSValue *val_rect = [NSValue valueWithCGRect:aRect];
NSValue *val_size = [NSValue valueWithCGSize:aSize];
NSArray *myArray = [NSArray arrayWithObjects:val_rect, val_size, nil];
Soon you will find it odd that NSArray does not have any method to add and remove objects. The reason for this is that most standard objects are non-changable (aka “immutable”) as such. To gain such modification features you have to use the mutable cousin NSMutableArray. This gives you methods like addObject or removeObject.
NSMutableArray *myArray = [[NSMutableArray alloc] init];
[myArray addObject:@"first string"];
[myArray addObject:@"second string"];
[myArray addObject:@"third string"];
NSLog([myArray description]); // a quick way to show contents
NSArrays are the meat and bones of most Objective-C apps. Anybody trying to master this language has no way around them.
Mar 23, 2009
These are the standard methods of getting the app directory and the documents directory. Remember that on the iPhone the app as well as the documents directory are “sandboxed” i.e. each app has it’s own two directories.
Here’s how you can get the path for files in your app bundle/directory:
// a regular file
NSString *plistPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"File.plist"];
// this works also for localized files, gets the best matching localized variant
NSBundle *thisBundle = [NSBundle bundleForClass:[self class]]; // alternate method to get bundle
NSString *path = [thisBundle pathForResource:@"File" ofType:@"plist"];
One common mistake is to pass a file name including extension in the first parameter of the pathForResource method. This won’t work, pass only the name without extension as first parameter and only the extension as second parameter.
And to get something from the documents directory you can do it like this:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *plistPath = [documentsDirectory stringByAppendingPathComponent:@"File.plist"];
You’ll find yourself copy/pasting these lines all the time basically for every file that you are reading or writing.